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 ?