8

Creating an XPC Service in Swift

 3 years ago
source link: https://matthewminer.com/2018/08/25/creating-an-xpc-service-in-swift
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Matthew Miner

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.1 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, here 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.

Xcode Build Phases

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
  • Objective-C Generated Interface Header Name: “”
  • Runtime Search Paths: @loader_path/../../../../Frameworks
  • Swift Language Version: whatever version of Swift you use
Xcode Build Settings

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.


  1. As of Xcode 9, at least. I hope Apple changes this in future versions and this bone dry article can be tossed into the World Wide Web’s trash bin. 


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK