AsyncXPCConnection

1.1.0

Concurrency support for NSXPCConnection
ChimeHQ/AsyncXPCConnection

What's New

v1.1.0

2024-01-20T12:04:42Z

What's Changed

  • Update README.md by @Wouter01 in #1
  • Add some missing Sendable constraints to RemoteXPCService
  • Add NSXPCConnection variants that support isolated parameters

New Contributors

Full Changelog: 1.0.0...1.1.0

Platforms Documentation Discord

AsyncXPCConnection

Swift concurrency support for NSXPCConnection

Features:

  • structured concurrency extensions for NSXPCConnection
  • convenience callbacks for better interfacing with Objective-C-based XPC protocols
  • RemoteXPCService for easier type-safety
  • QueuedRemoteXPCService for message ordering control

You might be tempted to just make your XPC interface functions async. While the compiler does support this, it is very unsafe. This approach does not handle connection failures and will results in hangs.

Usage

Given an XPC service like this:

@objc
protocol XPCService {
    func method()
    func errorMethod(reply: (Error?) -> Void)
    func valueAndErrorMethod(reply: (String?, Error?) -> Void)
    func dataAndErrorMethod(reply: (Data?, Error?) -> Void)
}

You can use NSXCPConnection directly:

let conn = NSXPCConnection()
conn.remoteObjectInterface = NSXPCInterface(with: XPCService.self)

// access to the underlying continuation
try await conn.withContinuation { (service: XPCService, continuation: CheckedContinuation<Void, Error>) in
    service.errorMethod() {
        if let error = $0 {
            continuation.resume(throwing: error)
        } else {
            continuation.resume()
        }
    }
}

try await conn.withService { (service: XPCService) in
    service.method()
}

try await conn.withErrorCompletion { (service: XPCService, handler) in
	service.errorMethod(reply: handler)
}

let value = try await conn.withValueErrorCompletion { (service: XPCService, handler) in
    service.valueAndErrorMethod(reply: handler)
}

let decodedValue = try await conn.withDecodingCompletion { (service: XPCService, handler) in
    service.dataAndErrorMethod(reply: handler)
}

You can also make use of the RemoteXPCService type, which will remove the need for explicit typing of the service.

let conn = NSXPCConnection()
let remote = RemoteXPCService<XPCService>(connection: conn, remoteInterface: XPCService.self)

let decodedValue = try await remote.withDecodingCompletion { service, handler in
    service.dataAndErrorMethod(reply: handler)
}

Ordering

The QueuedRemoteXPCService type is very similar to RemoteXPCService, but offers a queuing interface to control the ordering of message delivery. This is done via the AsyncQueuing protocol, for flexible, dependency-free support. If you need a compatible queue implementation, check out Queue. And, if you know of another, let me know so I can link to it.

import AsyncXPCConnection
import Queue

extension AsyncQueue: AsyncQueuing {}

let queue = AsyncQueue()
let connection = NSXPCConnection()
let queuedService = QueuedRemoteXPCService<XPCService, AsyncQueue>(queue: queue, provider: { connection })

queuedService.addOperation { service in
    service.method()
}

let value = try await queuedService.addDecodingOperation { service, handler in
    service.dataAndErrorMethod(reply: handler)
}

Alternatives

Contributing and Collaboration

I would love to hear from you! Issues or pull requests work great. A Discord server is also available for live help, but I have a strong bias towards answering in the form of documentation.

I prefer collaboration, and would love to find ways to work together if you have a similar project.

I prefer indentation with tabs for improved accessibility. But, I'd rather you use the system you want and make a PR than hesitate because of whitespace.

By participating in this project you agree to abide by the Contributor Code of Conduct.

Description

  • Swift Tools 5.8.0
View More Packages from this Author

Dependencies

  • None
Last updated: Mon Mar 18 2024 09:13:37 GMT-0900 (Hawaii-Aleutian Daylight Time)