Finder Sync Extension 和 Main App 应该如何通信?

How should Finder Sync Extension and Main App communicate?

我的用例:我有一个 'MainApp' 可以同步文件。 我希望 'MainApp' 处理有关同步和其他 REST API 调用(例如文档共享等)的所有服务器调用。

另一方面,我会有一个 Finder 同步扩展,它会显示同步状态图标覆盖。 它还将有一个文件上下文菜单项 'Share',它将显示一个共享对话框,用户可以在其中选择与谁共享文件。

问题:

  1. FinderSyncExtension和MainApp应该如何通信?是否应该使用 XCP?如果是,双向通信是否可以?例如 MainApp 通知 Finder 它应该刷新,因为一些文件已经同步,Finder 通知 MainApp 它应该执行 'Share' 操作。

  2. 谁应该提出 'Share' 对话?单击 FinderSyncExtension 'Share' 菜单项时,应显示共享表单。这应该由 finder 扩展程序或 MainApp 显示(假设 FinderExtension 通知它单击了 'Share' 项)。

如果 Finder 扩展应该显示表单,那么 FinderExtension 还应该从服务器检索数据(例如用于共享的联系人和组),我不确定 Finder 扩展是否应该对服务器执行任何网络调用。

研究主题,我发现了几种方法:

  1. FinderSyncExtension 和 MainApp 不直接通信。相反,FinderExtension 从数据库中读取数据以正确显示徽章。在这种情况下,不清楚同步完成后 FinderExtension 应该如何更新,或者它应该如何通知 MainApp 执行某些操作。
  2. XPC 通信。我想 FinderExtension 可以发起对 MainApp 的调用,但方向与预期相反?
  3. macOS 进程之间是否有某种 NotificationCenter?我尝试使用 NSWorkspace.sharedWorkspace.notificationCenterNSDistributedNotificationCenter.defaultCenter,但它们似乎没有在 MainApp 中发送通知。
  4. mach_ports 和 Seafile 项目一样?

我设法通过 CFMessagePort API 做到了这一点。为了让沙盒扩展和主应用程序进行通信,需要在 Xcode 功能中启用 AppGroups。此外,需要使用带后缀(由您选择)的应用组密钥作为消息端口标识符。

主应用程序

在主应用程序的某个地方,这种代码会监听消息端口:

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 是一种实现为:

的方法
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 同步扩展

然后,在扩展中的某处(即当用户点击菜单项时):

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

这将向主应用程序发送一条消息。

关于沙盒 Mach 进程间通信的 Apple 文档:

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.