@@ -59,31 +59,134 @@ class IterableKeychain {
5959 }
6060 }
6161
62- func getLastPushPayload( currentDate: Date ) -> [ AnyHashable : Any ] ? {
63- // let data = wrapper.data(forKey: Const.Keychain.Key.lastPushPayload)
62+ init ( wrapper: KeychainWrapper = KeychainWrapper ( ) ) {
63+ self . wrapper = wrapper
64+ }
65+
66+ private let wrapper : KeychainWrapper
67+ }
68+
69+ /// Basic wrapper for keychain
70+ /// This should have no dependency on Iterable classes
71+ class KeychainWrapper {
72+ init ( serviceName: String = Const . Keychain. serviceName) {
73+ self . serviceName = serviceName
74+ }
75+
76+ @discardableResult
77+ func set( _ value: Data , forKey key: String ) -> Bool {
78+ var keychainQueryDictionary : [ String : Any ] = setupKeychainQueryDictionary ( forKey: key)
79+
80+ keychainQueryDictionary [ SecValueData] = value
6481
82+ // Assign default protection - Protect the keychain entry so it's only valid when the device is unlocked
83+ keychainQueryDictionary [ SecAttrAccessible] = SecAttrAccessibleWhenUnlocked
6584
85+ let status : OSStatus = SecItemAdd ( keychainQueryDictionary as CFDictionary , nil )
6686
67- return nil
87+ if status == errSecSuccess {
88+ return true
89+ } else if status == errSecDuplicateItem {
90+ return update ( value, forKey: key)
91+ } else {
92+ return false
93+ }
94+ }
95+
96+ func data( forKey key: String ) -> Data ? {
97+ var keychainQueryDictionary = setupKeychainQueryDictionary ( forKey: key)
98+
99+ // Limit search results to one
100+ keychainQueryDictionary [ SecMatchLimit] = SecMatchLimitOne
101+
102+ // Specify we want Data/CFData returned
103+ keychainQueryDictionary [ SecReturnData] = CFBooleanTrue
104+
105+ // Search
106+ var result : AnyObject ?
107+ let status = SecItemCopyMatching ( keychainQueryDictionary as CFDictionary , & result)
108+
109+ return status == noErr ? result as? Data : nil
68110 }
69111
70- func setLastPushPayload( _ payload: [ AnyHashable : Any ] ? , withExpiration expiration: Date ? ) {
71- guard let value = payload? . jsonValue, JSONSerialization . isValidJSONObject ( value) else {
72- wrapper. removeValue ( forKey: Const . Keychain. Key. lastPushPayload)
73- return
112+ @discardableResult
113+ func removeValue( forKey key: String ) -> Bool {
114+ let keychainQueryDictionary : [ String : Any ] = setupKeychainQueryDictionary ( forKey: key)
115+
116+ // Delete
117+ let status : OSStatus = SecItemDelete ( keychainQueryDictionary as CFDictionary )
118+
119+ if status == errSecSuccess {
120+ return true
121+ } else {
122+ return false
74123 }
124+ }
125+
126+ @discardableResult
127+ func removeAll( ) -> Bool {
128+ var keychainQueryDictionary : [ String : Any ] = [ SecClass: SecClassGenericPassword]
129+
130+ keychainQueryDictionary [ SecAttrService] = serviceName
131+
132+ let status : OSStatus = SecItemDelete ( keychainQueryDictionary as CFDictionary )
75133
76- do {
77- let data = try JSONSerialization . data ( withJSONObject: value, options: [ ] )
78- wrapper. set ( data, forKey: Const . Keychain. Key. lastPushPayload)
79- } catch {
80- wrapper. removeValue ( forKey: Const . Keychain. Key. lastPushPayload)
134+ if status == errSecSuccess {
135+ return true
136+ } else {
137+ return false
81138 }
82139 }
83140
84- init ( wrapper: KeychainWrapper = KeychainWrapper ( ) ) {
85- self . wrapper = wrapper
141+
142+ private let serviceName : String
143+
144+ private func setupKeychainQueryDictionary( forKey key: String ) -> [ String : Any ] {
145+ // Setup default access as generic password (rather than a certificate, internet password, etc)
146+ var keychainQueryDictionary : [ String : Any ] = [ SecClass: SecClassGenericPassword]
147+
148+ // Uniquely identify this keychain accessor
149+ keychainQueryDictionary [ SecAttrService] = serviceName
150+
151+ // Uniquely identify the account who will be accessing the keychain
152+ let encodedIdentifier : Data ? = key. data ( using: . utf8)
153+
154+ keychainQueryDictionary [ SecAttrGeneric] = encodedIdentifier
155+
156+ keychainQueryDictionary [ SecAttrAccount] = encodedIdentifier
157+
158+ keychainQueryDictionary [ SecAttrSynchronizable] = CFBooleanFalse
159+
160+ return keychainQueryDictionary
86161 }
87162
88- private let wrapper : KeychainWrapper
163+ private func update( _ value: Data , forKey key: String ) -> Bool {
164+ let keychainQueryDictionary : [ String : Any ] = setupKeychainQueryDictionary ( forKey: key)
165+ let updateDictionary = [ SecValueData: value]
166+
167+ // Update
168+ let status : OSStatus = SecItemUpdate ( keychainQueryDictionary as CFDictionary , updateDictionary as CFDictionary )
169+
170+ if status == errSecSuccess {
171+ return true
172+ } else {
173+ return false
174+ }
175+ }
176+
177+ private let SecValueData = kSecValueData as String
178+ private let SecAttrAccessible : String = kSecAttrAccessible as String
179+ private let SecAttrAccessibleWhenUnlocked = kSecAttrAccessibleWhenUnlocked
180+ private let SecClass : String = kSecClass as String
181+ private let SecClassGenericPassword = kSecClassGenericPassword
182+ private let SecAttrService : String = kSecAttrService as String
183+ private let SecAttrGeneric : String = kSecAttrGeneric as String
184+ private let SecAttrAccount : String = kSecAttrAccount as String
185+ private let SecAttrSynchronizable : String = kSecAttrSynchronizable as String
186+ private let CFBooleanTrue = kCFBooleanTrue
187+ private let CFBooleanFalse = kCFBooleanFalse
188+ private let SecMatchLimit : String = kSecMatchLimit as String
189+ private let SecMatchLimitOne = kSecMatchLimitOne
190+ private let SecReturnData : String = kSecReturnData as String
89191}
192+
0 commit comments