【问题标题】:Communicate with another app using XPC使用 XPC 与另一个应用程序通信
【发布时间】:2025-12-21 23:05:17
【问题描述】:

我有一个窗口应用程序,要添加一些功能,我需要另一个应用程序,它在登录时启动并将数据同步到服务器(如果可用)。

我尝试过使用 NSDistributionNotification,但它在沙盒应用程序中几乎没有用。我查看了 XPC 并希望它能工作,但我只是不知道如何让它与助手一起工作。到目前为止,我已经使用 XPC 完成了这项工作。

主应用

    NSXPCInterface *remoteInterface = [NSXPCInterface interfaceWithProtocol:@protocol(AddProtocol)];
    NSXPCConnection *xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.example.SampleService"];

    xpcConnection.remoteObjectInterface = remoteInterface;

    xpcConnection.interruptionHandler = ^{
        NSLog(@"Connection Terminated");
    };

    xpcConnection.invalidationHandler = ^{
        NSLog(@"Connection Invalidated");
    };

    [xpcConnection resume];

    NSInteger num1 = [_number1Input.stringValue integerValue];
    NSInteger num2 = [_number2Input.stringValue integerValue];

    [xpcConnection.remoteObjectProxy add:num1 to:num2 reply:^(NSInteger result) {
        NSLog(@"Result of %d + %d = %d", (int) num1, (int) num2, (int) result);
    }];

XPC 服务

In main () ...
SampleListener *delegate = [[SampleListener alloc] init];
NSXPCListener *listener = [NSXPCListener serviceListener];
listener.delegate = delegate;
[listener resume];

// In delegate
-(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
    NSXPCInterface *interface = [NSXPCInterface interfaceWithProtocol:@protocol(AddProtocol)];
    newConnection.exportedInterface = interface;
    newConnection.exportedObject = [[SampleObject alloc] init];
    [newConnection resume];
    return YES;
}

// In Exported Object class

-(void)add:(NSInteger)num1 to:(NSInteger)num2 reply:(void (^)(NSInteger))respondBack {
    resultOfAddition = num1 + num2;
    respondBack(resultOfAddition);
}

这很好用,现在我需要将此结果传递给 Helper 应用程序。我怎样才能做到这一点 ?如果 XPC 不是这里交流的答案,那么我应该使用哪一个?请大家指点一下?

【问题讨论】:

    标签: macos cocoa helper xpc


    【解决方案1】:

    我想我想出了如何做到这一点。您所要做的就是在 Xcode 中创建一个命令行帮助工具,将其安装为 Launchd 作业(根据权限要求,可以是守护进程或代理)。您可以使用定义的协议与帮助工具进行通信。请参考以下来自 Apple 的示例代码以了解它是如何完成的。

    来自 Apple 的示例代码: https://developer.apple.com/library/mac/samplecode/EvenBetterAuthorizationSample/Listings/Read_Me_About_EvenBetterAuthorizationSample_txt.html#//apple_ref/doc/uid/DTS40013768-Read_Me_About_EvenBetterAuthorizationSample_txt-DontLinkElementID_17

    阅读下面的链接以了解您真正想要的是守护程序还是代理: https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/DesigningDaemons.html#//apple_ref/doc/uid/10000172i-SW4-BBCBHBFB

    【讨论】:

      【解决方案2】:

      好吧,对于一直在努力解决这个问题的任何人,我终于能够 100% 使用 NSXPCConnection 在两个应用程序进程之间进行通信

      要注意的关键是你只能为三个东西创建一个NSXPCConnection

      1. XPC 服务。您可以通过严格连接到 XPCService 一个名字
      2. 马赫服务。您还可以连接到 Mach 服务 严格通过名字
      3. NSXPCEndpoint。这就是我们 寻找,在两个应用程序进程之间进行通信。

      问题是我们不能直接将NSXPCEndpoint 从一个应用程序转移到另一个应用程序。

      它涉及创建一个拥有 NSXPCEndpoint 属性的 machservice 启动代理 (See this example for how to do that)。一个应用程序可以连接到 machservice,并将该属性设置为它自己的[NSXPCListener anonymousListener].endpoint

      然后其他应用程序可以连接到 machservice,并请求该端点。

      然后使用该端点,可以创建一个NSXPCConnection,它成功地在两个应用程序之间建立了一座桥梁。我已经测试了来回发送对象,一切正常。

      请注意,如果您的应用程序是沙盒化的,您必须创建一个 XPCService,作为您的应用程序和 Machservice 之间的中间人

      我很高兴我得到了这个工作——我在 SO 中相当活跃,所以如果有人对源代码感兴趣,只需添加评论,我可以努力发布更多细节

      我遇到的一些障碍:

      你必须启动你的 machservice,这些是行:

         OSStatus                    err;
         AuthorizationExternalForm   extForm;
      
         err = AuthorizationCreate(NULL, NULL, 0, &self->_authRef);
         if (err == errAuthorizationSuccess) {
            NSLog(@"SUCCESS AUTHORIZING DAEMON");
         }
         assert(err == errAuthorizationSuccess);
      
         Boolean             success;
         CFErrorRef          error;
      
         success = SMJobBless(
                              kSMDomainSystemLaunchd,
                              CFSTR("DAEMON IDENTIFIER HERE"),
                              self->_authRef,
                              &error
                              );
      

      此外,每次重建守护程序时,都必须使用以下 bash 命令卸载以前的启动代理:

      sudo launchctl unload /Library/LaunchDaemons/com.example.apple-samplecode.EBAS.HelperTool.plist
      sudo rm /Library/LaunchDaemons/com.example.apple-samplecode.EBAS.HelperTool.plist
      sudo rm /Library/PrivilegedHelperTools/com.example.apple-samplecode.EBAS.HelperTool
      

      (当然是你对应的标识符)

      【讨论】:

      • 如果有,请发布更多代码。需要在我自己的应用和使用我构建的 SDK 的其他应用之间进行通信。
      • 您有什么问题?已经有一段时间了,很多地方都有代码,如果你能帮助进一步解释这个问题,我可以尝试为你发布更多相关的代码^
      • 这在生产应用中是否可行?
      • 几年前我离开了公司,所以我不确定他们是否最终将其转移到生产中——我知道应用商店中的沙盒应用存在问题,其他人会必须回答这个问题:\或者你必须尝试一下
      • 是的,任何人都想知道如何在沙盒应用中做到这一点,Apple 有这个演示:developer.apple.com/library/archive/samplecode/…
      【解决方案3】:

      如果您正在搜索如何在 Swift 中完成此任务。我写了一个关于如何做到这一点的教程:

      https://rderik.com/blog/creating-a-launch-agent-that-provides-an-xpc-service-on-macos/

      您必须首先创建一个公开 XPC 服务的启动代理(或守护程序,如果您需要更多权限)。 XPC 服务将注册为您的代理提供的 mach 服务。因此,您的代理必须创建一个如下所示的侦听器:

      let listener = NSXPCListener(machServiceName: "com.rderik.exampleXPC" )
      

      要从其他客户端使用该服务,您需要为该 mach 服务创建一个NSXPCConnection。像这样:

      let connection = NSXPCConnection(machServiceName: "com.rderik.exampleXPC")
      

      在幕后,简单来说,您的代理会将您的 mach 服务注册到 launchd。当你的“客户端”想要连接到一个 mach 服务时,launchd 已经注册了它,所以它会在两者之间建立连接。

      希望对你有帮助。

      【讨论】:

      • 我是 swift 新手,你能用 Objectvie c 制作吗