Search code examples
objective-cmacosgococoacgo

Golang + cgo - AppDelegate implementation doesn't work


I want to code an app in go that is able to open a custom filetype (.slc) on MacOS. I created a blank xcode project to get all the necessary code and implemented it via cgo into my app. When I double click a file the app opens but complains that it cannot open files in this format:

enter image description here

This is my Info.plist:

enter image description here

Implementation as follows:

/surge/appDelegate_darwin.go

package surge

//#cgo CFLAGS: -x objective-c
//#cgo LDFLAGS: -framework Cocoa
//#include "appDelegate_darwin.h"
import "C"

/surge/appDelegate_darwin.h

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate>


@end

/surge/appDelegate_darwin.m

#include "appDelegate_darwin.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

-(BOOL)application:(NSApplication *)sender openFile:(NSString *)filename
{
   NSLog(@"%@", filename);
   YES;
}
 
-(void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
{
   NSLog(@"%@", filenames);
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}

@end

Additional information: I use the wails framework (https://wails.app) to add a nice vue.js frontend to the app and use the built-in wails build command.

Other implementations in cgo and objective-c (like custom protocol handler) work.


Solution

  • After some sleepless nights I found my own solution through investigating and learning how a regular mac app is structured.

    Additionally to implementing AppDelegate it is also necessary to implement Document and some additional functions to get it running. Here is my

    /surge/appDelegate_darwin.h

    #import <Cocoa/Cocoa.h>
    
    extern void HandleFile(char *);
    
    @interface AppDelegate : NSObject <NSApplicationDelegate>
    
    @end
    
    @interface Document : NSDocument
    
    @end
    

    /surge/appDelegate_darwin.m

    #include "appDelegate_darwin.h"
    
    @interface AppDelegate ()
    
    @end
    
    @implementation AppDelegate
    
    -(BOOL)application:(NSApplication *)sender openFile:(NSString *)filename
    {
       YES;
    }
     
    -(void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
    {
       NSLog(@"%@", filenames);
    }
    
    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
        // Insert code here to initialize your application
    }
    
    
    - (void)applicationWillTerminate:(NSNotification *)aNotification {
        // Insert code here to tear down your application
    }
    
    
    @end
    
    @interface Document ()
    
    @end
    
    @implementation Document
    
    - (instancetype)init {
        self = [super init];
        if (self) {
            // Add your subclass-specific initialization here.
        }
        return self;
    }
    
    + (BOOL)autosavesInPlace {
        return YES;
    }
    
    
    - (NSString *)windowNibName {
        // Override returning the nib file name of the document
        // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
        return @"Document";
    }
    
    
    - (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError {
        // Insert code here to write your document to data of the specified type. If outError != NULL, ensure that you create and set an appropriate error if you return nil.
        // Alternatively, you could remove this method and override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
        [NSException raise:@"UnimplementedMethod" format:@"%@ is unimplemented", NSStringFromSelector(_cmd)];
        return nil;
    }
    
    
    - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
        NSData *dataFromFile = [data retain];
        NSString *myString = [[NSString alloc] initWithData:dataFromFile encoding:NSUTF8StringEncoding];
    
        // This is the place where the magic happens. In my case I just call the HandleFile-function to process the file contents in my main go app
        NSLog(@"Data received: %@", myString);
        HandleFile([myString UTF8String]);
        return YES;
    }
    
    
    @end
    
    

    I hope someone finds this useful!