| import Foundation |
| import Security |
|
|
| enum KeychainStore { |
| static func loadString(service: String, account: String) -> String? { |
| 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 item: CFTypeRef? |
| let status = SecItemCopyMatching(query as CFDictionary, &item) |
| guard status == errSecSuccess, let data = item as? Data else { return nil } |
| return String(data: data, encoding: .utf8) |
| } |
|
|
| static func saveString(_ value: String, service: String, account: String) -> Bool { |
| let data = Data(value.utf8) |
| let query: [String: Any] = [ |
| kSecClass as String: kSecClassGenericPassword, |
| kSecAttrService as String: service, |
| kSecAttrAccount as String: account, |
| ] |
|
|
| let update: [String: Any] = [kSecValueData as String: data] |
| let status = SecItemUpdate(query as CFDictionary, update as CFDictionary) |
| if status == errSecSuccess { return true } |
| if status != errSecItemNotFound { return false } |
|
|
| var insert = query |
| insert[kSecValueData as String] = data |
| insert[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly |
| return SecItemAdd(insert as CFDictionary, nil) == errSecSuccess |
| } |
|
|
| static func delete(service: String, account: String) -> Bool { |
| let query: [String: Any] = [ |
| kSecClass as String: kSecClassGenericPassword, |
| kSecAttrService as String: service, |
| kSecAttrAccount as String: account, |
| ] |
| let status = SecItemDelete(query as CFDictionary) |
| return status == errSecSuccess || status == errSecItemNotFound |
| } |
| } |
|
|