Create plugin system using XPC

I am using runtime loaded dynamic libraries (in an NSBundle) as a plugin system in my app.

I would like to switch over to using XPC Services though, for various reasons.


Is it possible to load plugins which are really xpc services from a user-specified folder?


I have not yet figured out how I would go about dicovering that a plugin is present or put in the PlugIns folder.

That folder would most likely be either under ~/Documents/MyApp/PlugIns or something else user-accessible.

(I may also ship some plugins with the app, which are inside the app's bundle, but discovering/loading those seems trivial)


Thank you for your help.

Thomas

Accepted Reply

Is it possible to load plugins which are really xpc services from a user-specified folder?

No.

One alternative that’s relatively straightforward is to have a single XPC Service that loads all your plug-ins.

Unfortunately there’s no public API for creating an XPC Service that you can instantiate multiple times (although that’d make a fine enhancement request). It may be possible to simulate that by using a parent XPC Service with a bunch of child processes, one for each plug-in, but I’ve never sat down to write the code to confirm that this works.

Finally, Apple has internal infrastructure — known as PlugInKit, hence the

pluginkit
command-line tool — that was specifically designed for this sort of thing. Indeed, it’s what the various app extensions are built around. Making a public API from this would be another fine enhancement request.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

Is it possible to load plugins which are really xpc services from a user-specified folder?

No.

One alternative that’s relatively straightforward is to have a single XPC Service that loads all your plug-ins.

Unfortunately there’s no public API for creating an XPC Service that you can instantiate multiple times (although that’d make a fine enhancement request). It may be possible to simulate that by using a parent XPC Service with a bunch of child processes, one for each plug-in, but I’ve never sat down to write the code to confirm that this works.

Finally, Apple has internal infrastructure — known as PlugInKit, hence the

pluginkit
command-line tool — that was specifically designed for this sort of thing. Indeed, it’s what the various app extensions are built around. Making a public API from this would be another fine enhancement request.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Actually Quinn, would it be feasible to make the plugin its own application?


Setup:


- Main app has a launch agent "plugin service" that is always running (or only when the app runs?)

- Plugin is an app that installs its own launch agent so it is always providing a service even when not open

- Main app somhow manages to connect to all services that are "connected" to the plugin service


Something like Anonymous Listeners and Endpoints


Thanks for your support!

Is the App Sandbox involved at any point here?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Yes, my app is sandboxed and notarized (but distributed via Developer ID signing)

OK. First things first, I need to draw a distinction between a Mac App Store app and a Developer ID app that has opted in to the App Sandbox. The key difference here relates to temporary exception entitlements. In a Mac App Store app, these have to be approved by App Review and, in my experience, they are very reluctant to do this. In contrast, Developer ID apps can use temporary exception entitlements at will.

This is relevant here because a sandboxed app cannot reach out and connect to an arbitrary XPC service. To do this, you’ll need to global Mach service temporary exception entitlement (

com.apple.security.temporary-exception.mach-lookup.global-name
). So, the arrangement you propose is only feasible in a Developer ID app.

Beyond that, I can’t see any specific problem with your proposal. You wrote:

or only when the app runs?

You should make your agent launches on demand, which should then allow the system to terminate it when it’s not in use.

Main app somhow manages to connect to all services that are "connected" to the plugin service

You can definitely do that using an XPC endpoint. The app would connect to the agent telling it what plug-in it wants to use. The agent then connects to the plug-in’s service in order to ‘check in’. The plug-in’s reply contains an XPC endpoint. The agent can include that in its reply to the app. At this point the app can connect to the plug-in directly via that endpoint.

The main problem here is lifecycle management. For example, if the plug-in crashes, it won’t be relaunched by the app’s connection. The app would have go back through the agent to get it to start.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"