import Foundation import Security struct AuthCredentials { let userId: Int let token: String } actor AuthStorage { static let shared = AuthStorage() private let serviceName = "com.payfrit.food" private let tokenKey = "userToken" private let userIdKey = "userId" // MARK: - Public Interface func saveCredentials(_ credentials: AuthCredentials) { saveToKeychain(key: tokenKey, value: credentials.token) saveToKeychain(key: userIdKey, value: String(credentials.userId)) UserDefaults.standard.set(credentials.userId, forKey: userIdKey) } func loadCredentials() -> AuthCredentials? { guard let token = loadFromKeychain(key: tokenKey), let userIdString = loadFromKeychain(key: userIdKey), let userId = Int(userIdString) else { return nil } return AuthCredentials(userId: userId, token: token) } func clearAll() { deleteFromKeychain(key: tokenKey) deleteFromKeychain(key: userIdKey) UserDefaults.standard.removeObject(forKey: userIdKey) } // MARK: - Keychain Operations private func saveToKeychain(key: String, value: String) { let data = value.data(using: .utf8)! // Delete existing item first deleteFromKeychain(key: key) let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: serviceName, kSecAttrAccount as String: key, kSecValueData as String: data, kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock ] SecItemAdd(query as CFDictionary, nil) } private func loadFromKeychain(key: String) -> String? { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: serviceName, kSecAttrAccount as String: key, 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, let value = String(data: data, encoding: .utf8) else { return nil } return value } private func deleteFromKeychain(key: String) { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: serviceName, kSecAttrAccount as String: key ] SecItemDelete(query as CFDictionary) } }