Created
March 12, 2026 18:44
-
-
Save Segmentational/b3bde68eedf1a4e2e94c887d5c4df7aa to your computer and use it in GitHub Desktop.
XPC Session Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import OSLog | |
| import XPC | |
| /// Executes an asynchronous end-to-end XPC round‑trip against the bundled XPC service | |
| /// to validate connectivity, code‑signing requirements, request/response encoding, | |
| /// and basic message handling. | |
| /// | |
| /// The function: | |
| /// - Creates an `XPCSession` targeting the XPC service with bundle identifier | |
| /// `io.polyium.dx.XPT`. | |
| /// - Configures a strict peer requirement that the service is signed by the same team | |
| /// and matches the app’s signing identifier (via `Entitlements.identifier()`). | |
| /// - Installs: | |
| /// - An incoming message handler that logs whether a reply is expected and echoes a | |
| /// simple test payload back to the sender. | |
| /// - A cancellation handler that logs rich cancellation reasons. | |
| /// - Activates the session, sends a test request (`XPCNS.Example.Input(first: 23, second: 19)`), | |
| /// and awaits a typed response (`XPCNS.Example.Output`) using | |
| /// `withCheckedThrowingContinuation`. | |
| /// - Defers session cancellation to ensure resources are released promptly. | |
| /// - Logs either the computed result on success or a descriptive error on failure. | |
| /// | |
| /// ### Security | |
| /// - Enforces that the peer is signed by the same team and matches the signing identifier, | |
| /// mitigating the risk of connecting to an untrusted service. | |
| /// | |
| /// - Important: The function assumes the presence of the `XPCNS.Example.Input` and | |
| /// `XPCNS.Example.Output` types on both client and service, and that the service | |
| /// understands and responds with the expected schema. | |
| /// | |
| /// - Note: The session is always cancelled at the end of the request cycle, even on error, | |
| /// to avoid leaking XPC resources. | |
| /// | |
| /// - SeeAlso: `XPCSession`, `XPCReceivedMessage`, `XPCRichError`, `Entitlements.identifier()` | |
| private func example() async { | |
| let logger = Internal.Logger() | |
| let session: XPCSession | |
| let identifier: String? | |
| let request: (any Encodable) = XPCNS.Example.Input(first: 23, second: 19) | |
| var response: XPCNS.Example.Output? | |
| let handler: (@Sendable (XPCReceivedMessage) -> (any Encodable)?)? = { message in | |
| // Indicates if the service expects a response. | |
| let reply: Bool = message.expectsReply | |
| logger.info("XPC reply expectation is \(reply ? "expected" : "not expected").") | |
| let request = XPCNS.Example.Input(first: 23, second: 19) | |
| message.reply(request) | |
| return nil | |
| } | |
| let cancellation: (@Sendable (XPCRichError) -> Void)? = { error in | |
| logger.notice("XPC session cancelled: \(error)") | |
| } | |
| do { | |
| identifier = try Entitlements.identifier() | |
| } catch { | |
| logger.warning("Unable to get signing identifier. Session will not enforce peer requirement(s). Error: \(error)") | |
| identifier = nil | |
| } | |
| if let identifier { | |
| logger.notice("XPC Session Peer Requirement Signing Identifier: \(identifier)") | |
| } else { | |
| logger.warning("Invalid or missing XPC peer requirement signing identifier. Session will not enforce peer requirement(s).") | |
| } | |
| do { | |
| session = try XPCSession( | |
| xpcService: "io.polyium.dx.XPT", | |
| targetQueue: .global(qos: .utility), | |
| options: .inactive, | |
| requirement: .isFromSameTeam(andMatchesSigningIdentifier: identifier), | |
| incomingMessageHandler: handler, | |
| cancellationHandler: cancellation | |
| ) | |
| response = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<XPCNS.Example.Output, any Error>) in | |
| defer { session.cancel(reason: Cancellation.completed) } | |
| do { | |
| try session.activate() | |
| } catch { | |
| response = nil | |
| logger.error("Failed to activate XPC session. Error: \(error)") | |
| continuation.resume(throwing: error) | |
| return | |
| } | |
| do { | |
| try session.send(request) { (result: Result<XPCReceivedMessage, XPCRichError>) in | |
| do { | |
| let value = try result.get() | |
| let response = try value.decode(as: XPCNS.Example.Output.self) | |
| continuation.resume(returning: response) | |
| } catch { | |
| continuation.resume(throwing: error) | |
| } | |
| } | |
| } catch { | |
| response = nil | |
| logger.error("Received an error while sending XPC service request. Error: \(error)") | |
| continuation.resume(throwing: error) | |
| } | |
| } | |
| } catch { | |
| logger.error("XPC add test failed: \(String(describing: error), privacy: .public)") | |
| } | |
| if let response { | |
| logger.notice("XPC add result: \(response.result, privacy: .public)") | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment