KeychainStorage.swift (2305B)
1 // 2 // KeychainStorage.swift 3 // damus 4 // 5 // Created by Bryan Montz on 5/2/23. 6 // 7 8 import Foundation 9 import Security 10 11 @propertyWrapper struct KeychainStorage { 12 let account: String 13 private let service = "damus" 14 15 var wrappedValue: String? { 16 get { 17 let query = [ 18 kSecAttrService: service, 19 kSecAttrAccount: account, 20 kSecClass: kSecClassGenericPassword, 21 kSecReturnData: true, 22 kSecMatchLimit: kSecMatchLimitOne 23 ] as [CFString: Any] as CFDictionary 24 25 var result: AnyObject? 26 let status = SecItemCopyMatching(query, &result) 27 28 if status == errSecSuccess, let data = result as? Data { 29 return String(data: data, encoding: .utf8) 30 } else { 31 return nil 32 } 33 } 34 set { 35 if let newValue { 36 let query = [ 37 kSecAttrService: service, 38 kSecAttrAccount: account, 39 kSecClass: kSecClassGenericPassword, 40 kSecValueData: newValue.data(using: .utf8) as Any 41 ] as [CFString: Any] as CFDictionary 42 43 var status = SecItemAdd(query, nil) 44 45 if status == errSecDuplicateItem { 46 let query = [ 47 kSecAttrService: service, 48 kSecAttrAccount: account, 49 kSecClass: kSecClassGenericPassword 50 ] as [CFString: Any] as CFDictionary 51 52 let updates = [ 53 kSecValueData: newValue.data(using: .utf8) as Any 54 ] as CFDictionary 55 56 status = SecItemUpdate(query, updates) 57 } 58 } else { 59 let query = [ 60 kSecAttrService: service, 61 kSecAttrAccount: account, 62 kSecClass: kSecClassGenericPassword 63 ] as [CFString: Any] as CFDictionary 64 65 _ = SecItemDelete(query) 66 } 67 } 68 } 69 70 init(account: String) { 71 self.account = account 72 } 73 }