import Foundation import Security /// Secure persistence for the session token using the iOS Keychain. /// Never stores the session in UserDefaults or plain files. struct KeychainStore { private let service = "com.proximity.app.session" func save(_ session: Session) { let data = (try? JSONEncoder().encode(session)) ?? Data() let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: "session" ] // Delete existing, then add. SecItemDelete(query as CFDictionary) var add = query add[kSecValueData as String] = data SecItemAdd(add as CFDictionary, nil) } func loadSession() -> Session? { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: "session", kSecReturnData as String: true, kSecMatchLimit as String: kSecMatchLimitOne ] var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) guard status == errSecSuccess, let data = result as? Data else { return nil } return try? JSONDecoder().decode(Session.self, from: data) } func clearSession() { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: "session" ] SecItemDelete(query as CFDictionary) } }