Hello! I'm trying to implement an editor app (macOS) that allows the user to write code, which will be compiled and executed, showing the result in the editor window.
Imagine it like SwiftUI previews, but the graphic output is created with Metal, not SwiftUI. I found that IOSurface can be used to share that kind of data over XPC, so I would not have to rely on the private NSRemoteView. However, I'm confused if it is, at all, possible for my editor app to connect to an XPC Service, that was NOT bundled with it (but compiled by it at runtime).
I succeeded to launch an XPC service defined as:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.myteam.myproject.service</string>
<key>MachServices</key>
<dict>
<key>com.myteam.myproject.service</key>
<true/>
</dict>
<key>Program</key>
<string>/Path/to/service/run_my_service.sh</string>
</dict>
</plist>
But the call to
let connection = NSXPCConnection(machServiceName: "com.myteam.myproject.service")
let proxy = connection.remoteObjectProxyWithErrorHandler { error in
continuation.resume(throwing: error)
} as? MyServiceProtocol
fails with
"The connection to service named com.myteam.myproject.service was invalidated: Connection init failed at lookup with error 3 - No such process."
I have added
<key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
<array>
<string>com.myteam.myproject.service</string>
</array>
to my entitlements.
Since the tutorials I followed are quite old, I'm wondering if support for something like this was dropped at some point.
Thanks for any advice!
You’ve bumped into a common point of confusion. An XPC service is a bundled program, with the .xpc extension. This is, by definitive embedded in your app [1]. However, that’s not the only way to use XPC. For example, a launchd daemon or agent can vend a named XPC endpoint via its MachSesrvices dictionary. This is not an XPC service per se, but you can talk to it much like you would talk to an XPC service. The biggest difference is the way that you connect. For an XPC service you use NSXPCConnection.init(listenerEndpoint:) and for a named XPC endpoint vended by a launchd daemon you use init(machServiceName:options:).
I talk a lot more about this in XPC and App-to-App Communication. This and other helpful things are linked to from XPC Resources.
Having said that, launchd daemons and agents are a clunky way to approach this. While there are now good ways to wrangle them [2], it’s still a hassle because they persist. XPC services are great because their lifecycle is tied to the lifecycle of your app.
Which brings me back to this:
NOT bundled with it (but compiled by it at runtime).
These two things aren’t really at odds. Specifically, it’s feasible to compile your code to something that’s loadable (a Mach-O dynamic library or bundle) and then have your XPC service load that. There are a few pitfalls to watch out for, but the overall approach should work. And that allows you to use your XPC service for its XPC, isolation, and lifecycle benefits, while still allowing you to load and run compiled code.
Oh, and the most obvious pitfall here is library validation. By default the Hardened Runtime will prevent your XPC service from loaded ad hoc signed code, so you’ll have to apply the entitlement to disable that.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] You can also embed these within frameworks and the system has other options, but those aren’t relevant to your setup.
[2] Using SMAppService; see Service Management Resources for links to docs and more.