My use case: I have a 'MainApp' which does the syncing of files. I would like that 'MainApp' handles all server calls regarding syncing and other REST API calls such as document-sharing, etc.
On the other hand, I would have a Finder Sync Extension which would show sync-status icon overlays. It would also have a file-context-menu-item 'Share' which would present a Share dialog where users can choose with whom to share the file.
Questions:
How should FinderSyncExtension and MainApp communicate? Should XCP be utilised and if so, is it ok that communication is two-ways? For example MainApp informing Finder it should refresh because some files have been synced, and Finder informing MainApp that it should perform 'Share' operation.
Who should present 'Share' dialog? When FinderSyncExtension 'Share' menu item is clicked, a Share form should be displayed. Should this be displayed by the finder extension or by the MainApp (assuming FinderExtension informed it that 'Share' item was clicked).
If Finder extension should present the form, then FinderExtension should also retrieve data from the server (such as contacts and groups for sharing) and I'm not sure if Finder Extension should perform any network calls towards the server.
Researching the topic, I found several approaches:
NSWorkspace.sharedWorkspace.notificationCenter
and with NSDistributedNotificationCenter.defaultCenter
but they don't seem to deliver notification in MainApp.I managed to do this via CFMessagePort
API. In order to have sandboxed extension and main app to communicate, AppGroups need to be enabled in Xcode capabilities. Additionally, app-group key with suffix (by your choice) needs to be used as message-port identifier.
Somewhere in the main app, this kind of code would listen on message port:
CFMessagePortRef port = CFMessagePortCreateLocal(nil, CFSTR("group.com.yourapp.mach_or_something"), Callback, nil,
nil);
CFRunLoopSourceRef runLoopSource = CFMessagePortCreateRunLoopSource(nil, port, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
Callback
is a method implemented as:
static CFDataRef Callback(CFMessagePortRef port, SInt32 messageID, CFDataRef data, void* info)
{
NSData* objcData = (__bridge NSData*) data;
NSLog(@"Message received: %@", [NSString.alloc initWithData:objcData encoding:NSASCIIStringEncoding]);
return data;
}
And then, somewhere in the extension (ie when user taps on menu item):
CFDataRef data = CFDataCreate(NULL, (const UInt8*) "somedata", 8);
SInt32 messageID = 0x1111; // Arbitrary
CFTimeInterval timeout = 1;
CFMessagePortRef remotePort = CFMessagePortCreateRemote(nil, CFSTR("group.com.yourapp.mach_or_something"));
SInt32 status = CFMessagePortSendRequest(remotePort, messageID, data, timeout, timeout, NULL, NULL);
if (status == kCFMessagePortSuccess)
{
NSLog(@"SUCCESS STATUS");
}
else
{
NSLog(@"FAIL STATUS");
}
This will send a message to the main application.