25
25
// THE SOFTWARE.
26
26
27
27
import UIKit
28
- import KeychainAccess
29
28
30
29
/**
31
30
The class for using Studyplus.
@@ -78,6 +77,9 @@ final public class Studyplus {
78
77
79
78
private let accessTokenStoreKey : String = " accessToken "
80
79
private let usernameStoreKey : String = " username "
80
+ private var serviceName : String {
81
+ return " Studyplus_iOS_SDK_ \( consumerKey) "
82
+ }
81
83
82
84
/// Opens the login screen by invoking the Studyplus application.
83
85
/// If Studyplus app is not installed, open the Studyplus page in AppStore.
@@ -94,13 +96,7 @@ final public class Studyplus {
94
96
///
95
97
/// Studyplusアプリとの連携を解除します。
96
98
public func logout( ) {
97
-
98
- let chain = keychain ( )
99
- do {
100
- try chain. remove ( usernameStoreKey)
101
- try chain. remove ( accessTokenStoreKey)
102
- } catch {
103
- }
99
+ deleteKey ( )
104
100
}
105
101
106
102
/// Returns to whether or not it is connected with Studyplus application.
@@ -118,12 +114,22 @@ final public class Studyplus {
118
114
///
119
115
/// - Returns: accessToken
120
116
public func accessToken( ) -> String ? {
121
-
122
- do {
123
- return try keychain ( ) . get ( accessTokenStoreKey)
124
- } catch {
117
+ let query = [
118
+ kSecClass: kSecClassGenericPassword,
119
+ kSecAttrService: serviceName,
120
+ kSecAttrSynchronizable: kSecAttrSynchronizableAny,
121
+ kSecMatchLimit: kSecMatchLimitOne,
122
+ kSecReturnData: true ,
123
+ kSecAttrAccount: accessTokenStoreKey
124
+ ] as CFDictionary
125
+
126
+ var item : CFTypeRef ?
127
+ let status = SecItemCopyMatching ( query, & item)
128
+ guard status == errSecSuccess, let data = item as? Data else {
125
129
return nil
126
130
}
131
+
132
+ return String ( data: data, encoding: . utf8)
127
133
}
128
134
129
135
/// Username of Studyplus account. It is set when the auth or login is successful.
@@ -132,12 +138,22 @@ final public class Studyplus {
132
138
///
133
139
/// - Returns: username
134
140
public func username( ) -> String ? {
135
-
136
- do {
137
- return try keychain ( ) . get ( usernameStoreKey)
138
- } catch {
141
+ let query = [
142
+ kSecClass: kSecClassGenericPassword,
143
+ kSecAttrService: serviceName,
144
+ kSecAttrSynchronizable: kSecAttrSynchronizableAny,
145
+ kSecMatchLimit: kSecMatchLimitOne,
146
+ kSecReturnData: true ,
147
+ kSecAttrAccount: usernameStoreKey
148
+ ] as CFDictionary
149
+
150
+ var item : CFTypeRef ?
151
+ let status = SecItemCopyMatching ( query, & item)
152
+ guard status == errSecSuccess, let data = item as? Data else {
139
153
return nil
140
154
}
155
+
156
+ return String ( data: data, encoding: . utf8)
141
157
}
142
158
143
159
/// Posts a new study record to Studyplus.
@@ -148,7 +164,9 @@ final public class Studyplus {
148
164
/// - studyRecord: see StudyplusRecord
149
165
/// - success: callback when success to post the studyRecord
150
166
/// - failure: callback when failure to post the studyRecord
151
- public func post( studyRecord: StudyplusRecord , success: @escaping ( ) -> Void , failure: @escaping ( _ error: StudyplusError ) -> Void ) {
167
+ public func post( studyRecord: StudyplusRecord ,
168
+ success: @escaping ( ) -> Void ,
169
+ failure: @escaping ( _ error: StudyplusError ) -> Void ) {
152
170
153
171
guard StudyplusRecord . durationRange ~= Int ( studyRecord. duration) else {
154
172
failure ( . postRecordFailed)
@@ -204,19 +222,36 @@ final public class Studyplus {
204
222
205
223
switch appDelegateUrl. pathComponents [ 1 ] {
206
224
case " success " :
207
-
208
- let accessToken : String = appDelegateUrl. pathComponents [ 2 ] . trimmingCharacters ( in: . whitespacesAndNewlines)
209
- let username : String = appDelegateUrl. pathComponents [ 3 ] . trimmingCharacters ( in: . whitespacesAndNewlines)
210
-
211
- let chain = keychain ( )
212
- do {
213
- try chain. set ( accessToken, key: accessTokenStoreKey)
214
- try chain. set ( username, key: usernameStoreKey)
225
+ let accessToken : Data = appDelegateUrl
226
+ . pathComponents [ 2 ]
227
+ . trimmingCharacters ( in: . whitespacesAndNewlines)
228
+ . data ( using: . utf8, allowLossyConversion: false ) !
229
+ let username : Data = appDelegateUrl
230
+ . pathComponents [ 3 ]
231
+ . trimmingCharacters ( in: . whitespacesAndNewlines)
232
+ . data ( using: . utf8, allowLossyConversion: false ) !
233
+
234
+ deleteKey ( )
235
+ let statusAccessToken = SecItemAdd ( [
236
+ kSecClass: kSecClassGenericPassword,
237
+ kSecAttrService: serviceName,
238
+ kSecAttrSynchronizable: kSecAttrSynchronizableAny,
239
+ kSecAttrAccount: accessTokenStoreKey,
240
+ kSecValueData: accessToken
241
+ ] as CFDictionary , nil )
242
+ let statusUsername = SecItemAdd ( [
243
+ kSecClass: kSecClassGenericPassword,
244
+ kSecAttrService: serviceName,
245
+ kSecAttrSynchronizable: kSecAttrSynchronizableAny,
246
+ kSecAttrAccount: usernameStoreKey,
247
+ kSecValueData: username
248
+ ] as CFDictionary , nil )
249
+
250
+ if statusAccessToken == noErr && statusUsername == noErr {
215
251
delegate? . studyplusDidSuccessToLogin ( )
216
- } catch {
252
+ } else {
217
253
delegate? . studyplusDidFailToLogin ( error: . unknownReason( " Could not access Keychain. " ) )
218
254
}
219
-
220
255
case " fail " :
221
256
222
257
if let errorCode: Int = Int ( appDelegateUrl. pathComponents [ 2 ] ) {
@@ -231,7 +266,7 @@ final public class Studyplus {
231
266
232
267
default :
233
268
#if DEBUG
234
- print ( " StudyplusSDK: Unknown format: \( appDelegateUrl. absoluteString) " )
269
+ print ( " StudyplusSDK: Unknown format: \( appDelegateUrl. absoluteString) " )
235
270
#endif
236
271
return false
237
272
}
@@ -290,11 +325,6 @@ final public class Studyplus {
290
325
self . consumerSecret = consumerSecret
291
326
}
292
327
293
- private func keychain( ) -> Keychain {
294
- let serviceName : String = " Studyplus_iOS_SDK_ \( consumerKey) "
295
- return Keychain ( service: serviceName)
296
- }
297
-
298
328
private func openStudyplus( command: String ) {
299
329
300
330
guard UIApplication . shared. canOpenURL ( URL ( string: " studyplus:// " ) !) else {
@@ -335,4 +365,12 @@ final public class Studyplus {
335
365
336
366
return true
337
367
}
368
+
369
+ private func deleteKey( ) {
370
+ SecItemDelete ( [
371
+ kSecClass: kSecClassGenericPassword,
372
+ kSecAttrService: serviceName,
373
+ kSecAttrSynchronizable: kSecAttrSynchronizableAny
374
+ ] as CFDictionary )
375
+ }
338
376
}
0 commit comments