I was successful in creating XPC service and communicating with XPC service by sending messages from main application. But what I want to know is, whether its possible to initiate a communication from XPC service to the main application. The Apple documentation says XPC is bidirectional. It would be much appreciated if someone can point me in right direction with an example.
Please note,
I succeeded in first two, but couldn't find any resource on the third one.
Thanks. :)
Figured everything out. The following should be a decent example:
ProcessorListener.h (included in both client and server):
@protocol Processor
- (void) doProcessing: (void (^)(NSString *response))reply;
@end
@protocol Progress
- (void) updateProgress: (double) currentProgress;
- (void) finished;
@end
@interface ProcessorListener : NSObject<NSXPCListenerDelegate, Processor>
@property (weak) NSXPCConnection *xpcConnection;
@end
ProcessorListener.m: (Included in just the server)
#import "ProcessorListener.h"
@implementation ProcessorListener
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
{
[newConnection setExportedInterface: [NSXPCInterface interfaceWithProtocol:@protocol(Processor)]];
[newConnection setExportedObject: self];
self.xpcConnection = newConnection;
newConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol: @protocol(Progress)];
// connections start suspended by default, so resume and start receiving them
[newConnection resume];
return YES;
}
- (void) doProcessing: (void (^)(NSString *g))reply
{
dispatch_async(dispatch_get_global_queue(0,0), ^{
for(int index = 0; index < 60; ++index)
{
[NSThread sleepWithTimeInterval: 1];
[[self.xpcConnection remoteObjectProxy] updateProgress: (double)index / (double)60 * 100];
}
[[self.xpcConnection remoteObjectProxy] finished];
}
// nil is a valid return value.
reply(@"This is a reply!");
}
@end
MainApplication.m (your main app):
#import "ProcessorListener.h"
- (void) executeRemoteProcess
{
// Create our connection
NSXPCInterface * myCookieInterface = [NSXPCInterface interfaceWithProtocol: @protocol(Processor)];
NSXPCConnection * connection = [[NSXPCConnection alloc] initWithServiceName: kServiceName];
[connection setRemoteObjectInterface: myCookieInterface];
connection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(Progress)];
connection.exportedObject = self;
[connection resume];
id<Processor> theProcessor = [connection remoteObjectProxyWithErrorHandler:^(NSError *err)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle: @"OK"];
[alert setMessageText: err.localizedDescription];
[alert setAlertStyle: NSWarningAlertStyle];
[alert performSelectorOnMainThread: @selector(runModal) withObject: nil waitUntilDone: YES];
}];
[theProcessor doProcessing: ^(NSString * response)
{
NSLog(@"Received response: %@", response);
}];
}
#pragma mark -
#pragma mark Progress
- (void) updateProgress: (double) currentProgress
{
NSLog(@"In progress: %f", currentProgress);
}
- (void) finished
{
NSLog(@"Has finished!");
}
@end
Note that this code is is a condensed version based on my working code. It may not compile 100% but shows the key concepts used. In the example, the doProcessing
runs async to show that the callbacks defined in the Progress
protocol still get executed even after the initial method has return and the Received response: This is a reply!
has been logged.