# XuqmGroup iOS SDK 文档 > Swift 5.9 · iOS 16+ · macOS 13+ · async/await ## 模块结构 ``` XuqmGroup-iOSSDK/ ├── Package.swift # SPM 包定义 ├── XuqmSDK.podspec # CocoaPods 发版描述 ├── XuqmWebViewSDK.podspec # WebView 独立模块发版描述 ├── PUBLISH.md # 发版操作手册 ├── Sources/XuqmCoreSDK/ # 核心:初始化、HTTP、Token ├── Sources/XuqmImSDK/ # IM:WebSocket 实时通信 ├── Sources/XuqmPushSDK/ # 推送:APNs Token 注册 ├── Sources/XuqmUpdateSDK/ # 版本管理:检查更新 ├── Sources/XuqmLicenseSDK/ # 授权管理 ├── Sources/XuqmFileSDK/ # 文件上传、下载、本地打开 └── Sources/XuqmWebViewSDK/ # WebView 嵌入式组件 / 独立页面 ├── XWebViewBridge.swift # 页面配置 / 控制器桥接 ├── XWebViewPage.swift # 独立页面 ├── XWebViewTypes.swift # 配置 / 控制器协议 └── XWebViewView.swift # 嵌入式组件 ``` ## 集成 ### Swift Package Manager(推荐) 在 Xcode → File → Add Package Dependencies,输入仓库地址: ``` https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git ``` 选择版本规则 `Up to Next Major: 0.1.0`。 或在 `Package.swift` 中添加: ```swift dependencies: [ .package(url: "https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git", from: "0.1.0") ], targets: [ .target(name: "YourApp", dependencies: ["XuqmSDK", "XuqmWebViewSDK"]) ] ``` ### CocoaPods ```ruby # Podfile source 'https://xuqinmin.com/xuqinmin12/xuqm-specs.git' source 'https://cdn.cocoapods.org/' pod 'XuqmSDK', '~> 0.1.0' pod 'XuqmWebViewSDK', '~> 0.1.0' ``` --- ## 快速开始 ### 1. 初始化(AppDelegate / @main) ```swift import XuqmSDK @main struct MyApp: App { init() { XuqmSDK.initialize( appKey: "ak_your_app_key", appSecret: "as_your_app_secret", debug: true ) } } ``` ### 2. 登录后存储 Token ```swift // 调用你的业务登录接口获得 token await XuqmSDK.setToken(token) ``` ### 3. WebView 独立模块 `XuqmWebViewSDK` 不需要单独初始化,直接依赖即可使用。 ```swift import XuqmWebViewSDK // 嵌入式组件:直接放到任意 SwiftUI 页面中 XWebViewView( config: XWebViewConfig( url: "https://example.com", title: "嵌入式网页" ) ) // 独立页面:在导航栈中直接 push NavigationLink("打开页面模式") { XWebViewPage( config: XWebViewConfig( url: "https://example.com", title: "独立页面" ) ) } ``` --- ## Core ### ApiClient 基于 `URLSession`,async/await,自动附加 Bearer Token,统一解析 `ApiResponse`。 ```swift // 定义响应模型 struct LoginResponse: Decodable { let token: String } // 发起请求 let response: LoginResponse = try await ApiClient.post( path: "/api/im/auth/login", body: ["appKey": appKey, "userId": userId] ) ``` ### TokenStore 基于 `Keychain` 存储(`kSecClassGenericPassword`),App 重启后自动恢复。 ```swift await XuqmSDK.setToken("eyJ...") // 存储 let token = XuqmSDK.getToken() // 读取(同步) await XuqmSDK.setToken(nil) // 清除(登出) ``` --- ## IM ### ImClient ```swift import XuqmSDK class ChatViewController: UIViewController, ImEventDelegate { let imClient = ImClient() override func viewDidLoad() { super.viewDidLoad() imClient.delegate = self imClient.connect() } // 发送消息 func sendMessage() throws { try imClient.send(SendMessageParams( toId: "user_002", chatType: .single, msgType: .text, content: "Hello!" )) } // 撤回消息 func revokeMessage(msgId: String) throws { try imClient.revoke(msgId: msgId) } deinit { imClient.disconnect() } } // MARK: - ImEventDelegate extension ChatViewController { func onConnected() { print("IM 连接成功") } func onDisconnected(code: Int, reason: String) { print("断开: \(code)") } func onMessage(_ msg: ImMessage) { /* 更新 UI */ } func onRevoke(msgId: String, operatorId: String) { /* 标记消息已撤回 */ } func onError(_ error: Error) { print("错误: \(error)") } } ``` ### ImMessage 结构 ```swift public struct ImMessage: Decodable { public let id: String public let fromId: String public let toId: String public let chatType: ChatType // single / group public let msgType: MsgType public let content: String public let extra: String? public let revoked: Bool public let createdAt: String public let editedAt: Int64? } ``` ### 消息类型(MsgType) `text` / `image` / `video` / `audio` / `file` / `custom` / `location` / `notify` / `richText` / `callAudio` / `callVideo` / `forward` ### 自动重连 断线后指数退避重连,初始 3s,最大 30s。`disconnect()` 后停止。 --- ## Push ```swift import XuqmSDK // 在 didRegisterForRemoteNotificationsWithDeviceToken 中调用 func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Task { let token = deviceToken.map { String(format: "%02x", $0) }.joined() try await PushSDK.registerToken( token, userId: "user_001" ) } } ``` `PushSDK.registerToken` 内部将 `Data` 转换为十六进制字符串,vendor 固定为 `APNS`。 --- ## Update ### 检查原生版本更新 ```swift import XuqmSDK let result = try await UpdateSDK.checkAppUpdate(currentVersionCode: 123) if result.needsUpdate, let info = result.info { print("最新版本: \(info.versionName)") // iOS 只能跳转 App Store if let url = URL(string: info.appStoreUrl) { await UIApplication.shared.open(url) } } ``` `UpdateSDK` 这里只负责 iOS App 版本更新。RN 热更新如果需要,走独立的 RN SDK / RN 更新模块,不并入统一发版能力。 `XuqmWebViewSDK` 提供独立的 WebView 组件和页面,可与 `XuqmSDK` 同时使用,也可单独依赖。 --- ## XuqmFileSDK 文件上传、下载、本地打开的统一入口。`XuqmImSDK` 和 `XuqmWebViewSDK` 均依赖此模块。 ### 上传 ```swift import XuqmFileSDK let result: FileUploadResult = try await FileSDK.shared.upload( fileURL: localFileURL, thumbnailData: thumbnailJpegData // 可选 ) // result.url / .thumbnailUrl / .hash / .size / .mimeType / .ext ``` ### 下载 ```swift // 下载存储目标 public enum FileDownloadDestination: Sendable { case sandbox // App Documents 目录(在 Files App 中以 App 名称显示) case userPick // 弹出系统存储面板,由用户选择保存位置 } // URL 下载,实时进度回调 let localURL: URL = try await FileSDK.shared.download( url: "https://example.com/report.pdf", fileName: "report.pdf", // 可选,默认从 URL 推断 destination: .sandbox, onProgress: { progress in // 0–100 print("下载进度:\(progress)%") } ) // Blob / base64 数据写盘 let localURL: URL = try FileSDK.shared.saveBlobDownload( base64Data: b64String, fileName: "export.xlsx", destination: .userPick ) ``` --- ## XuqmWebViewSDK ### XWebViewConfig 完整参数 | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `url` | String | `""` | 初始加载地址 | | `title` | String | `""` | 页面标题(独立页面模式使用) | | `hideToolbar` | Bool | `false` | 隐藏独立页面顶栏 | | `hideStatusBar` | Bool | `false` | 隐藏状态栏 | | `userAgent` | String? | `nil` | 自定义 User-Agent | | `injectedJavaScript` | String? | `nil` | 页面加载后注入的额外 JS | | `downloadDestination` | FileDownloadDestination | `.sandbox` | 下载文件存储目标 | | `onMessage` | `(String) -> Void`? | `nil` | H5 发送消息的回调 | ### 文件选择与拍照 WKWebView 原生支持 ``,SDK 额外处理: - `` 摄像头:iOS 15+ 通过 `requestMediaCapturePermissionFor` 自动授权 - 麦克风 / 摄像头 WebRTC `getUserMedia()`:自动授权 ### 下载拦截与存储 ```swift XWebViewView( config: XWebViewConfig( url: "https://example.com", downloadDestination: .userPick // 弹出系统 Files 存储面板 ) ) ``` - `.sandbox`:下载完成后弹出 `UIActivityViewController`(分享 / 存储到 Files) - `.userPick`:下载完成后弹出 `UIDocumentPickerViewController`(直接选择存储位置) ### H5 监听下载进度 ```javascript // 下载进度(0–100) window.addEventListener('__xwvDownloadProgress', (e) => { console.log(e.detail.url, e.detail.progress) }) // 下载完成 window.addEventListener('__xwvDownloadDone', (e) => { if (e.detail.success) { console.log('下载成功', e.detail.url) } else { console.error('下载失败', e.detail.error) } }) ``` ### H5 ↔ Native 消息通信 ```javascript // H5 → Native(触发 onMessage 回调) window.webkit.messageHandlers.ReactNativeWebView.postMessage( JSON.stringify({ type: 'login', token: '...' }) ) ``` ```swift // Native → H5 XWebViewBridge.shared.currentController()?.postMessageToWeb( "window.dispatchEvent(new CustomEvent('nativeMsg', { detail: { key: 'value' } }))" ) ``` --- ## 发版 ### SPM(推荐) ```bash git add -A && git commit -m "release: 0.1.0" git tag 0.1.0 git push origin main 0.1.0 ``` Xcode 用户在 Package Dependencies 刷新即可获取新版本。 ### CocoaPods 私有 Spec ```bash # 首次(只需一次) pod repo add xuqm-specs https://xuqinmin.com/xuqinmin12/xuqm-specs.git # 每次发版 pod spec lint XuqmSDK.podspec --allow-warnings pod repo push xuqm-specs XuqmSDK.podspec --allow-warnings ```