Skip to content

Commit

Permalink
Cache access token
Browse files Browse the repository at this point in the history
Avoid repetitively reading the access token from the keychain
  • Loading branch information
jacobsimeon authored and Jacob Morris committed Aug 19, 2021
1 parent d4d9d5d commit bcd3c38
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 0 deletions.
10 changes: 10 additions & 0 deletions PocketKit/Sources/PocketKit/Authorization/AccessTokenStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ class KeychainAccessTokenStore: AccessTokenStore {
self.keychain = keychain
}

private var _accessToken: String?
var accessToken: String? {
guard _accessToken == nil else {
return _accessToken
}

let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: Bundle.main.bundleIdentifier!,
Expand All @@ -44,10 +49,13 @@ class KeychainAccessTokenStore: AccessTokenStore {
return nil
}

_accessToken = token
return token
}

func save(token: String) throws {
_accessToken = nil

let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: Bundle.main.bundleIdentifier!,
Expand All @@ -65,6 +73,8 @@ class KeychainAccessTokenStore: AccessTokenStore {
}

func delete() throws {
_accessToken = nil

let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: Bundle.main.bundleIdentifier!,
Expand Down
26 changes: 26 additions & 0 deletions PocketKit/Tests/PocketKitTests/AccessTokenStoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,32 @@ class KeychainAccessTokenStoreTests: XCTestCase {
)
}

func test_accessToken_cachesValueFromKeychain() throws {
let store = KeychainAccessTokenStore(keychain: mockKeychain)
mockKeychain.copyMatchingResult = "the-token".data(using: .utf8) as CFTypeRef
mockKeychain.deleteReturnVal = 0

_ = store.accessToken // fetch from keychain
_ = store.accessToken // use in-memory cache

XCTAssertEqual(mockKeychain.copyMatchingCalls.count, 1)

try store.save(token: "new-token")
mockKeychain.copyMatchingResult = "new-token".data(using: .utf8) as CFTypeRef
XCTAssertEqual(store.accessToken, "new-token")
XCTAssertEqual(store.accessToken, "new-token")
XCTAssertEqual(mockKeychain.copyMatchingCalls.count, 2)

try store.delete()
mockKeychain.copyMatchingResult = nil
XCTAssertEqual(store.accessToken, nil)
XCTAssertEqual(mockKeychain.copyMatchingCalls.count, 3)

mockKeychain.copyMatchingResult = "even-newer-token".data(using: .utf8) as CFTypeRef
XCTAssertEqual(store.accessToken, "even-newer-token")
XCTAssertEqual(mockKeychain.copyMatchingCalls.count, 4)
}

func test_accessToken_whenCopyMatchingSucceeds_returnsDecodedToken() {
mockKeychain.copyMatchingResult = "the-token".data(using: .utf8) as CFTypeRef
let store = KeychainAccessTokenStore(keychain: mockKeychain)
Expand Down

0 comments on commit bcd3c38

Please sign in to comment.