@@ -24,9 +24,10 @@ public class NativeDBManager: NSObject, GnoGnonativeNativeDBProtocol {
2424
2525 // MARK: - Public Interface Implementation
2626
27- public func get( _ key: Data ? ) -> Data ? {
28- guard let key = key else { return nil }
29-
27+ public func get( _ key: Data ? ) throws -> Data {
28+ guard let key = key, !key. isEmpty else {
29+ throw NativeDBError . invalidArgument ( description: " Key must not be nil or empty. " )
30+ }
3031 let account = keyToAccount ( key)
3132
3233 var query : [ String : Any ] = [
@@ -44,21 +45,25 @@ public class NativeDBManager: NSObject, GnoGnonativeNativeDBProtocol {
4445 var result : AnyObject ?
4546 let status = SecItemCopyMatching ( query as CFDictionary , & result)
4647
47- guard status == errSecSuccess else {
48- return nil
48+ switch status {
49+ case errSecSuccess:
50+ return ( result as? Data ) ?? Data ( )
51+ case errSecItemNotFound:
52+ // This is not an error; it's the expected result for a missing key.
53+ return Data ( )
54+ default :
55+ throw NativeDBError . keychainError ( status: status, message: " Failed to get item. " )
4956 }
50-
51- return result as? Data
5257 }
5358
54- public func delete( _ key: Data ? ) {
55- DispatchQueue . global ( qos: . utility) . async {
56- self . deleteSync ( key)
57- }
59+ public func delete( _ key: Data ? ) throws {
60+ try self . deleteSync ( key)
5861 }
5962
60- public func deleteSync( _ key: Data ? ) {
61- guard let key = key else { return }
63+ public func deleteSync( _ key: Data ? ) throws {
64+ guard let key = key, !key. isEmpty else {
65+ throw NativeDBError . invalidArgument ( description: " Key must not be nil or empty. " )
66+ }
6267
6368 let account = keyToAccount ( key)
6469
@@ -72,38 +77,63 @@ public class NativeDBManager: NSObject, GnoGnonativeNativeDBProtocol {
7277 query [ kSecAttrAccessGroup as String ] = accessGroup
7378 }
7479
75- SecItemDelete ( query as CFDictionary )
80+ let status = SecItemDelete ( query as CFDictionary )
81+
82+ // Deleting a non-existent item is not considered an error.
83+ if status != errSecSuccess && status != errSecItemNotFound {
84+ throw NativeDBError . keychainError ( status: status, message: " Failed to delete item. " )
85+ }
7686 }
7787
78- public func has( _ key: Data ? ) -> Bool {
79- guard let key = key else { return false }
88+ public func has( _ key: Data ? , ret0_ returnPointer: UnsafeMutablePointer < ObjCBool > ? ) throws {
89+ // 1. Ensure the return pointer provided by the caller is valid.
90+ guard let returnPointer = returnPointer else {
91+ throw NativeDBError . invalidArgument ( description: " Return pointer must not be nil. " )
92+ }
93+
94+ // 2. Validate the input key.
95+ guard let key = key, !key. isEmpty else {
96+ throw NativeDBError . invalidArgument ( description: " Key must not be nil or empty. " )
97+ }
8098
8199 let account = keyToAccount ( key)
82100
83101 var query : [ String : Any ] = [
84102 kSecClass as String : kSecClassGenericPassword,
85103 kSecAttrService as String : service,
86- kSecAttrAccount as String : account,
87- kSecReturnData as String : false ,
88- kSecMatchLimit as String : kSecMatchLimitOne
104+ kSecAttrAccount as String : account
89105 ]
90-
91106 if let accessGroup = accessGroup {
92107 query [ kSecAttrAccessGroup as String ] = accessGroup
93108 }
94109
95110 let status = SecItemCopyMatching ( query as CFDictionary , nil )
96- return status == errSecSuccess
111+
112+ // 3. Handle the result from the Keychain.
113+ switch status {
114+ case errSecSuccess:
115+ // Key exists. Write `true` to the pointer's memory location.
116+ returnPointer. pointee = ObjCBool ( true )
117+ case errSecItemNotFound:
118+ // Key does not exist. Write `false` to the pointer's memory location.
119+ returnPointer. pointee = ObjCBool ( false )
120+ default :
121+ // A real keychain error occurred. Throw an error.
122+ throw NativeDBError . keychainError ( status: status, message: " Failed to check for item existence. " )
123+ }
97124 }
98125
99- public func set( _ key: Data ? , p1 value: Data ? ) {
100- DispatchQueue . global ( qos: . utility) . async {
101- self . setSync ( key, p1: value)
102- }
126+ public func set( _ key: Data ? , value: Data ? ) throws {
127+ try self . setSync ( key, value: value)
103128 }
104129
105- public func setSync( _ key: Data ? , p1 value: Data ? ) {
106- guard let key = key, let value = value else { return }
130+ public func setSync( _ key: Data ? , value: Data ? ) throws {
131+ guard let key = key, !key. isEmpty else {
132+ throw NativeDBError . invalidArgument ( description: " Key must not be nil or empty. " )
133+ }
134+ guard let value = value else {
135+ throw NativeDBError . invalidArgument ( description: " Value must not be nil. " )
136+ }
107137
108138 let account = keyToAccount ( key)
109139
@@ -124,13 +154,23 @@ public class NativeDBManager: NSObject, GnoGnonativeNativeDBProtocol {
124154
125155 let updateStatus = SecItemUpdate ( query as CFDictionary , attributes as CFDictionary )
126156
127- if updateStatus == errSecItemNotFound {
128- // Item doesn't exist, create new one
157+ switch updateStatus {
158+ case errSecSuccess:
159+ // Update was successful.
160+ return
161+ case errSecItemNotFound:
162+ // Item doesn't exist, so add it.
129163 var newItem = query
130164 newItem [ kSecValueData as String ] = value
131165 newItem [ kSecAttrAccessible as String ] = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
132166
133- SecItemAdd ( newItem as CFDictionary , nil )
167+ let addStatus = SecItemAdd ( newItem as CFDictionary , nil )
168+ if addStatus != errSecSuccess {
169+ throw NativeDBError . keychainError ( status: addStatus, message: " Failed to add new item. " )
170+ }
171+ default :
172+ // Another error occurred during update.
173+ throw NativeDBError . keychainError ( status: updateStatus, message: " Failed to update item. " )
134174 }
135175 }
136176
@@ -260,3 +300,18 @@ public class NativeDBManager: NSObject, GnoGnonativeNativeDBProtocol {
260300 return true
261301 }
262302}
303+
304+ public enum NativeDBError : Error , LocalizedError {
305+ case invalidArgument( description: String )
306+ case keychainError( status: OSStatus , message: String )
307+
308+ public var errorDescription : String ? {
309+ switch self {
310+ case . invalidArgument( let description) :
311+ return " Invalid Argument: \( description) "
312+ case . keychainError( let status, let message) :
313+ let statusMessage = SecCopyErrorMessageString ( status, nil ) as String ? ?? " Unknown error "
314+ return " Keychain Error: \( message) (status: \( status) , reason: \( statusMessage) ) "
315+ }
316+ }
317+ }
0 commit comments