Skip to content
This repository was archived by the owner on Apr 7, 2021. It is now read-only.

Commit 721972d

Browse files
authored
Merge pull request #28 from studyplus/refactor
Refactor delegate and internal classes
2 parents 5c8b415 + 97c35ff commit 721972d

File tree

7 files changed

+189
-129
lines changed

7 files changed

+189
-129
lines changed

Examples/Demo/ViewController.swift

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// The MIT License (MIT)
66
//
7-
// Copyright (c) 2017 Studyplus inc.
7+
// Copyright (c) 2021 Studyplus inc.
88
//
99
// Permission is hereby granted, free of charge, to any person obtaining a copy
1010
// of this software and associated documentation files (the "Software"), to deal
@@ -27,7 +27,7 @@
2727
import UIKit
2828
import StudyplusSDK
2929

30-
class ViewController: UIViewController, StudyplusLoginDelegate {
30+
class ViewController: UIViewController {
3131

3232
@IBOutlet weak var isConnectedLabel: UILabel!
3333
@IBOutlet weak var resultLabel: UILabel!
@@ -39,14 +39,6 @@ class ViewController: UIViewController, StudyplusLoginDelegate {
3939
Studyplus.shared.delegate = self
4040
}
4141

42-
// MARK: - Label
43-
44-
private func updateIsConnected() {
45-
isConnectedLabel.text = Studyplus.shared.isConnected() ? "True" : "False"
46-
}
47-
48-
// MARK: - Button Action
49-
5042
@IBAction func loginButton(_ sender: UIButton) {
5143
Studyplus.shared.login()
5244
}
@@ -81,29 +73,25 @@ class ViewController: UIViewController, StudyplusLoginDelegate {
8173
})
8274
}
8375

84-
// MARK: - Picker
85-
8676
@IBAction func studyRecordDurationPicker(_ sender: UIDatePicker) {
8777
duration = sender.countDownDuration
8878
}
8979

90-
// MARK: - StudyplusLoginDelegate
80+
private func updateIsConnected() {
81+
isConnectedLabel.text = Studyplus.shared.isConnected() ? "True" : "False"
82+
}
83+
}
9184

92-
func studyplusDidSuccessToLogin() {
85+
extension ViewController: StudyplusLoginDelegate {
86+
func studyplusLoginSuccess() {
9387
print("-- Called studyplusDidSuccessToLogin --")
9488
resultLabel.text = "Login succeeded"
9589
updateIsConnected()
9690
}
9791

98-
func studyplusDidFailToLogin(error: StudyplusLoginError) {
92+
func studyplusLoginFail(error: StudyplusLoginError) {
9993
print("-- Called studyplusDidFailToLogin --")
10094
resultLabel.text = "Error Code: \(error)"
10195
updateIsConnected()
10296
}
103-
104-
func studyplusDidCancelToLogin() {
105-
print("-- Called studyplusDidCancelToLogin --")
106-
resultLabel.text = "Login canceled"
107-
updateIsConnected()
108-
}
10997
}

Lib/StudyplusSDK.xcodeproj/project.pbxproj

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
183FDAAB1E75148D0085589F /* Studyplus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183FDAAA1E75148D0085589F /* Studyplus.swift */; };
1414
183FDAAD1E753E430085589F /* StudyplusLoginDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183FDAAC1E753E430085589F /* StudyplusLoginDelegate.swift */; };
1515
183FDAAF1E7544240085589F /* StudyplusError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183FDAAE1E7544240085589F /* StudyplusError.swift */; };
16-
183FDAB11E754DCB0085589F /* StudyplusAPIRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183FDAB01E754DCB0085589F /* StudyplusAPIRequest.swift */; };
16+
183FDAB11E754DCB0085589F /* StudyplusAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183FDAB01E754DCB0085589F /* StudyplusAPI.swift */; };
1717
183FDAB31E7561B90085589F /* StudyplusRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183FDAB21E7561B90085589F /* StudyplusRecord.swift */; };
18+
65E3912925F8A74E00490547 /* StudyplusKeychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E3912825F8A74E00490547 /* StudyplusKeychain.swift */; };
1819
/* End PBXBuildFile section */
1920

2021
/* Begin PBXContainerItemProxy section */
@@ -37,8 +38,9 @@
3738
183FDAAA1E75148D0085589F /* Studyplus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Studyplus.swift; sourceTree = "<group>"; };
3839
183FDAAC1E753E430085589F /* StudyplusLoginDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StudyplusLoginDelegate.swift; sourceTree = "<group>"; };
3940
183FDAAE1E7544240085589F /* StudyplusError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StudyplusError.swift; sourceTree = "<group>"; };
40-
183FDAB01E754DCB0085589F /* StudyplusAPIRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StudyplusAPIRequest.swift; sourceTree = "<group>"; };
41+
183FDAB01E754DCB0085589F /* StudyplusAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StudyplusAPI.swift; sourceTree = "<group>"; };
4142
183FDAB21E7561B90085589F /* StudyplusRecord.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StudyplusRecord.swift; sourceTree = "<group>"; };
43+
65E3912825F8A74E00490547 /* StudyplusKeychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyplusKeychain.swift; sourceTree = "<group>"; };
4244
/* End PBXFileReference section */
4345

4446
/* Begin PBXFrameworksBuildPhase section */
@@ -82,8 +84,8 @@
8284
isa = PBXGroup;
8385
children = (
8486
183FDA6B1E7510920085589F /* Info.plist */,
87+
65E3911625F889A700490547 /* internal */,
8588
183FDAAA1E75148D0085589F /* Studyplus.swift */,
86-
183FDAB01E754DCB0085589F /* StudyplusAPIRequest.swift */,
8789
183FDAAE1E7544240085589F /* StudyplusError.swift */,
8890
183FDAAC1E753E430085589F /* StudyplusLoginDelegate.swift */,
8991
183FDAB21E7561B90085589F /* StudyplusRecord.swift */,
@@ -101,6 +103,15 @@
101103
path = StudyplusSDKTests;
102104
sourceTree = "<group>";
103105
};
106+
65E3911625F889A700490547 /* internal */ = {
107+
isa = PBXGroup;
108+
children = (
109+
183FDAB01E754DCB0085589F /* StudyplusAPI.swift */,
110+
65E3912825F8A74E00490547 /* StudyplusKeychain.swift */,
111+
);
112+
path = internal;
113+
sourceTree = "<group>";
114+
};
104115
/* End PBXGroup section */
105116

106117
/* Begin PBXHeadersBuildPhase section */
@@ -218,9 +229,10 @@
218229
isa = PBXSourcesBuildPhase;
219230
buildActionMask = 2147483647;
220231
files = (
221-
183FDAB11E754DCB0085589F /* StudyplusAPIRequest.swift in Sources */,
232+
183FDAB11E754DCB0085589F /* StudyplusAPI.swift in Sources */,
222233
183FDAB31E7561B90085589F /* StudyplusRecord.swift in Sources */,
223234
183FDAAD1E753E430085589F /* StudyplusLoginDelegate.swift in Sources */,
235+
65E3912925F8A74E00490547 /* StudyplusKeychain.swift in Sources */,
224236
183FDAAF1E7544240085589F /* StudyplusError.swift in Sources */,
225237
183FDAAB1E75148D0085589F /* Studyplus.swift in Sources */,
226238
);

Lib/StudyplusSDK/Studyplus.swift

Lines changed: 32 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// The MIT License (MIT)
66
//
7-
// Copyright (c) 2017 Studyplus inc.
7+
// Copyright (c) 2021 Studyplus inc.
88
//
99
// Permission is hereby granted, free of charge, to any person obtaining a copy
1010
// of this software and associated documentation files (the "Software"), to deal
@@ -35,13 +35,6 @@ import UIKit
3535
*/
3636
final public class Studyplus {
3737

38-
/**
39-
Returns studyplus sdk version.
40-
41-
StudyplusSDKのバージョンを返します
42-
*/
43-
public static let SDKVersion: String = "2.0.1"
44-
4538
/**
4639
Returns the shared defaults object.
4740

@@ -68,8 +61,6 @@ final public class Studyplus {
6861
*/
6962
public weak var delegate: StudyplusLoginDelegate?
7063

71-
private let accessTokenStoreKey: String = "accessToken"
72-
private let usernameStoreKey: String = "username"
7364
private var serviceName: String {
7465
return "Studyplus_iOS_SDK_\(consumerKey)"
7566
}
@@ -89,7 +80,7 @@ final public class Studyplus {
8980
///
9081
/// Studyplusアプリとの連携を解除します。
9182
public func logout() {
92-
deleteKey()
83+
StudyplusKeychain.deleteAll(serviceName: serviceName)
9384
}
9485

9586
/// Returns to whether or not it is connected with Studyplus application.
@@ -107,22 +98,7 @@ final public class Studyplus {
10798
///
10899
/// - Returns: accessToken
109100
public func accessToken() -> String? {
110-
let query = [
111-
kSecClass: kSecClassGenericPassword,
112-
kSecAttrService: serviceName,
113-
kSecAttrSynchronizable: kSecAttrSynchronizableAny,
114-
kSecMatchLimit: kSecMatchLimitOne,
115-
kSecReturnData: true,
116-
kSecAttrAccount: accessTokenStoreKey
117-
] as CFDictionary
118-
119-
var item: CFTypeRef?
120-
let status = SecItemCopyMatching(query, &item)
121-
guard status == errSecSuccess, let data = item as? Data else {
122-
return nil
123-
}
124-
125-
return String(data: data, encoding: .utf8)
101+
return StudyplusKeychain.accessToken(serviceName: serviceName)
126102
}
127103

128104
/// Username of Studyplus account. It is set when the auth or login is successful.
@@ -131,22 +107,7 @@ final public class Studyplus {
131107
///
132108
/// - Returns: username
133109
public func username() -> String? {
134-
let query = [
135-
kSecClass: kSecClassGenericPassword,
136-
kSecAttrService: serviceName,
137-
kSecAttrSynchronizable: kSecAttrSynchronizableAny,
138-
kSecMatchLimit: kSecMatchLimitOne,
139-
kSecReturnData: true,
140-
kSecAttrAccount: usernameStoreKey
141-
] as CFDictionary
142-
143-
var item: CFTypeRef?
144-
let status = SecItemCopyMatching(query, &item)
145-
guard status == errSecSuccess, let data = item as? Data else {
146-
return nil
147-
}
148-
149-
return String(data: data, encoding: .utf8)
110+
return StudyplusKeychain.username(serviceName: serviceName)
150111
}
151112

152113
/// Studyplusに学習記録を投稿
@@ -156,7 +117,7 @@ final public class Studyplus {
156117
/// - completion: 投稿完了後のコールバック
157118
public func post(_ record: StudyplusRecord, completion: @escaping (Result<Void, StudyplusPostError>) -> Void) {
158119
guard let accessToken = self.accessToken() else {
159-
completion(.failure(.needLogin))
120+
completion(.failure(.loginRequired))
160121
return
161122
}
162123

@@ -165,7 +126,21 @@ final public class Studyplus {
165126
return
166127
}
167128

168-
StudyplusAPIRequest(accessToken: accessToken).post(record, completion: completion)
129+
StudyplusAPI(accessToken: accessToken).post(record, completion: { result in
130+
switch result {
131+
case .failure(let error):
132+
switch error {
133+
case .loginRequired:
134+
// clear invalid access token
135+
StudyplusKeychain.deleteAll(serviceName: self.serviceName)
136+
default:
137+
break
138+
}
139+
case .success: break
140+
}
141+
142+
completion(result)
143+
})
169144
}
170145

171146
/// It is responsible for processing custom URL scheme
@@ -187,7 +162,7 @@ final public class Studyplus {
187162
/// __[studyplus-{consumerKey}]__と正しいpathComponentsを持つことを確認してください。
188163
public func handle(_ url: URL) -> Bool {
189164
guard isAcceptableURL(url: url) else {
190-
delegate?.studyplusDidFailToLogin(error: .unknownUrl(url))
165+
delegate?.studyplusLoginFail(error: .unknownUrl(url))
191166
return false
192167
}
193168

@@ -202,31 +177,20 @@ final public class Studyplus {
202177
.trimmingCharacters(in: .whitespacesAndNewlines)
203178
.data(using: .utf8, allowLossyConversion: false)!
204179

205-
deleteKey()
206-
let statusAccessToken = SecItemAdd([
207-
kSecClass: kSecClassGenericPassword,
208-
kSecAttrService: serviceName,
209-
kSecAttrSynchronizable: kSecAttrSynchronizableAny,
210-
kSecAttrAccount: accessTokenStoreKey,
211-
kSecValueData: accessToken
212-
] as CFDictionary, nil)
213-
let statusUsername = SecItemAdd([
214-
kSecClass: kSecClassGenericPassword,
215-
kSecAttrService: serviceName,
216-
kSecAttrSynchronizable: kSecAttrSynchronizableAny,
217-
kSecAttrAccount: usernameStoreKey,
218-
kSecValueData: username
219-
] as CFDictionary, nil)
220-
221-
if statusAccessToken == noErr && statusUsername == noErr {
222-
delegate?.studyplusDidSuccessToLogin()
223-
} else {
224-
delegate?.studyplusDidFailToLogin(error: .keychainError)
180+
StudyplusKeychain.set(serviceName: serviceName,
181+
accessToken: accessToken,
182+
username: username) { result in
183+
switch result {
184+
case .failure(let error):
185+
self.delegate?.studyplusLoginFail(error: error)
186+
case .success:
187+
self.delegate?.studyplusLoginSuccess()
188+
}
225189
}
226190
case "fail":
227-
delegate?.studyplusDidFailToLogin(error: .fail)
191+
delegate?.studyplusLoginFail(error: .applicationError)
228192
case "cancel":
229-
delegate?.studyplusDidFailToLogin(error: .cancel)
193+
delegate?.studyplusLoginFail(error: .cancel)
230194
default:
231195
return false
232196
}
@@ -323,12 +287,4 @@ final public class Studyplus {
323287

324288
return true
325289
}
326-
327-
private func deleteKey() {
328-
SecItemDelete([
329-
kSecClass: kSecClassGenericPassword,
330-
kSecAttrService: serviceName,
331-
kSecAttrSynchronizable: kSecAttrSynchronizableAny
332-
] as CFDictionary)
333-
}
334290
}

Lib/StudyplusSDK/StudyplusError.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ import Foundation
2828

2929
public enum StudyplusPostError: Error {
3030
case invalidDuration
31-
case needLogin
3231
case offline
3332
case badRequest
34-
case invalidAccessToken
33+
case loginRequired
3534
case serverError
3635
case unknown(_ message: String)
3736

@@ -40,7 +39,7 @@ public enum StudyplusPostError: Error {
4039
case 400:
4140
return .badRequest
4241
case 401:
43-
return .invalidAccessToken
42+
return .loginRequired
4443
case 500...599:
4544
return .serverError
4645
default:
@@ -52,6 +51,6 @@ public enum StudyplusPostError: Error {
5251
public enum StudyplusLoginError: Error {
5352
case unknownUrl(_ url: URL)
5453
case keychainError
55-
case fail
54+
case applicationError
5655
case cancel
5756
}

Lib/StudyplusSDK/StudyplusLoginDelegate.swift

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,7 @@
2626

2727
import Foundation
2828

29-
/**
30-
The delegate to receive callbacks from Studyplus.
31-
32-
Studyplusオブジェクトに対する各種操作後のコールバックを受けるdelegateです。
33-
*/
34-
public protocol StudyplusLoginDelegate: class {
35-
36-
/// Will be called after the Studyplus#login was successful.
37-
///
38-
/// Studyplus#login が成功した後に呼ばれます。
39-
func studyplusDidSuccessToLogin()
40-
41-
/// Will be called after the Studyplus#login was failure.
42-
///
43-
/// Studyplus#login が失敗した後に呼ばれます。
44-
///
45-
/// - Parameter error: failure reason, see StudyplusError. 失敗の理由です。詳細は StudyplusError を参照してください。
46-
func studyplusDidFailToLogin(error: StudyplusLoginError)
29+
public protocol StudyplusLoginDelegate: AnyObject {
30+
func studyplusLoginSuccess()
31+
func studyplusLoginFail(error: StudyplusLoginError)
4732
}

0 commit comments

Comments
 (0)