How should Finder Sync Extension and Main App communicate?
Asked Answered
B

2

7

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:

  1. 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.

  2. 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:

  1. FinderSyncExtension and MainApp don't communicate directly. Instead, FinderExtension reads data from database to properly show badges. In this scenario it is unclear how FinderExtension should update when sync is finished or how should it inform MainApp to perform some action.
  2. XPC communication. I guess FinderExtension could initiate calls to MainApp but is opposite direction expected?
  3. Is there some kind of NotificationCenter between macOS processes? I tried with NSWorkspace.sharedWorkspace.notificationCenter and with NSDistributedNotificationCenter.defaultCenter but they don't seem to deliver notification in MainApp.
  4. mach_ports as in Seafile project?
Barrick answered 7/12, 2016 at 11:41 Comment(0)
B
7

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.

Main app

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;
}

Finder Sync Extension

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.

Barrick answered 15/12, 2016 at 13:55 Comment(5)
Hm, there seems to be a crash with this approach in macOS Sierra. It works ok on El Capitan. I filed a bug to Apple regarding.Barrick
I don't think it's an issue with Sierra. I modified your callback function into something like this and then it worked in Sierra without any problemEyelash
That works! I'll modify answer to reflect correct solution. Thank you Alex.Barrick
@AlexLing What also works is if NSData* is casted to '__bridge' instead of '__bridge_transfer'. '__bridge_transfer' causes ARC to release the "data" parameter when the callback returns. Instead, it should be (__bridge NSData*), without the "_transfer". That change resolves the crash.Barrick
@mixtly87, i get an error for "permission denied" in sandbox app, do you know how to resolve it? *** CFMessagePort: bootstrap_register(): failed 1100 (0x44c) 'Permission denied', port = 0x7c0f, name =Mcclinton
C
1

Apple documentation about sandboxed Mach Inter-Process Communication:

https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24

IPC and POSIX Semaphores and Shared Memory

Normally, sandboxed apps cannot use Mach IPC, POSIX semaphores and shared memory, or UNIX domain sockets (usefully). However, by specifying an entitlement that requests membership in an application group, an app can use these technologies to communicate with other members of that application group.

Any semaphore or Mach port that you wish to access within a sandboxed app must be named according to a special convention:

  • POSIX semaphores and shared memory names must begin with the application group identifier, followed by a slash (/), followed by a name of your choosing. Mach port names must begin with the application group identifier, followed by a period (.), followed by a name of your choosing.

  • For example, if your application group’s name is Z123456789.com.example.app-group, you might create two semaphores named Z123456789.myappgroup/rdyllwflg and Z123456789.myappgroup/bluwhtflg. You might create a Mach port named Z123456789.com.example.app-group.Port_of_Kobe.

Note: The maximum length of a POSIX semaphore name is only 31 bytes, so if you need to use POSIX semaphores, you should keep your app group names short.

Celt answered 19/10, 2018 at 4:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.