I am trying to write a simple pair of “client applications” and “XPC services”. I managed to start the xpc service from the client (i.e., I see the service running in the Activity Monitoring process list), but when I try to send any request that has a response block, I get the error message: "Could not contact assistant application. "
Worst of all, the error does not give me any information about what went wrong. And I also cannot debug the service properly. As far as I understand, the correct way to do this is to connect a debugger for processing (Debug-> Attach to process, see here ). I have both client and service projects in one workspace.
When I start the client from xcode and try to connect the debugger to the running service, it ends with the error "Could not connect to pid: X".
If I archive the client application, run it from the application file, and then try to connect the debugger for maintenance, the result will be the same.
The only way I can write something from a service that I could imagine is to write a log class that will write data to a file. I have not tried this approach yet, however it looks crazy for me.
So my question is:
a) How do I find out what went wrong when I received an uninformative response such as: "Failed to contact the assistive application"?
b) And also, what is the proper way to debug an xpc service in the first place? The link is above 5 years, but I see that some people said that "attach to the debugger" does not work.
The code itself is pretty simple:
XPC service, listener implementation:
And the main file for maintenance:
For a client application, the user interface contains a bunch of buttons:
- (IBAction)buttonSendMessageTap:(id)sender { if ([daemonController running]) { [self executeRemoteProcessWithName:@"NoResponse"]; } else { [[self.labelMessageResult cell] setTitle: @"Error"]; } } - (IBAction)buttonSendMessage2:(id)sender { if ([daemonController running]) { [self executeRemoteProcessWithName:@"WithResponse"]; } else { [[self.labelMessageResult cell] setTitle: @"Error"]; } } - (void) executeRemoteProcessWithName: (NSString*) processName { // Create connection NSXPCInterface * myCookieInterface = [NSXPCInterface interfaceWithProtocol: @protocol(Processor)]; NSXPCConnection * connection = [[NSXPCConnection alloc] initWithServiceName: @"bunldeID"]; // there a correct bundle id there, really [connection setRemoteObjectInterface: myCookieInterface]; connection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(Progress)]; connection.exportedObject = self; [connection resume]; // NOTE that this error handling code is not called, when debugging client, ie connection seems to be established id<Processor> theProcessor = [connection remoteObjectProxyWithErrorHandler:^(NSError *err) { NSAlert *alert = [[NSAlert alloc] init]; [alert addButtonWithTitle: @"OK"]; [alert setMessageText: err.localizedDescription]; [alert setAlertStyle: NSAlertStyleWarning]; [alert performSelectorOnMainThread: @selector(runModal) withObject: nil waitUntilDone: YES]; }]; if ([processName containsString:@"NoResponse"]) { [theProcessor sendMessageWithNoResponse:@"message"]; } else if ([processName containsString:@"WithResponse"]) { [theProcessor sendMessageWithResponse:@"message" reply:^(NSString* replyString) { [[self.labelMessageResult cell] setTitle: replyString]; }]; } }