如何解决keychain-swift库关键问题,保障deviceId永久唯一?
解决Keychain读取返回nil导致Device ID重复生成的问题
我基于keychain-swift库实现了Keychain编解码类和Devicer类,原本期望设备上的应用生命周期内保持唯一的deviceId,但现在每隔几天decode方法会意外返回nil,导致新的deviceId被重新生成并存储。以下是我的实现代码,以及可行的解决方案:
现有代码
Keychain类
class Keychain: Keychainable { private let keychain: KeychainSwift init(keychain: KeychainSwift) { self.keychain = keychain } func encode(value: String, forKey key: String) { keychain.set(value, forKey: key) } func decode(forKey key: String) -> String? { var returnValue: String? for _ in 1...3 { if let value = keychain.get(key) { returnValue = value } // ❌ 当前无效的临时解决方法 } return returnValue } func delete(forKey key: String) { keychain.delete(key) } }
Devicer类
class Devicer: Devicable { private let device: UIDevice private let keychain: Keychainable var deviceIdentifier: String { guard let identifier = keychain.decode(forKey: "did") else { let uuid = device.identifierForVendor!.uuidString keychain.encode(value: uuid, forKey: "did") return uuid } return identifier } }
解决方案
1. 优化重试逻辑,确保读取成功后立即返回
当前的重试循环没有正确处理成功后的返回,且缺少失败后的短暂延迟,可能导致重复读取无效。修改decode方法:
func decode(forKey key: String) -> String? { for attempt in 1...3 { if let value = keychain.get(key) { return value } // 非最后一次尝试时,短暂等待后重试 if attempt < 3 { Thread.sleep(forTimeInterval: 0.1) } } return nil }
2. 配置KeychainSwift的持久化访问策略
默认配置可能存在数据被系统清理或同步异常的问题,初始化时明确设置访问控制策略:
// 初始化KeychainSwift时使用以下配置 let keychain = KeychainSwift( synchronizable: false, // 关闭iCloud同步,避免跨设备数据干扰 accessControl: KeychainSwiftAccessControl.afterFirstUnlockThisDeviceOnly // 仅本设备可访问,且设备解锁后可用 )
3. 添加写入后的验证逻辑
生成新UUID并写入Keychain后,立即读取验证是否成功,避免写入失败导致后续读取nil:
var deviceIdentifier: String { guard let identifier = keychain.decode(forKey: "did") else { let uuid = device.identifierForVendor!.uuidString keychain.encode(value: uuid, forKey: "did") // 验证写入结果 if let savedId = keychain.decode(forKey: "did") { return savedId } else { assertionFailure("Keychain写入device ID失败") // 写入失败时,临时返回生成的UUID,同时建议添加日志上报 return uuid } } return identifier }
4. 处理并发访问的竞态条件
如果多个线程同时访问deviceIdentifier,可能导致Keychain操作冲突,添加线程锁保证原子性:
class Devicer: Devicable { private let device: UIDevice private let keychain: Keychainable private let lock = NSLock() // 线程锁 var deviceIdentifier: String { lock.lock() defer { lock.unlock() } // 确保锁最终会释放 guard let identifier = keychain.decode(forKey: "did") else { let uuid = device.identifierForVendor!.uuidString keychain.encode(value: uuid, forKey: "did") if let savedId = keychain.decode(forKey: "did") { return savedId } else { assertionFailure("Keychain写入device ID失败") return uuid } } return identifier } }
5. 排查系统级潜在问题
- 检查应用签名配置,确保Keychain访问权限正常;
- 避免在后台频繁调用Keychain操作,iOS可能限制后台的Keychain访问;
- 确认没有其他代码或第三方库修改了
did这个Keychain键值。
内容的提问来源于stack exchange,提问作者kunass2
相关产品推荐
相关产品推荐

