Creating an XPC Service in Swift August 25, 2018

Say you want to add an XPC Service to your Swift app. Fine idea, but notice that Xcode spits out Objective-C when you add an XPC Service target. Nobody has time for that. Fortunately converting Xcode’s starter code to Swift is mostly straightforward. Let’s twiddle some knobs and twist a few dials and get you rolling.

After creating an XPC Service target, in our case named “MyService”, we have four files: main.m, MyService.h, MyService.m, and MyServiceProtocol.h. Rename these to main.swift, MyService.swift, MyServiceDelegate.swift, and MyServiceProtocol.swift. Next, add them to the target’s “Compile Sources” build phase.

Now replace the Objective-C code in each file with its Swift translation.

// main.swift import Foundation let delegate = MyServiceDelegate () let listener = NSXPCListener . service () listener . delegate = delegate listener . resume ()

// MyService.swift import Foundation class MyService : NSObject , MyServiceProtocol { func upperCaseString ( _ string : String , withReply reply : @escaping ( String ) -> Void ) { let response = string . uppercased () reply ( response ) } }

// MyServiceDelegate.swift import Foundation class MyServiceDelegate : NSObject , NSXPCListenerDelegate { func listener ( _ listener : NSXPCListener , shouldAcceptNewConnection newConnection : NSXPCConnection ) -> Bool { let exportedObject = MyService () newConnection . exportedInterface = NSXPCInterface ( with : MyServiceProtocol . self ) newConnection . exportedObject = exportedObject newConnection . resume () return true } }

// MyServiceProtocol.swift import Foundation @objc public protocol MyServiceProtocol { func upperCaseString ( _ string : String , withReply reply : @escaping ( String ) -> Void ) }

Before compiling this code we need to tweak build settings:

Install Objective-C Compatibility Header : NO

: NO Objective-C Generated Interface Header Name : “”

: “” Runtime Search Paths : @loader_path/../../../../Frameworks

: @loader_path/../../../../Frameworks Swift Language Version: whatever version of Swift you use

With any luck we can now build. In our main application we delegate a task to our service by creating an NSXPCConnection with the name of the target’s bundle identifier then calling the functions defined in MyService .

import MyService ... let connection = NSXPCConnection ( serviceName : "com.matthewminer.MyService" ) connection . remoteObjectInterface = NSXPCInterface ( with : MyServiceProtocol . self ) connection . resume () let service = connection . remoteObjectProxyWithErrorHandler { error in print ( "Received error:" , error ) } as? MyServiceProtocol service ? . upperCaseString ( "hello XPC" ) { response in print ( "Response from XPC service:" , response ) }

That’s it! For reference you can find the above code snippets in this Gist. Boy howdy.

Thanks to GitHub user adur1990 for Swift 4 compatibility fixes.