Assume this over-simplified @protocol I'm using for my XPC-service:
@protocol MyMinimalProtocol <NSObject>
- (void)getStatusWithReply:(void (^ _Nullable)(NSDictionary * _Nonnull))reply;
@end
The Client side would then
NSXPCConnection *connection = [[NSXPCConnection alloc] initWithMachServiceName:myServiceLabel options:NSXPCConnectionPrivileged];
connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyMinimalProtocol)];
connection.interruptionHandler = ^{ NSLog(@"XPC: connection - interrupted"); };
connection.invalidationHandler = ^{ NSLog(@"XPC: connection - invalidated"); };
[connection resume];
[connection.remoteObjectProxy getStatusWithReply:^(NSDictionary * response) {
NSLog(@"XPC service status received - %@", response);
}];
So far - so good. My XPC service receives the asynchronous call, schedules it's "status gathering operation" on internal background queue, and returns. Later - when information is available, my XPC service executes the reply-block then, on the remote calling side - I see the log line with the status, as expected.
BUT!!!
If I add another different code-block argument to the method e.g.
@protocol MyMinimalProtocol <NSObject>
- (void)getStatusWithReply:(void (^ _Nullable)(NSDictionary * _Nonnull))reply andFailureBlock:(void (^ _Nullable)(NSError * _Nonnull))failureBlock;
@end
Then all hell breaks loose. Both XPC service and the client crash with hilarious crash reasons I can't decipher.
Here's "Client side" caller crash (excerpt - forgive the methods are NOT the simplified ones above)
while on the "XPC Service" side, crashes like these:
I wonder if there's something inherently wrong with having two code-block arguments for an XPC remote method?
Another issue. The client XPC calls are asynchronous. They return immediately. The XPC service implementing the remote-call also returns immediately - and it executes the "reply block" far (a minute!) later, on another queue.
However, if the XPC service attempts to execute the code-block MORE THAN ONCE, then the client-side code-block is only called ONCE. rest of the executions look benign in the XPC-service side - but never happen on the calling (client) side.
Any idea why? can this be overcome?
Any thoughts/ideas/references to documentation will be greatly appreciated. I couldn't find any good source on this.
Thanks.
I wonder if there's something inherently wrong with having two code-block arguments for an XPC remote method?
Correct. NSXPCConnection
requires that all protocol methods have at most one block parameter, that it be the last parameter, and that it has reply semantics. Adding extra block parameters isn’t supported.
However, if the XPC service attempts to execute the code-block MORE THAN ONCE
That’s also not supported.
You need to design your protocols with these restrictions in mind. For example:
-
You might have a single method that completes immediately with a request token.
-
And then have other methods that get the latest status for that token.
You can use an anonymous connection (via NSXPCListenerEndpoint
) for this if you want.
You can also use NSProgress
.
It’s also possible to set up bi-directional messaging, but that’s challenging because you want the server to be resilient in the face of a wedged client.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"