问题
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 ?
回答1:
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)
回答2:
I think I figured out how to do this. All you have to do is create a command line helper tool in Xcode, install it as a Launchd job (Either a daemon or an Agent depending on the privilege requirement). You can use the defined protocol to communicate with the helper tool. Refer to the below sample code from Apple to understand how it is done.
Sample Code from 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
Read the below link to understand what you really want, a Daemon or an Agent: https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/DesigningDaemons.html#//apple_ref/doc/uid/10000172i-SW4-BBCBHBFB
回答3:
If you are searching on how to accomplish this in Swift. I wrote a tutorial on how to do this:
https://rderik.com/blog/creating-a-launch-agent-that-provides-an-xpc-service-on-macos/
You have to first create a Launch Agent (or a daemon, if you need more privileges) that exposes an XPC service. The XPC service will be registered as a mach service that your agent provides. So your Agent will have to create a listener like the following:
let listener = NSXPCListener(machServiceName: "com.rderik.exampleXPC" )
And to use that service from other client, you'll need to create aNSXPCConnection
to that mach service. Like this:
let connection = NSXPCConnection(machServiceName: "com.rderik.exampleXPC")
Behind the scenes, a simplification of what happens is that your Agent will register your mach service to launchd
. When your "client" wants to connect to to a mach service launchd
will already have it register, so it will build the connection between the two.
I hope that helps.
来源:https://stackoverflow.com/questions/24040765/communicate-with-another-app-using-xpc