Skip to content

移动端 SDK 接入指南(Android / iOS) ​

双端 SDK 同一套协议与服务端(WS wss://<host>/api/v1/ws + 访客工单 POST /api/v1/tickets), API 语义逐一镜像。本页面向接入方;协议细节见 sdk/PROTOCOL.md(帧契约)与 平台规格 §4(API 冻结面),架构与决策见设计文档。

1. 前置要求 ​

AndroidiOS
系统版本minSdk 24+iOS 15+
权限/配置宿主 manifest 已有 INTERNET(绝大多数 App 已有)无 Info.plist 新增项(不启用推送时);强制 ATS/WSS
依赖注入零三方(白名单:kotlin-stdlib、kotlinx-coroutines、kotlinx-serialization-json、okhttp、Compose)零三方(SwiftUI/Combine 全系统库,Package.swift 无 dependencies 结构性保证)
体积增量≤ 1.5MB(R8 口径)≤ 2MB(XCFramework zip 口径)

服务端侧:wss://<host>/api/v1/ws 握手与 POST /api/v1/tickets 免认证可达;移动端原生 WS 不发送 Origin 头,若服务端开了 Origin 白名单需放行空 Origin。

2. 获取 SDK 产物 ​

当前为仓库内分发(无正式发布渠道前,不内置自动发版):

  • Android:CI android-probe job 的 AAR 产物(artifact servify-sdk-aar),或本地构建: cd sdk/android && ./gradlew :servify-sdk:assembleRelease(产物 servify-sdk/build/outputs/aar/)。
  • iOS:XCFramework(静态,BUILD_LIBRARY_FOR_DISTRIBUTION),CI ios-macos job 产物, 或本地构建:scripts/check-ios-sdk-size.sh(产物 sdk/ios/build/ServifyKit.xcframework)。
  • CocoaPods 不提供(M3 评估结论:SPM/XCFramework 单轨,按需重建触发条件见 评估文档)。

引入方式:

kotlin
// Android:libs/ 放入 AAR
implementation(files("libs/servify-sdk-release.aar"))
swift
// iOS:XCFramework 拖入工程(General → Frameworks, Embed 静态库选 Do Not Embed),
// 或本地路径 SwiftPM 引用(远程 URL 引用需仓库根的 Package.swift,暂不适用——见评估文档)

3. 最小接入(≤10 行) ​

Android ​

kotlin
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main) // 宿主自己的业务界面

        val chat = ServifyChat.create(
            this,
            ServifyConfig(apiUrl = "https://your-servify-host"),
        )
        chat.show(this) // 挂浮动按钮 + 首次触发 WS 连接
    }
}
  • show(hostActivity):浮钮(右下角 56dp,主色取 branding.primaryColor)+ 面板; hide() 收起面板但连接保持,期间到达的坐席/AI 消息计入未读(浮钮角标)。
  • 宿主 Activity 需为 ComponentActivity(面板经 ComposeView 挂载);未桥接的宿主自动降级: 浮钮仍显示,面板不挂载。
  • AAR 自带 consumer-rules.pro(kotlinx.serialization 的 R8 规则),宿主无需手工配置。

iOS(SwiftUI) ​

swift
struct ContentView: View {
    var body: some View {
        // 浮钮 + 抽屉/全屏面板一行拉起
        ServifyView(config: try! ServifyConfig(apiUrl: "https://your-servify-host"))
    }
}
  • 需要门面级控制(事件订阅/工单/推送注册)时:let chat = ServifyChat.create(config:) 后用 ServifyView(chat: chat) 挂载。
  • 收起(点 ✕ / scrim)连接保持;展开即清未读并回放 hide 期间累积的消息。

4. 配置参考(ServifyConfig) ​

字段类型默认说明
apiUrlString必填服务端地址,须 https:///wss:// 前缀(否则构造期报 config_invalid);无尾斜杠、无路径后缀,SDK 自行拼 WS 与 REST 路径
guestTokenString?null访客 token(服务端 POST /api/v1/guest/session 签发,宿主后端持 service key 换取;security.guest_token.required=false(默认)时握手不消费,开启后强制校验签名/时效/会话绑定、失败 401 拒升级)
brandingBranding全默认品牌化四件套,见下表
presentationStyleDrawer / FullScreenDrawer面板形态
loggingEnabledBoolfalse调试日志
pushTokenProvider闭包null/nil推送 token 获取闭包(宿主 FCM/APNs 集成面);null = 推送不启用

Branding 四件套:title(组件标题,默认「在线客服」)、primaryColor(Int/UInt32,ARGB,默认 0xFF2563EB 品牌蓝)、 welcomeText(欢迎行)、offlineText(断线系统提示行,null = 不出提示行)。

5. 工单创建 / 推送注册 / 离线提示(M3 API) ​

kotlin
// Android
val config = ServifyConfig(
    apiUrl = "https://your-servify-host",
    branding = Branding(offlineText = "客服当前不在线,请稍后再来"),
    pushTokenProvider = { fetchFcmToken() },
)

val receipt: TicketReceipt? = chat.createTicket(title = "无法登录", description = "选填补充")
chat.registerPushToken() // 挂起,返回 Boolean
chat.syncReadState() // 增强面:提交服务端已读游标,挂起,返回 Boolean
swift
// iOS
let config = try ServifyConfig(
    apiUrl: "https://your-servify-host",
    branding: Branding(offlineText: "客服当前不在线,请稍后再来"),
    pushTokenProvider: { await fetchAPNsToken() }
)

let receipt: TicketReceipt? = await chat.createTicket(title: "无法登录", description: "选填补充")
_ = await chat.registerPushToken()
_ = await chat.syncReadState() // 增强面:提交服务端已读游标

语义:

  • createTicket:服务端自动组装会话摘要(最近 10 条,[访客]/[AI]/[客服] 标注)随单 提交,成功返回 TicketReceipt(ticketId),会话内出现系统提示行"工单 #N 已创建…"; 失败返回 null 且 error 流给出错误(IO→network、非 2xx/畸形→ticket_failed)。也可从 会话面板标题栏"工单"按钮发起(表单:标题必填 + 描述选填)。
  • registerPushToken:未配置 provider→error 流 unsupported;provider 返回 null (宿主未授权,正常态)→静默 false,非错误;取到 token→真实上报 POST {apiUrl}/api/v1/push/register(2xx→true;IO/HTTP 失败→false,可重报)。 服务端下发编排与 FCM/APNs 传输已就位(离线访客收推送),端到端真实凭证联调进行中。
  • offlineText:断线(重连耗尽/握手失败)后自动追加系统提示行;用户主动关闭面板不提示。
  • syncReadState(可选增强):把 SDK 内部对账(断线补拉)确认的最新服务端消息 ID 提交为 服务端已读游标 POST {apiUrl}/api/v1/sessions/{id}/read。SDK 的未读角标 V1 仍由客户端 推导(本方法不改变其行为);只有当宿主需要跨设备/跨入口一致未读(如自有服务端按 游标展示未读)时才需要调用——典型时机:会话页回前台、用户点开推送。无已对账消息→静默 false(正常态);IO/HTTP 失败→error 流 network + false,可在下次时机重试;游标只前进 不后退,重复提交幂等。

6. 事件流(chat.events) ​

流内容
messages消息(含流式增量标记 isStreaming;System 提示行不来自服务端帧)
unreadCount未读数(面板可见期间为 0)
connectionStateidle / connecting / connected / reconnecting(n) / disconnected
agentAssigned坐席接入
waitingInQueue排队提示
error错误事件(码表见下)

Android 为 Kotlin Flow(collect),iOS 为 EventStream/StateStream(for await)。 订阅即时生效:订阅前发生的历史经 historySnapshot() 获取(内存级会话连续性,面板 hide 期间消息不丢)。

7. 错误码 ​

config_invalid(构造期抛出)/ network / server_unavailable / handshake_rejected / send_timeout / unsupported / ticket_failed——双端 ServifyError 七码一致。详细语义见 平台规格 §4.5。

8. 接入自验清单 ​

  1. wss://<host>/api/v1/ws?session_id=test 握手 101;
  2. apiUrl 无尾斜杠、无路径后缀;
  3. 混淆构建(Android R8)/ 裁剪构建(iOS)通过——无需接入方额外配置;
  4. 退后台 → 回前台:会话页状态与未读计数无丢失;
  5. 工单创建后管理面 tickets 列表可见(含 AI 摘要列);
  6. 体积对账:AAR ≤ 1.5MB / XCFramework ≤ 2MB。

常见问题(握手 400/403、反复断线、无回显)对照表见 平台规格 §5.4。

9. V1 边界(明确不做的,防止接入误期许) ​

离线发送队列、消息撤回/编辑、富媒体消息(图片/文件)、坐席移动端、多语言内置 (V1 中文,Branding 预留 i18n 钩子)。断连期间的客户消息不自动补发(发送超时按 失败标记,手动重发)——重连成功后 SDK 自动增量补拉(GET /api/v1/sessions/:session_id/messages,after_id 游标 + has_more 续拉):断连 期间坐席/AI 的消息自动合并进 history 并计未读,接入方无需自行对账;服务端未读 游标端点同样已就位(POST /api/v1/sessions/:session_id/read + GET .../unread),SDK V1 未读以客户端推导为准(服务端游标是宿主需要跨入口 一致未读时的增强面)。Keychain 凭证存储不在 V1(匿名 session 无凭证)。