@@ -82,17 +82,44 @@ public class Keychain {
8282
8383 /// :nodoc:
8484 public func set( password: String , for username: String ) throws {
85- removePassword ( for: username)
85+ guard let data = password. data ( using: . utf8) else {
86+ throw KeychainError . add
87+ }
8688
8789 var query = [ String: Any] ( )
8890 setScope ( query: & query)
8991 query [ kSecClass as String ] = kSecClassGenericPassword
9092 query [ kSecAttrAccount as String ] = username
91- query [ kSecAttrAccessible as String ] = kSecAttrAccessibleAfterFirstUnlock
92- query [ kSecValueData as String ] = password. data ( using: . utf8)
9393
94- let status = SecItemAdd ( query as CFDictionary , nil )
95- guard ( status == errSecSuccess) else {
94+ let attributesToUpdate : [ String : Any ] = [
95+ kSecValueData as String : data,
96+ kSecAttrAccessible as String : kSecAttrAccessibleAfterFirstUnlock
97+ ]
98+ let updateStatus = SecItemUpdate ( query as CFDictionary , attributesToUpdate as CFDictionary )
99+
100+ switch updateStatus {
101+ case errSecSuccess:
102+ return
103+ case errSecItemNotFound:
104+ // No existing item — add a fresh one.
105+ query [ kSecAttrAccessible as String ] = kSecAttrAccessibleAfterFirstUnlock
106+ query [ kSecValueData as String ] = data
107+ let addStatus = SecItemAdd ( query as CFDictionary , nil )
108+ switch addStatus {
109+ case errSecSuccess:
110+ return
111+ case errSecDuplicateItem:
112+ // Lost a first-write race — the item now exists, so update it in place.
113+ query. removeValue ( forKey: kSecAttrAccessible as String )
114+ query. removeValue ( forKey: kSecValueData as String )
115+ guard SecItemUpdate ( query as CFDictionary , attributesToUpdate as CFDictionary ) == errSecSuccess
116+ else {
117+ throw KeychainError . add
118+ }
119+ default :
120+ throw KeychainError . add
121+ }
122+ default :
96123 throw KeychainError . add
97124 }
98125 }
0 commit comments