该软件包现在是 SwiftSpellbook_macOS 的一部分

sXPC - NSXPCConneciton 的 Swift 类型化封装

sXPC 允许你

XPCTransport

XPCTransport 是 sXPC 的一种附加组件,为 XPC 通信提供面向消息的方法。XPCTransport 在客户端和服务端点之间引入了一种稳定的连接

库系列

你还可以找到用于 macOS / *OS 开发的 Swift 库

XPC 服务示例

注意:完整的示例代码位于 Sample 文件夹中

假设你要通过 XPC 连接使用协议 TokenService

public struct TokenRequest: Equatable, Codable {
    public var user: String
    public var password: String
}

public struct TokenResponse: Equatable, Codable {
    public var token: String
    public var expiration: Date
}

public protocol TokenService {
    func requestToken(_ request: TokenRequest, reply: @escaping (Result<TokenResponse, Error>) -> Void)
}

创建到 TokenService 的连接并请求 token

let connection = TokenServiceXPCConnection(xpc: .service("com.alkenso.XPCService"))
connection.resume()

let proxy = connection.remoteObjectProxy { error in
    print(error)
}

let request = TokenRequest(user: "alkenso", password: "1234567_lol")
proxy.requestToken(request) {
    print($0)
}

设置 TokenService 监听器

// Define TokenService implementation
struct MockTokenService: TokenService { ... }

// Start listener
let listener = CreateServiceXPCListener(listener: listener)
listener.newConnectionHandler = {
    $0.exportedObject = MockTokenService()
    $0.resume()
    return true
}
listener.resume()

XPCTransport 示例

注意:完整的示例代码位于 Sample 文件夹中

假设我们需要实现两个非常常见、接近真实场景的用例

  1. 接收远程通知
  2. 发送分析事件

接收/发送的逻辑在另一个进程中实现,我们可以通过 XPC 连接它

// XPCService -> App
public enum RemoteNotification: Codable {
    case notify(String)
    
    // first generic parameter - approval text
    // second generic parameter - Boolean, indicating request is approved by user
    case askApproval(XPCTransportMessage<String, Bool>)
}

// App -> XPCService
public struct AnalyticEvent: Codable {
    var reason: String
    var date: Date
}

创建到 XPCService 的连接

// create XPCTransport connection
let connection = XPCTransportConnection(xpc: .service("com.alkenso.XPCService"))

// setup incoming messages handler
connection.setReceiveMessageHandler(RemoteNotification.self) { 
    switch $0 {
        case .notify(let text):
            print("[Notifcation] \(text)")
        case .askApproval(let message):
            print("[Approval] \(message.request)")
            
            // Reply to XPCTransportMessage
            message.reply(.success(true))
        }
    }
}

// receive connection state updates (react to reconnects)
connection.stateHandler = { print("Connection state: \($0)") }

// all handlers/replies will be called on this queue
connection.queue = .main

// activate connection
connection.activate()

// send analytic event through connection
do {
    try connection.send(AnalyticEvent(reason: "Button clicked", date: Date()))
} catch {
    print("Failed to send analytic event. Error: \(error)")
}

设置 XPCTransport 服务器

// create XPCTransport server
let server = XPCTransportServer(.service)

// setup incoming messages handler
server.setReceiveMessageHandler(AnalyticEvent.self) { _, event in
    print("Analytic event: \(event)")
}

// activate server
server.activate()

// ... some time later
// send notifications to all connected clients
guard let peer = server.activeConnections.first else { return }
do {
    // send simple notification
    try server.send(to: peer, message: .notify("You have new message"))
    
    // send approval request that requires reply from the client(s)
    let message = XPCTransportMessage<String, Bool>(
        request: "New sign in detected on another device. Allow it?",
        reply: { approvalResult in
            switch approvalResult {
            case .success(let approved):
                print("Approved: \(approved)")
            case .failure(let error):
                print("Approval failed: \(error)")
            }
        }
    )
    try server.send(to: peer, message: .askApproval(message))
} catch {
    print("Failed to send notification to peer. Error: \(error)")
}