Communicate with another app using XPC

cocoa, helper, macos, xpc

Solution

Alright for anyone that has been struggling with this, I was finally able to 100% get communication working between two application processes, using `NSXPCConnection`

The key to note is that you can only create an `NSXPCConnection` to three things.

- An XPCService. You can connect to an XPCService strictly through a name

- A Mach Service. You can also connect to a Mach Service strictly through a name

- An `NSXPCEndpoint`. This is what we're looking for, to communicate between two application processes.

The problem being that we can't directly transfer an `NSXPCEndpoint` from one application to another.

It involved creating a machservice Launch Agent (See this example for how to do that) that held an `NSXPCEndpoint` property. One application can connect to the machservice, and set that property to it's own `[NSXPCListener anonymousListener].endpoint`

Then the other application can connect to the machservice, and ask for that endpoint.

Then using that endpoint, an `NSXPCConnection` can be created, which successfully established a bridge between the two applications. I have tested sending objects back and forth, and it all works as expected.

Note that if your application is sandboxed, you will have to create an `XPCService`, as a middle man between your Application and the Machservice

I'm pretty pumped that I got this working-- I'm fairly active in SO, so if anybody is interested in source code, just add a comment and I can go through the effort to post more details

Some hurdles I came across:

You have to launch your machservice, these are the lines:

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

Also, every time you rebuild your daemon, you have to unload the previous launch agent, with these bash commands:

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

(With your corresponding identifiers, of course)

Problem

I have a windowed app, and to add some functionality I need another app which launches at login and sync data to server if available. I have tried with NSDistributionNotification but its practically useless in a sandboxed app. I looked up XPC and hoped it will work but I just dont know how to get it to work with the helper. So far I have done this using XPC. Main App ``` 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 Service ``` 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); } ``` This works fine, now I need to pass this result to Helper app. How Can I do this ? If XPC is not the answer here to communicate, then which one should I be using ? Any pointers please ?

Original source