@@ -59,31 +59,134 @@ class IterableKeychain {
59
59
}
60
60
}
61
61
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
64
81
82
+ // Assign default protection - Protect the keychain entry so it's only valid when the device is unlocked
83
+ keychainQueryDictionary [ SecAttrAccessible] = SecAttrAccessibleWhenUnlocked
65
84
85
+ let status : OSStatus = SecItemAdd ( keychainQueryDictionary as CFDictionary , nil )
66
86
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
68
110
}
69
111
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
74
123
}
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 )
75
133
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
81
138
}
82
139
}
83
140
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
86
161
}
87
162
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
89
191
}
192
+
0 commit comments