主题
消息与会话 API
1. 消息发送与状态
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
message.send_text | 发送文字 | conversationID, 可选 clientMessageID, text | XHIMSendReceipt |
message.send_custom | 发送任意标准/自定义消息 | conversationID, 可选 clientMessageID, XHIMOutgoingMessage | XHIMSendReceipt |
message.retry | 重试一条失败的 Outbox 消息 | clientMessageID | XHIMSendReceipt |
message.cancel | 取消一条尚未被服务端接受的消息 | clientMessageID | XHIMSendReceipt |
sendText 是 text/plain@1 的便利接口。其他类型统一通过 XHIMOutgoingMessage:
| 字段 | 约束 |
|---|---|
contentType | 稳定命名空间,例如 com.customer.order.card |
contentVersion | 该类型自己的正整数版本 |
payload | 不可变二进制载荷;不能放本地路径或临时下载 URL |
fallbackText | 旧客户端、通知和会话预览可安全显示的文本 |
成功返回表示消息意图已被本地可靠 Outbox 接受,不等于对方已经收到。最终状态 从消息查询结果的 state 和 messageStateChanged 事件判断。
clientMessageID 省略时由 SDK 生成。业务需要跨进程幂等重试时,应自行生成 UUID,并在每次重试中复用同一个值。
2. 编辑、撤回和仅自己删除
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
message.edit_text | 编辑文字消息 | conversationID, serverMessageID, mutationID, expectedRevision, text | 更新后的 XHIMMessage |
message.recall | 撤回消息 | conversationID, serverMessageID, mutationID, expectedRevision | 更新后的 XHIMMessage |
message.delete_for_self | 仅当前账号删除 | conversationID, serverMessageID, mutationID, expectedRevision | XHIMMessageVisibilityMutation |
expectedRevision == 0 表示创建型/未指定 CAS 基线,其他值表示只在服务端当前 revision 与之相等时执行。发生并发冲突时重新查询目标消息或可见性,再让用户 决定是否重试;不要自动不断增加条件绕过冲突。
编辑和撤回会保留稳定消息身份,mutationKind 分别变为 editText 和 recall。业务 UI 应根据模型显示“已编辑”或“已撤回”,不要自行删除时间线项。
3. 单条、分页和搜索
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
message.get | 按客户端消息 ID 获取单条 | clientMessageID | XHIMMessage |
message.list | 读取本地会话消息页 | conversationID, 可选 cursor, limit | XHIMMessagePage |
message.history | 按服务端序号读取历史 | conversationID, 可选 continuation, limit | XHIMMessageHistoryPage |
message.search | 搜索本地消息投影 | XHIMMessageSearchQuery, 可选 cursor, limit | XHIMMessagePage |
messages 用于常规本地时间线和游标翻页。getMessageHistory 使用稳定服务端 序号 continuation,适合清空/隐藏视图规则和历史回溯。两种 continuation 都是 SDK 所有,不允许用页码或时间戳代替。
XHIMMessageSearchQuery 可按文本、会话、发送者、内容类型和时间范围组合过滤。 搜索结果仍是 XHIMMessage,未知自定义类型也保留原 payload 与 fallback。
分页通用规则:
- 首次调用传空 cursor / continuation;
nextCursor或continuation为空表示结束;- cursor 只在同一账号、同一查询条件下复用;
- 收到投影变更后刷新第一页,不能假定旧 cursor 是实时订阅;
limit必须处于各 Facade 声明的范围,推荐聊天页 50、会话页 50。
4. 会话列表与单聊
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
conversation.list | 读取本地会话页 | 可选 cursor, limit | XHIMConversationPage |
conversation.direct | 获取或创建稳定单聊会话 | peerUserID | XHIMDirectConversation |
conversation.clear | 为当前账号清空到指定序号 | conversationID, throughServerSequence, mutationID, expectedRevision | XHIMConversationViewMutation |
conversation.hide | 隐藏会话当前视图 | conversationID, mutationID, expectedRevision | XHIMConversationViewMutation |
请优先用 directConversation(peerUserID) 获取单聊 ID,不要在客户端拼接 alice_bob。服务端返回的 participant 和 conversation ID 才是跨端合同。
清空和隐藏是当前账号的 Server-authoritative 视图操作,不会替其他成员删除 消息。clearConversation 的 throughServerSequence 必须来自已查询消息或会话 高水位。
5. 已读、未读和对端已读
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
conversation.mark_read | 提交一个会话的已读水位 | conversationID, throughServerSequence | XHIMConversationReadReceipt |
conversation.mark_all_read | 幂等标记全部会话已读 | mutationID, maxChangedReads | XHIMMarkAllConversationsReadResult |
conversation.total_unread | 查询本地总未读 | 无 | 64 位整数 |
conversation.peer_reads | 查询成员已读投影 | conversationID, limit | XHIMConversationPeerReadPage |
已读水位只前进不后退。聊天页进入前台并确定用户确实看见消息后,再提交当前 最大 serverSequence。收到 conversationReadChanged 或 conversationChanged 后重查会话,而不是在 UI 中直接对未读数做加减。
markAllConversationsRead 的结果可能因保护上限只带一部分 changedReads; changedReadsTruncated == true 或 localProjectionComplete == false 时刷新整个 会话列表。
6. 会话偏好与本地草稿
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
conversation.set_preference | 原子修改置顶和免打扰 | conversationID, isPinned, notificationsMuted, mutationID, expectedRevision | XHIMConversationPreference |
conversation.set_draft | 保存账号隔离本地草稿 | conversationID, XHIMOutgoingMessage | XHIMConversationDraft |
conversation.get_draft | 读取草稿 | conversationID | XHIMConversationDraft |
conversation.clear_draft | 清除草稿 | conversationID | XHIMConversationDraft |
偏好是可同步的服务端状态,使用 mutation ID 和 revision。草稿是本机、当前账号 的本地状态,不上传、不产生聊天消息,也不要把密码、Token 或支付敏感信息放入 草稿 payload。
7. 典型聊天页
swift
eventToken = client.addEventListener { [weak self] event in
switch event {
case .messageUpserted(let change),
.messageStateChanged(let change):
if change.conversationID == conversationID {
self?.reloadFirstPage()
}
default:
break
}
}
client.messages(
conversationID: conversationID,
limit: 50,
onSuccess: { page in
self.messages = page.messages
},
onFailure: { error in
self.showError(error.message)
}
)
client.sendText(
conversationID: conversationID,
text: composer.text,
onSuccess: { _ in
composer.clear()
},
onFailure: { error in
self.showError(error.message)
}
)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
页面销毁时取消监听 token,但不要因为一个页面消失就关闭账号级 Client。 事件的完整平台写法和线程要求见事件与回调。