Skip to content

Commit bcd3c38

Browse files
jacobsimeonJacob Morris
authored and
Jacob Morris
committed
Cache access token
Avoid repetitively reading the access token from the keychain
1 parent d4d9d5d commit bcd3c38

File tree

2 files changed

+36
-0
lines changed

2 files changed

+36
-0
lines changed

PocketKit/Sources/PocketKit/Authorization/AccessTokenStore.swift

+10
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ class KeychainAccessTokenStore: AccessTokenStore {
2525
self.keychain = keychain
2626
}
2727

28+
private var _accessToken: String?
2829
var accessToken: String? {
30+
guard _accessToken == nil else {
31+
return _accessToken
32+
}
33+
2934
let query: [String: Any] = [
3035
kSecClass as String: kSecClassGenericPassword,
3136
kSecAttrService as String: Bundle.main.bundleIdentifier!,
@@ -44,10 +49,13 @@ class KeychainAccessTokenStore: AccessTokenStore {
4449
return nil
4550
}
4651

52+
_accessToken = token
4753
return token
4854
}
4955

5056
func save(token: String) throws {
57+
_accessToken = nil
58+
5159
let query: [String: Any] = [
5260
kSecClass as String: kSecClassGenericPassword,
5361
kSecAttrService as String: Bundle.main.bundleIdentifier!,
@@ -65,6 +73,8 @@ class KeychainAccessTokenStore: AccessTokenStore {
6573
}
6674

6775
func delete() throws {
76+
_accessToken = nil
77+
6878
let query: [String: Any] = [
6979
kSecClass as String: kSecClassGenericPassword,
7080
kSecAttrService as String: Bundle.main.bundleIdentifier!,

PocketKit/Tests/PocketKitTests/AccessTokenStoreTests.swift

+26
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,32 @@ class KeychainAccessTokenStoreTests: XCTestCase {
5151
)
5252
}
5353

54+
func test_accessToken_cachesValueFromKeychain() throws {
55+
let store = KeychainAccessTokenStore(keychain: mockKeychain)
56+
mockKeychain.copyMatchingResult = "the-token".data(using: .utf8) as CFTypeRef
57+
mockKeychain.deleteReturnVal = 0
58+
59+
_ = store.accessToken // fetch from keychain
60+
_ = store.accessToken // use in-memory cache
61+
62+
XCTAssertEqual(mockKeychain.copyMatchingCalls.count, 1)
63+
64+
try store.save(token: "new-token")
65+
mockKeychain.copyMatchingResult = "new-token".data(using: .utf8) as CFTypeRef
66+
XCTAssertEqual(store.accessToken, "new-token")
67+
XCTAssertEqual(store.accessToken, "new-token")
68+
XCTAssertEqual(mockKeychain.copyMatchingCalls.count, 2)
69+
70+
try store.delete()
71+
mockKeychain.copyMatchingResult = nil
72+
XCTAssertEqual(store.accessToken, nil)
73+
XCTAssertEqual(mockKeychain.copyMatchingCalls.count, 3)
74+
75+
mockKeychain.copyMatchingResult = "even-newer-token".data(using: .utf8) as CFTypeRef
76+
XCTAssertEqual(store.accessToken, "even-newer-token")
77+
XCTAssertEqual(mockKeychain.copyMatchingCalls.count, 4)
78+
}
79+
5480
func test_accessToken_whenCopyMatchingSucceeds_returnsDecodedToken() {
5581
mockKeychain.copyMatchingResult = "the-token".data(using: .utf8) as CFTypeRef
5682
let store = KeychainAccessTokenStore(keychain: mockKeychain)

0 commit comments

Comments
 (0)