KeychainHelper.swift (2183B)
1 import Foundation 2 import Security 3 4 enum KeychainHelper { 5 private static let service = "net.inqk.crossmate" 6 7 static func save(key: String, data: Data) throws { 8 let query: [String: Any] = [ 9 kSecClass as String: kSecClassGenericPassword, 10 kSecAttrService as String: service, 11 kSecAttrAccount as String: key, 12 ] 13 14 let update: [String: Any] = [ 15 kSecValueData as String: data, 16 kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, 17 ] 18 let updateStatus = SecItemUpdate(query as CFDictionary, update as CFDictionary) 19 if updateStatus == errSecSuccess { return } 20 guard updateStatus == errSecItemNotFound else { 21 throw KeychainError.unhandledError(status: updateStatus) 22 } 23 24 var attributes = query 25 for (key, value) in update { 26 attributes[key] = value 27 } 28 29 let status = SecItemAdd(attributes as CFDictionary, nil) 30 guard status == errSecSuccess else { 31 throw KeychainError.unhandledError(status: status) 32 } 33 } 34 35 static func load(key: String) -> Data? { 36 loadWithStatus(key: key).data 37 } 38 39 static func loadWithStatus(key: String) -> (data: Data?, status: OSStatus) { 40 let query: [String: Any] = [ 41 kSecClass as String: kSecClassGenericPassword, 42 kSecAttrService as String: service, 43 kSecAttrAccount as String: key, 44 kSecReturnData as String: true, 45 kSecMatchLimit as String: kSecMatchLimitOne, 46 ] 47 48 var result: AnyObject? 49 let status = SecItemCopyMatching(query as CFDictionary, &result) 50 guard status == errSecSuccess else { return (nil, status) } 51 return (result as? Data, status) 52 } 53 54 static func delete(key: String) { 55 let query: [String: Any] = [ 56 kSecClass as String: kSecClassGenericPassword, 57 kSecAttrService as String: service, 58 kSecAttrAccount as String: key, 59 ] 60 SecItemDelete(query as CFDictionary) 61 } 62 } 63 64 enum KeychainError: Error { 65 case unhandledError(status: OSStatus) 66 }