Skip to content

iOS 回调式 API

适用版本:0.1 Commercial Beta
适用模块:XHIM
最低版本:iOS 15、Swift 5.9

本页是 iOS 开发者的首选 API 入口。它采用与主流 IM SDK 相同的 onSuccess / onFailure 形式,不要求接入者先理解 Swift async throws。 所有回调都在 MainActor 执行,可以直接更新 UIKit / SwiftUI 状态。

原有 Swift Concurrency API 完整保留,适合 Repository、批处理和高级并发控制。 两套入口调用的是同一个 XHIM Core,不会形成两份状态或两套数据库。

1. 回调类型

swift
typealias XHIMSuccessCallback<Value> = (Value) -> Void
typealias XHIMFailureCallback = (XHIMCallbackError) -> Void
typealias XHIMEventCallback = (XHIMEvent) -> Void
类型用途
XHIMRequest一次异步请求的句柄;调用 cancel() 取消等待中的请求
XHIMEventListenerToken账号事件监听句柄;释放或调用 cancel() 即退订
XHIMCallbackError稳定错误对象,包含 code、stableCode、retryable 和 traceID

XHIMRequest.cancel() 只取消当前 API 请求。已经进入 Outbox 的消息用 cancelMessage,持久媒体任务用 cancelMediaTask

2. 连接并登录

方法原型

swift
XHIMClient.connect(
    server:userID:appID:authentication:storageRootURL:
    onConnecting:onConnectSuccess:onConnectFailure:
) -> XHIMRequest

参数

参数类型必填说明
serverStringXHIM Server 基地址,例如 https://im.example.com
userIDString当前业务用户 ID
appIDString?单租户可省略,由服务端公开配置返回
authenticationXHIMAuthenticationDevelopment 默认免 Token;Production 使用 business provider
storageRootURLURL?自定义账号数据库根目录,通常省略

回调

回调参数触发时机
onConnectingSDK 开始发现配置、启动 Core 并建立连接
onConnectSuccessXHIMClient登录、连接和首次同步完成
onConnectFailureXHIMCallbackError任一步骤失败

Development 示例

swift
private var client: XHIMClient?
private var connectRequest: XHIMRequest?

connectRequest = XHIMClient.connect(
    server: "https://im-test.example.com",
    userID: "alice",
    onConnecting: {
        print("XHIM 正在连接")
    },
    onConnectSuccess: { [weak self] client in
        self?.client = client
        print("XHIM 已连接")
    },
    onConnectFailure: { error in
        print("连接失败:", error.code, error.message)
    }
)

Development Server 显式启用免密测试登录时,客户端只填写 serveruserID。App 不需要手工申请、保存或续期测试 Token。

Production 示例

swift
connectRequest = XHIMClient.connect(
    server: "https://im.example.com",
    userID: account.userID,
    authentication: .business(
        AccountService.shared.xhimCredential
    ),
    onConnecting: {
        connectionState = .connecting
    },
    onConnectSuccess: { [weak self] client in
        self?.client = client
        self?.connectionState = .connected
    },
    onConnectFailure: { [weak self] error in
        self?.showConnectionError(error)
    }
)

AccountService.xhimCredential 属于 App 自己的账号层。页面不接触 Token; XHIM 需要续期时会自动再次调用它。

3. 添加事件监听

方法原型

swift
client.addEventListener(_:) -> XHIMEventListenerToken

示例

swift
private var eventToken: XHIMEventListenerToken?

eventToken = client.addEventListener { [weak self] event in
    switch event {
    case .stateChanged(let state):
        self?.updateConnectionState(state)

    case .messageUpserted(let change),
         .messageStateChanged(let change):
        self?.reloadMessages(conversationID: change.conversationID)

    case .conversationChanged:
        self?.reloadConversations()

    case .conversationReadChanged:
        self?.reloadUnreadCount()

    case .presenceChanged(let update):
        self?.updatePresence(update)

    case .typingChanged(let update):
        self?.updateTyping(update)

    default:
        break
    }
}

必须先保存监听 token,再查询首屏。退出账号时:

swift
eventToken?.cancel()
eventToken = nil

事件是“本地投影已变化”的通知,不是完整消息副本。收到消息或会话事件后,调用 查询 API 刷新对应页面。

4. 获取或创建单聊

方法原型

swift
client.directConversation(
    with:onSuccess:onFailure:
) -> XHIMRequest

示例

swift
client.directConversation(
    with: "bob",
    onSuccess: { conversation in
        self.openChat(conversationID: conversation.conversationID)
    },
    onFailure: { error in
        self.showError(error.message)
    }
)

不要在客户端自行拼接单聊会话 ID。

5. 发送文字消息

方法原型

swift
client.sendText(
    conversationID:clientMessageID:text:
    onSuccess:onFailure:
) -> XHIMRequest

参数

参数类型必填说明
conversationIDString服务端返回的会话 ID
clientMessageIDString?幂等 ID;省略时由 SDK 生成
textString文字内容

示例

swift
let messageID = UUID().uuidString.lowercased()

client.sendText(
    conversationID: conversationID,
    clientMessageID: messageID,
    text: "你好,晞晗IM",
    onSuccess: { receipt in
        print("消息已进入发送队列:", receipt.payload)
    },
    onFailure: { error in
        if error.retryable {
            self.showRetryButton(clientMessageID: messageID)
        } else {
            self.showError(error.message)
        }
    }
)

成功表示可靠 Outbox 已接受消息,不等于对端已读。最终发送状态通过消息查询和 messageStateChanged 事件刷新。

6. 发送自定义消息

方法原型

swift
client.sendMessage(
    conversationID:clientMessageID:message:
    onSuccess:onFailure:
) -> XHIMRequest

示例

swift
let payload = Data(
    #"{"orderID":"20260728001","amount":199}"#.utf8
)
let message = XHIMOutgoingMessage(
    contentType: "com.example.order.card",
    contentVersion: 1,
    payload: payload,
    fallbackText: "[订单] 20260728001"
)

client.sendMessage(
    conversationID: conversationID,
    message: message,
    onSuccess: { _ in
        print("自定义消息已入队")
    },
    onFailure: { error in
        print(error.stableCode, error.message)
    }
)

自定义类型必须使用自有命名空间、正整数版本和安全 fallback。配套解析与 UI Renderer 见二次开发扩展点

7. 查询会话列表

方法原型

swift
client.conversations(
    cursor:limit:onSuccess:onFailure:
) -> XHIMRequest

示例

swift
client.conversations(
    limit: 50,
    onSuccess: { page in
        self.items = page.conversations
        self.nextCursor = page.nextCursor
    },
    onFailure: { error in
        self.showError(error.message)
    }
)

下一页把 page.nextCursor 原样传回 cursor。收到 conversationChanged 后重新查询第一页。

8. 查询消息列表

方法原型

swift
client.messages(
    conversationID:cursor:limit:onSuccess:onFailure:
) -> XHIMRequest

示例

swift
client.messages(
    conversationID: conversationID,
    limit: 50,
    onSuccess: { page in
        self.messages = page.messages
        self.nextCursor = page.nextCursor
    },
    onFailure: { error in
        self.showError(error.message)
    }
)

查询更早的服务端历史使用:

swift
client.getMessageHistory(
    conversationID: conversationID,
    continuation: historyContinuation,
    limit: 50,
    onSuccess: { page in
        self.prepend(page.messages)
        self.historyContinuation = page.continuation
    },
    onFailure: { error in
        self.showError(error.message)
    }
)

9. 已读和未读

标记会话已读

swift
client.markConversationRead(
    conversationID: conversationID,
    throughServerSequence: latestServerSequence,
    onSuccess: { receipt in
        self.unreadCount = receipt.unreadCount
    },
    onFailure: { error in
        self.showError(error.message)
    }
)

查询总未读

swift
client.totalUnreadCount(
    onSuccess: { count in
        UIApplication.shared.applicationIconBadgeNumber = Int(count)
    },
    onFailure: { error in
        print(error.message)
    }
)

全部标记已读

swift
client.markAllConversationsRead(
    mutationID: UUID().uuidString,
    onSuccess: { result in
        self.reloadConversations()
        print("已更新会话数:", result.changedConversationCount)
    },
    onFailure: { error in
        self.showError(error.message)
    }
)

10. 用户、好友与群组查询

这些查询具有相同的 onSuccess / onFailure 结构:

swift
client.currentUserProfile(
    onSuccess: { profile in self.profile = profile },
    onFailure: { error in self.showError(error.message) }
)

client.userProfiles(
    userIDs: ["alice", "bob"],
    onSuccess: { batch in self.consume(batch) },
    onFailure: { error in self.showError(error.message) }
)

client.friendships(
    limit: 50,
    onSuccess: { page in self.friends = page.items },
    onFailure: { error in self.showError(error.message) }
)

client.groups(
    limit: 50,
    onSuccess: { page in self.groups = page.items },
    onFailure: { error in self.showError(error.message) }
)

client.groupMembers(
    conversationID: groupID,
    limit: 50,
    onSuccess: { page in self.members = page.items },
    onFailure: { error in self.showError(error.message) }
)

同类回调式查询还包括 friendRequestsblocksgroupJoinRequests

11. 媒体任务

图片、视频、语音和文件由 SDK 建立持久媒体任务。创建任务成功不代表上传完成, 最终状态通过 mediaTaskUpdated 监听并用 mediaTask 查询。

swift
client.sendImage(
    conversationID: conversationID,
    fileURL: attachment.localURL,
    mimeType: attachment.mimeType,
    width: 1080,
    height: 1920,
    onSuccess: { task in
        self.observeMediaTask(task.taskID)
    },
    onFailure: { error in
        self.showError(error.message)
    }
)

client.mediaTask(
    taskID: taskID,
    onSuccess: { task in
        self.updateProgress(task)
    },
    onFailure: { error in
        self.showError(error.message)
    }
)

同样提供 sendVideosendAudiosendFile 回调接口;需要完全自定义 持久任务参数时使用 acceptMediaUpload

取消持久媒体任务:

swift
client.cancelMediaTask(
    taskID: taskID,
    onSuccess: { task in self.updateProgress(task) },
    onFailure: { error in self.showError(error.message) }
)

12. 失败回调与错误处理

swift
func handle(_ error: XHIMCallbackError) {
    switch error.userAction {
    case .retry:
        showRetryButton()
    case .updateCredential:
        accountService.refreshLogin()
    case .checkNetwork:
        showNetworkNotice()
    case .contactSupport:
        showSupport(traceID: error.traceID)
    case .none:
        showError(error.message)
    }
}
字段用途
code平台稳定整数错误码
domain错误子系统
stableCode业务分支和统计的稳定字符串
retryable是否允许使用同一幂等 ID 重试
retryAfterMilliseconds建议的最短等待时间
userAction推荐的用户或账号层动作
operationID / traceID客服与服务端排障
message日志和用户提示,不用于业务判断

13. 退出登录

swift
client.logout(
    onSuccess: { [weak self] in
        self?.eventToken?.cancel()
        self?.eventToken = nil
        self?.client = nil
    },
    onFailure: { error in
        print("退出失败:", error.message)
    }
)

账号容器销毁时还应调用 shutdown() 完成不可逆资源释放。需要完全使用 Swift Concurrency 的团队,可继续查阅100 个 API 并发示例

XHIM 客户端 SDK 与服务端文档