如何检测iOS APNs推送Token变化并仅在变更时更新服务器
无需缓存检测APNs Token变化并更新服务器的方案
核心思路
苹果禁止的是内存缓存或临时存储设备Token(避免Token变更后仍使用旧值),但可以用Keychain持久化存储之前的Token用于对比——Keychain是系统级安全存储,不属于苹果定义的“缓存”范畴,且能在应用重启、升级后保留数据,完美满足对比需求。
具体实现步骤
1. 规范Token字符串格式
在didRegisterForRemoteNotificationsWithDeviceToken回调中,将苹果返回的Data类型Token转换为统一格式的字符串,避免因格式差异导致误判:func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() // 后续对比、上传逻辑 }2. 封装Keychain读写工具
写一个轻量的Keychain工具类,专门用于存储和读取设备Token,确保只有当前应用可访问:struct KeychainManager { private static let tokenKey = "APNsDeviceToken" static func saveToken(_ token: String) -> Bool { let data = token.data(using: .utf8)! let query: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrAccount: tokenKey, kSecValueData: data, kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly ] SecItemDelete(query as CFDictionary) return SecItemAdd(query as CFDictionary, nil) == errSecSuccess } static func getToken() -> String? { let query: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrAccount: tokenKey, kSecReturnData: kCFBooleanTrue!, kSecMatchLimit: kSecMatchLimitOne ] var data: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &data) guard status == errSecSuccess, let tokenData = data as? Data else { return nil } return String(data: tokenData, encoding: .utf8) } }3. 对比Token并触发更新
在拿到新Token后,读取Keychain中的旧Token,对比后决定是否请求服务器:func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let newToken = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() let oldToken = KeychainManager.getToken() guard newToken != oldToken else { // Token未变化,无需更新 return } // 调用服务器接口更新Token updateTokenOnServer(newToken) { success in if success { // 仅在服务器更新成功后,更新Keychain中的Token _ = KeychainManager.saveToken(newToken) } } }
关键注意事项
- 服务器端做幂等处理:即使因极端情况(如Keychain读取失败)发送了重复Token,服务器要忽略重复请求,避免数据异常。
- Keychain权限设置:使用
kSecAttrAccessibleWhenUnlockedThisDeviceOnly确保Token仅在设备解锁时可访问,且不会同步到iCloud(若需跨设备同步可调整权限,但不推荐)。 - 处理Token获取失败场景:在
didFailToRegisterForRemoteNotificationsWithError中记录错误,避免遗漏注册流程。
内容的提问来源于stack exchange,提问作者Alisher Baigazin
相关产品推荐
相关产品推荐

