crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

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 }