import Foundation import Security enum KeychainStore { private static let service = "xyz.lschaefer.Six-Card-Golf" /// Shared by the app and Messages extension via Keychain Sharing. private static let accessGroup = "6TY4NWX4TF.xyz.lschaefer.Six-Card-Golf" static func string(forKey account: String) -> String? { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account, kSecAttrAccessGroup as String: accessGroup, kSecReturnData as String: true, kSecMatchLimit as String: kSecMatchLimitOne ] var item: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &item) guard status == errSecSuccess, let data = item as? Data, let value = String(data: data, encoding: .utf8) else { return nil } return value } static func set(_ value: String?, forKey account: String) { delete(account) guard let value, let data = value.data(using: .utf8) else { return } let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account, kSecAttrAccessGroup as String: accessGroup, kSecValueData as String: data, kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly ] SecItemAdd(query as CFDictionary, nil) } static func delete(_ account: String) { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: account, kSecAttrAccessGroup as String: accessGroup ] SecItemDelete(query as CFDictionary) } }