import Foundation import Security enum HoloLakeKeychain { private static let service = "com.guanghulab.hololake.mobile-sync" private static let account = "HLP-IOS-CLIENT-001" static func load() throws -> StoredSession? { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account, kSecReturnData as String: true, kSecMatchLimit as String: kSecMatchLimitOne ] var result: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &result) if status == errSecItemNotFound { return nil } guard status == errSecSuccess, let data = result as? Data else { throw HoloLakeMobileError.keychain(status) } return try JSONDecoder().decode(StoredSession.self, from: data) } static func save(_ session: StoredSession) throws { let data = try JSONEncoder().encode(session) let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account ] let attributes: [String: Any] = [ kSecValueData as String: data, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly ] let update = SecItemUpdate(query as CFDictionary, attributes as CFDictionary) if update == errSecItemNotFound { var insertion = query insertion.merge(attributes) { _, new in new } let status = SecItemAdd(insertion as CFDictionary, nil) guard status == errSecSuccess else { throw HoloLakeMobileError.keychain(status) } } else if update != errSecSuccess { throw HoloLakeMobileError.keychain(update) } } static func remove() throws { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account ] let status = SecItemDelete(query as CFDictionary) guard status == errSecSuccess || status == errSecItemNotFound else { throw HoloLakeMobileError.keychain(status) } } }