XuqmGroup-iOSSDK/Sources/XuqmSDK/Core/XuqmSDK.swift
XuqmGroup e7067d03cb feat(sdk): 更新 SDK 设计文档和 API 重构
- 添加 expiresAt 和 refreshUserSig 参数支持自动续签
- 修改 PushSDK 初始化方式,自动完成设备注册和厂商初始化
- 调整过期续签策略,从提前 15 分钟改为提前 5 分钟触发
- 重构 RN SDK 文档结构,简化安装和使用方式
- 更新统一登录流程,支持 profile 信息传递
- 添加 IM 数据库自动隔离功能
- 修复 Android 群消息聚合问题
- 补充自动化测试验证和错误处理机制
2026-05-01 21:27:39 +08:00

123 行
3.6 KiB
Swift

import Foundation
@MainActor
public final class XuqmSDK: NSObject {
public static let shared = XuqmSDK()
private(set) var config: SDKConfig?
private(set) var tokenStore: TokenStore?
public private(set) var currentUserId: String?
public var onUserSigExpired: (() -> Void)?
private var userSig: String?
private var userSigTimer: Timer?
private var cachedDeviceToken: String?
private override init() {
super.init()
}
public func initialize(config: SDKConfig) {
self.config = config
self.tokenStore = TokenStore()
ApiClient.shared.configure(with: config)
}
public func requireConfig() -> SDKConfig {
guard let config else {
fatalError("XuqmSDK not initialized. Call XuqmSDK.shared.initialize() first.")
}
return config
}
public func login(userId: String, userSig: String) async {
self.currentUserId = userId
self.userSig = userSig
startUserSigExpirationTimer(userSig: userSig)
do {
try await ImSDK.shared.loginWithUserSig(userId, userSig)
} catch {
// IM login failed; silently ignored per facade pattern
}
if let cachedDeviceToken {
do {
try await PushSDK.shared.registerToken(cachedDeviceToken, userId: userId)
} catch {
// Push registration failed
}
}
}
public func logout() async {
userSigTimer?.invalidate()
userSigTimer = nil
ImSDK.shared.disconnect()
if let userId = currentUserId {
do {
try await PushSDK.shared.unregisterToken(userId: userId)
} catch {
// Push unregistration failed
}
}
tokenStore?.clear()
currentUserId = nil
userSig = nil
cachedDeviceToken = nil
}
public func registerDeviceToken(_ deviceToken: Data) {
let token = deviceToken.map { String(format: "%02x", $0) }.joined()
self.cachedDeviceToken = token
Task { @MainActor in
if let userId = self.currentUserId {
try? await PushSDK.shared.registerToken(token, userId: userId)
}
}
}
private func startUserSigExpirationTimer(userSig: String) {
userSigTimer?.invalidate()
guard let expDate = extractExpirationDate(from: userSig) else { return }
let interval = expDate.timeIntervalSinceNow - 300 // 5 minutes before expiry
guard interval > 0 else {
onUserSigExpired?()
return
}
userSigTimer = Timer.scheduledTimer(
timeInterval: interval,
target: self,
selector: #selector(userSigDidExpire),
userInfo: nil,
repeats: false
)
}
@objc private func userSigDidExpire() {
onUserSigExpired?()
}
private func extractExpirationDate(from userSig: String) -> Date? {
let parts = userSig.split(separator: ".")
guard parts.count >= 2 else { return nil }
var base64 = String(parts[1])
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let padding = 4 - base64.count % 4
if padding != 4 {
base64 += String(repeating: "=", count: padding)
}
guard let data = Data(base64Encoded: base64),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let exp = json["exp"] as? TimeInterval else {
return nil
}
return Date(timeIntervalSince1970: exp)
}
}