Portrait

I2H3

Notes from work as an app developer for iPhones, iPads and Macs.

RSS Icon GitHub Mastodon

Asynchronous Throwing XPC Protocols

I always thought that @objc protocols put constraints on protocol definitions that make the use of async and throws impossible. Don’t ask me why. It actually is not necessary to use completion handlers. async throws methods can be used instead.

File providers can provide custom services via XPC (see NSFileProviderSource). Usually, this means the communication of an app and its file provider extension. In example, to provide credentials to the file provider extension the user entered in the app’s user interface.

Without going into full detail, this requires the declaration of a protocol used by both the app and the file provider extension. It is required to be Objective-C compatible. For whatever reason I was still thinking that only completion handlers can be used. To make usage from Swift more convenient, I additionally implemented something I called a service proxy. It wraps the synchronous method with a completion handler into an asynchronous throwing method with the help of continuations.

@objc
public protocol FileProviderService {
    func provision(with user: String, on host: URL, completionHandler: @escaping (Error?)  Void)
}

class FileProviderServiceProxy {
    func provision(with user: String, on host: URL) async throws {
        return withCheckedThrowingContinuation { continuation in
            service.provision(with: user, on: host) { error in
                if error == nil {
                    continuation.resume()
                } else {
                    continuation.throw(error)
                }
            }
        }
    }
}

Recently I stumbled over the updated file provider example project by Apple. The protocol definition catched my eye and I realized that there is a more convenient way to define the file provider XPC service protocol. I realized I can achieve the same by just writing this:

@objc
public protocol FileProviderService {
    func provision(with user: String, on host: URL) async throws
}
a memoji

Apple has an article about calling Objective-C APIs asynchronously which explains the conventions in detail. Funnily, I was aware of that as long as I had to deal with the file provider XPC protocol definition. Anyway, that was quite relieving discovery.