In this article on Xcode Source Editor extensions, it mentions that XPC is a way to circumvent the app sandbox:
The extension must be sandboxed just to be loaded by Xcode, whereas calls to SourceKit needs to be un-sandboxed, which of course won’t fly in the App Store. We could distribute independently and use an un-sandboxed XPC service embedded in the extension.
However, I'm not sure how to tie everything together to use an XPC service.
How do I tie my Xcode Source Editor extension to an XPC service?
I was able to figure this out thanks to the LinuxSupportForXcode extension.
I'm going to make the assumption that you followed the tutorial on creating an Xcode Extension Editor, and made the main project a macOS App. You should have a target structure similar to:
To use XPC with a Source Editor Extension:
File > New > Target... > XPC Service.
For example purposes, we'll assume it's called
MyAppXPCService
and its bundle identifier iscom.example.MyAppXPCService
.Move the XPC service dependency from the App to the Extension:
If you don't do this step, you may run into issues where your XPCService isn't being executed by the extension. E.g. you invoke a command that should launch the XPCService, but in the Xcode Debug Navigator, your XPCService never comes up.
In the XPC Service, convert it to Swift, mainly following the instructions here:
Note: If you'd prefer to not convert to Swift, and use a mixed target instead, simply create a Swift file and when prompted, choose to create the bridging header, then include
#import "MyAppXPCServiceProtocol.h"
in the bridging header.Create
main.swift
,MyService.swift
,MyServiceDelegate.swift
,MyServiceProtocol.swift
normally.Set the following build settings:
NO
Choose your desired Swift Language Version in Build Settings.
In Build Settings, add (don't replace):
@loader_path/../../../../Frameworks
to Runtime Search Paths.If you accidentally replace, and use an embedded framework, XPC will crash on launch.
In your extension target:
import MyAppXPCService
so that it can see the protocol.Create the connection, using your XPC target's bundle identifier for
serviceName
:Call your XPC service:
The nice thing about creating the connection is it'll automatically start your XPC service, without needing the UI app to be running.