主题
macOS 回调式 API
适用版本:0.1 Commercial Beta 适用模块:
XHIM最低版本:macOS 12、Swift 5.9
本页是 macOS 原生开发者的回调 API 入口。所有 onSuccess/onFailure 和事件 回调都在 MainActor 串行执行,可以直接更新 AppKit 或 SwiftUI 状态。macOS 与 iOS 复用 Swift 类型,但本页只说明 macOS 的安装后调用、窗口/进程生命周期、 Sandbox 和文件访问行为。
默认 Completion Facade 覆盖全部公开异步/抛错入口;页面代码不需要 为 SDK 调用展开 Task 或 do-catch。Swift Concurrency 同名重载仅作为 Repository、批处理和高级并发项目的可选入口。
1. 连接并登录
swift
XHIMClient.connect(
server:userID:appID:authentication:storageRootURL:
onConnecting:onConnectSuccess:onConnectFailure:
) -> XHIMRequestswift
private var client: XHIMClient?
private var connectRequest: XHIMRequest?
connectRequest = XHIMClient.connect(
server: "https://im-test.example.com",
userID: "alice",
onConnecting: {
print("XHIM macOS connecting")
},
onConnectSuccess: { [weak self] client in
self?.client = client
},
onConnectFailure: { error in
print(error.stableCode, error.message)
}
)Production 把宿主已有账号会话转换为 SDK 凭证 Provider。Window 和 ViewController 不接触短期凭证:
swift
connectRequest = XHIMClient.connect(
server: "https://im.example.com",
userID: account.userID,
authentication: .business(AccountService.shared.xhimCredential),
onConnecting: {},
onConnectSuccess: { [weak self] in self?.client = $0 },
onConnectFailure: { [weak self] in self?.show($0.message) }
)| 参数 | 类型 | 说明 |
|---|---|---|
server | String | XHIM Server HTTPS 基地址 |
userID | String | 当前业务用户 ID |
appID | String? | 单租户部署通常省略 |
authentication | XHIMAuthentication | Development 或业务凭证 Provider |
storageRootURL | URL? | 通常省略;默认写入 Application Support 的账号隔离目录 |
2. 事件监听
swift
private var eventToken: XHIMEventListenerToken?
eventToken = client.addEventListener { [weak self] event in
switch event {
case .stateChanged(let state):
self?.renderConnection(state)
case .messageUpserted(let change),
.messageStateChanged(let change):
self?.reloadMessages(conversationID: change.conversationID)
case .conversationChanged:
self?.reloadConversations()
case .presenceChanged(let update):
self?.renderPresence(update)
case .typingChanged(let update):
self?.renderTyping(update)
case .customSignalReceived(let event):
self?.handleOnlineCollaborationSignal(event.signal)
default:
break
}
}XHIMEventListenerToken 必须由账号 Session 持有,不能只存在局部变量。多窗口 共享一个 Client,可以各自安装监听并在窗口释放时取消自己的 token。
customSignalReceived 只用于光标、协作选择区、白板指针等在线瞬时状态;它不 进入 SQLite、Outbox 或 Sync,超时/离线允许丢失。需要离线补发和历史审计时应 改用版本化自定义消息。
3. 单聊与文字消息
swift
client.directConversation(
with: "bob",
onSuccess: { [weak self] conversation in
self?.client?.sendText(
conversationID: conversation.conversationID,
text: "Hello from macOS",
onSuccess: { _ in print("message queued") },
onFailure: { print($0.stableCode, $0.message) }
)
},
onFailure: { print($0.stableCode, $0.message) }
)sendText 成功表示消息进入可靠 Outbox,不表示对端已读。页面通过 messageStateChanged 后重查消息,通过已读事件后重查会话/同伴已读。
4. 会话和消息分页
swift
client.conversations(
limit: 50,
onSuccess: { [weak self] in self?.conversations = $0.conversations },
onFailure: { print($0.stableCode, $0.message) }
)
client.messages(
conversationID: conversationID,
limit: 50,
onSuccess: { [weak self] in self?.messages = $0.messages },
onFailure: { print($0.stableCode, $0.message) }
)分页 Cursor 是不透明 Data,只能原样传回同一查询。不要从 SQLite 自己拼会话 或未读数。
群聊窗口的已读上报和读者列表使用同一回调 Facade:
swift
client.markGroupMessagesRead(
conversationID: groupConversationID,
serverMessageIDs: visibleServerMessageIDs,
onSuccess: { _ in self.reloadConversation() },
onFailure: { self.showError($0.message) }
)
client.groupMessageReaders(
conversationID: groupConversationID,
serverMessageIDs: [serverMessageID],
onSuccess: { self.readerPage = $0 },
onFailure: { self.showError($0.message) }
)两个方法只消费本地已同步的服务端消息 ID,不会在 AppKit 层建第二份 逐消息已读状态。
5. 自定义消息
swift
let message = XHIMOutgoingMessage(
contentType: "com.example.order.card",
contentVersion: 1,
payload: Data(#"{"orderID":"20260728001"}"#.utf8),
fallbackText: "[订单] 20260728001"
)
client.sendMessage(
conversationID: conversationID,
message: message,
onSuccess: { _ in print("custom message queued") },
onFailure: { print($0.stableCode, $0.message) }
)未知 contentType 必须显示 fallbackText。Renderer 只负责展示,不改变协议 payload。
Presence 和 Typing 快照
swift
presenceRequest = client.subscribePresence(
userIDs: visibleMemberIDs,
onSuccess: { [weak self] snapshots in
self?.outlineView.reloadPresence(snapshots)
},
onFailure: { [weak self] error in
self?.presentError(error)
}
)
typingRequest = client.queryTyping(
conversationID: conversationID,
userID: peerUserID,
onSuccess: { [weak self] snapshot in
self?.typingLabel.isHidden = !snapshot.isTyping
},
onFailure: { [weak self] error in
self?.presentError(error)
}
)subscribePresence 失败不会部分更新本地集合。 unsubscribePresence 和 listPresenceSubscriptions 也提供同样的 onSuccess/onFailure 重载;登出或销毁 Client 才会恢复默认受众模式。
6. 附件和安全作用域
通过 NSOpenPanel 取得用户选择的 URL;启用 Sandbox 时只在读取/提交期间访问 安全作用域:
swift
guard url.startAccessingSecurityScopedResource() else { return }
defer { url.stopAccessingSecurityScopedResource() }
client.sendFile(
conversationID: conversationID,
fileURL: url,
displayName: url.lastPathComponent,
onSuccess: { _ in print("file task accepted") },
onFailure: { print($0.stableCode, $0.message) }
)上传任务由 Core 持久化。Window 关闭不取消附件任务;需要业务取消时调用 cancelMediaTask。
7. 请求取消、登出和进程退出
swift
let request = client.searchMessages(
keyword: keyword,
limit: 50,
onSuccess: { [weak self] in self?.results = $0.messages },
onFailure: { error in
if error.stableCode != "request_cancelled" {
print(error.message)
}
}
)
request.cancel()普通 XHIMRequest.cancel() 只取消本次等待;已经进入 Outbox 的消息使用 cancelMessage,媒体使用 cancelMediaTask。账号退出调用 logout;应用终止 或账号容器销毁时调用 shutdown。关闭最后一个 Window 不应自动销毁 Client。
8. 回调错误
XHIMCallbackError 的稳定字段:
| 字段 | 用途 |
|---|---|
stableCode | 业务分支,不匹配 message 文案 |
retryable | 是否适合用户重试 |
retryAfterMilliseconds | 服务端建议的最短等待 |
userAction | 登录、升级、释放空间或联系支持 |
operationID / traceID | 脱敏诊断与服务端追踪 |
所有业务 API 的模型、约束和事件载荷继续按模块查阅 SDK API Reference。macOS 的 App Sandbox、窗口管理、睡眠唤醒 和发布验收见 macOS QuickStart。