Skip to content

Commit

Permalink
Remove config caching throttle delay (#1287)
Browse files Browse the repository at this point in the history
- Move from `URLCache` to `NSCache` and wrap in custom `ConfigurationCache` class
- Remove caching logic from `BTHTTP`
    - Call caching functions directly from `BTAPIClient.fetchOrReturnRemoteConfig()`
- Cleanup
    - Remove unused `URLSession` config injected at `APIClient` level (this was never caching locally since `v1/configuration` has no Cache Control headers)
  • Loading branch information
scannillo authored May 4, 2024
1 parent 83a5709 commit dd70d70
Show file tree
Hide file tree
Showing 14 changed files with 223 additions and 215 deletions.
22 changes: 11 additions & 11 deletions Braintree.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
80BA64AC29D788E000E15264 /* BTLocalPaymentResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BA64AB29D788E000E15264 /* BTLocalPaymentResult.swift */; };
80BA64B229D7937E00E15264 /* BTLocalPaymentRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BA64B129D7937E00E15264 /* BTLocalPaymentRequest.swift */; };
80BA64B429D795D000E15264 /* BTLocalPaymentRequestDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80BA64B329D795D000E15264 /* BTLocalPaymentRequestDelegate.swift */; };
80C10F832BE090AA00BFA2EE /* ConfigurationCache_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE698EA528B3CDAD001D9B10 /* ConfigurationCache_Tests.swift */; };
80CD34002A6042FC009545F5 /* CardinalSessionTestable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80CD33FF2A6042FC009545F5 /* CardinalSessionTestable.swift */; };
80CD34012A604307009545F5 /* MockCardinalSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80CD33FD2A603892009545F5 /* MockCardinalSession.swift */; };
80CF988529DB64D400D51979 /* BTLocalPaymentError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80CF988429DB64D400D51979 /* BTLocalPaymentError.swift */; };
Expand Down Expand Up @@ -225,9 +226,8 @@
BE642DA927D013A400694A5B /* BTSEPADirectDebitClient_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE642D8C27D0130300694A5B /* BTSEPADirectDebitClient_Tests.swift */; };
BE642DAB27D01BCA00694A5B /* BTSEPADirectDebitNonce_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE642DAA27D01BCA00694A5B /* BTSEPADirectDebitNonce_Tests.swift */; };
BE698EA028AA8DCB001D9B10 /* BTHTTP.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE698E9F28AA8DCB001D9B10 /* BTHTTP.swift */; };
BE698EA228AA8EEA001D9B10 /* BTCacheDateValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE698EA128AA8EEA001D9B10 /* BTCacheDateValidator.swift */; };
BE698EA228AA8EEA001D9B10 /* ConfigurationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE698EA128AA8EEA001D9B10 /* ConfigurationCache.swift */; };
BE698EA428AD2C10001D9B10 /* BTCoreConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE698EA328AD2C10001D9B10 /* BTCoreConstants.swift */; };
BE698EA628B3CDAD001D9B10 /* BTCacheDateValidator_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE698EA528B3CDAD001D9B10 /* BTCacheDateValidator_Tests.swift */; };
BE70A963284FA3F000F6D3F7 /* BTDataCollectorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE70A962284FA3F000F6D3F7 /* BTDataCollectorError.swift */; };
BE70A965284FA9DE00F6D3F7 /* MockBTDataCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE70A964284FA9DE00F6D3F7 /* MockBTDataCollector.swift */; };
BE70A983284FC07C00F6D3F7 /* BraintreeDataCollector.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A76D7C001BB1CAB00000FA6A /* BraintreeDataCollector.framework */; };
Expand Down Expand Up @@ -860,9 +860,9 @@
BE642DA027D0132A00694A5B /* BraintreeSEPADirectDebitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BraintreeSEPADirectDebitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
BE642DAA27D01BCA00694A5B /* BTSEPADirectDebitNonce_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTSEPADirectDebitNonce_Tests.swift; sourceTree = "<group>"; };
BE698E9F28AA8DCB001D9B10 /* BTHTTP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTHTTP.swift; sourceTree = "<group>"; };
BE698EA128AA8EEA001D9B10 /* BTCacheDateValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTCacheDateValidator.swift; sourceTree = "<group>"; };
BE698EA128AA8EEA001D9B10 /* ConfigurationCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationCache.swift; sourceTree = "<group>"; };
BE698EA328AD2C10001D9B10 /* BTCoreConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTCoreConstants.swift; sourceTree = "<group>"; };
BE698EA528B3CDAD001D9B10 /* BTCacheDateValidator_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTCacheDateValidator_Tests.swift; sourceTree = "<group>"; };
BE698EA528B3CDAD001D9B10 /* ConfigurationCache_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationCache_Tests.swift; sourceTree = "<group>"; };
BE698EAA28B50F41001D9B10 /* BTClientToken_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTClientToken_Tests.swift; sourceTree = "<group>"; };
BE70A962284FA3F000F6D3F7 /* BTDataCollectorError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTDataCollectorError.swift; sourceTree = "<group>"; };
BE70A964284FA9DE00F6D3F7 /* MockBTDataCollector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockBTDataCollector.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1215,7 +1215,6 @@
579DAEC6286E064500FCE87F /* BTAppContextSwitchClient.swift */,
579DAEC4286E04A700FCE87F /* BTAppContextSwitcher.swift */,
BED00CAD28A5419900D74AEC /* BTBinData.swift */,
BE698EA128AA8EEA001D9B10 /* BTCacheDateValidator.swift */,
BE32ACBB2907744400A61FED /* BTCardNetwork.swift */,
574891EC286F7ECA0020DA36 /* BTClientMetadata.swift */,
574891EA286F7E4F0020DA36 /* BTClientMetadataIntegration.swift */,
Expand Down Expand Up @@ -1243,6 +1242,7 @@
BED7493528579BAC0074C818 /* BTURLUtils.swift */,
BEBD05212A1FE8BE0003C15C /* BTWebAuthenticationSession.swift */,
BEB9BF522A26872B00A3673E /* BTWebAuthenticationSessionClient.swift */,
BE698EA128AA8EEA001D9B10 /* ConfigurationCache.swift */,
80F86FEF29FC2ED6003297FF /* Encodable+Dictionary.swift */,
80A6C6182B21205900416D50 /* UIApplication+URLOpener.swift */,
8053F05829FB2F700076F988 /* URL+IsPayPal.swift */,
Expand Down Expand Up @@ -1564,12 +1564,12 @@
A908436924FD88C3004134CA /* Helpers */ = {
isa = PBXGroup;
children = (
BEBC6F3429380BA6004E25A0 /* BTExceptionCatcher.h */,
BEBC6F3129380B82004E25A0 /* BTExceptionCatcher.m */,
BE54C0342912B6BC009C6CEE /* BTHTTPTestProtocol.swift */,
80BA3C282B23892700900BBB /* FakeRequest.swift */,
428F976426727333001042E1 /* BTMockOpenURLContext.h */,
428F976526727333001042E1 /* BTMockOpenURLContext.m */,
BEBC6F3129380B82004E25A0 /* BTExceptionCatcher.m */,
BEBC6F3429380BA6004E25A0 /* BTExceptionCatcher.h */,
80BA3C282B23892700900BBB /* FakeRequest.swift */,
);
path = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -1719,7 +1719,6 @@
842B68F01BCF083E0039634F /* BTAPIClient_Tests.swift */,
A7CCE2AD1B67F26C006EA661 /* BTAppContextSwitcher_Tests.swift */,
030DBF451F3A5F5B00E959F0 /* BTBinData_Tests.swift */,
BE698EA528B3CDAD001D9B10 /* BTCacheDateValidator_Tests.swift */,
BEBC6F252937A510004E25A0 /* BTClientMetadata_Tests.swift */,
BE698EAA28B50F41001D9B10 /* BTClientToken_Tests.swift */,
A7B7989B1C233C57001327FA /* BTConfiguration_Tests.swift */,
Expand All @@ -1730,6 +1729,7 @@
805FD35B2331780F0000B514 /* BTPostalAddress_Tests.swift */,
A7E93E571B601EE900957223 /* BTURLUtils_Tests.swift */,
A7B861BE1C24B19300A2422E /* BTVersion_Tests.swift */,
BE698EA528B3CDAD001D9B10 /* ConfigurationCache_Tests.swift */,
8053F05629FAD4790076F988 /* Encodable+Dictionary_Tests.swift */,
A908436924FD88C3004134CA /* Helpers */,
A9E5C22824FD6D0800EE691F /* Info.plist */,
Expand Down Expand Up @@ -2810,7 +2810,7 @@
BE5C8C0328A2C183004F9130 /* BTConfiguration+Core.swift in Sources */,
BEE2E4E6290080BD00C03FDD /* BTAnalyticsServiceError.swift in Sources */,
BE9FB82B2898324C00D6FE2F /* BTPaymentMethodNonce.swift in Sources */,
BE698EA228AA8EEA001D9B10 /* BTCacheDateValidator.swift in Sources */,
BE698EA228AA8EEA001D9B10 /* ConfigurationCache.swift in Sources */,
BEB9BF532A26872B00A3673E /* BTWebAuthenticationSessionClient.swift in Sources */,
80F86FF029FC2ED6003297FF /* Encodable+Dictionary.swift in Sources */,
579DAEC7286E064500FCE87F /* BTAppContextSwitchClient.swift in Sources */,
Expand Down Expand Up @@ -3040,7 +3040,6 @@
A948D6D224FAB8BA00F4F178 /* BTAmericanExpressRewardsBalance_Tests.swift in Sources */,
BEDB820429B10ADA00075AF3 /* BTApplePayAnalytics_Tests.swift in Sources */,
3B0EF89629B28F3800456973 /* BTAmericanExpressAnalytics_Tests.swift in Sources */,
BE698EA628B3CDAD001D9B10 /* BTCacheDateValidator_Tests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -3130,6 +3129,7 @@
A908436224FD82A9004134CA /* BTAppContextSwitcher_Tests.swift in Sources */,
BEBC6F282937BD1F004E25A0 /* BTGraphQLHTTP_Tests.swift in Sources */,
BE54C0352912B6BC009C6CEE /* BTHTTPTestProtocol.swift in Sources */,
80C10F832BE090AA00BFA2EE /* ConfigurationCache_Tests.swift in Sources */,
BEDA91A028EDDE64007441D9 /* FakeAnalyticsService.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Braintree iOS SDK Release Notes

## unreleased
* Remove throttle delay in accessing configuration, added in v5.9.0
* Move from URLCache to NSCache for configuration caching

## 6.18.0 (2024-04-25)
* BraintreePayPalNativeCheckout
* Bump PayPalCheckout to version 1.3.0 with code signing & a privacy manifest file.
Expand Down
91 changes: 44 additions & 47 deletions Sources/BraintreeCore/BTAPIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,11 @@ import Foundation
public private(set) var metadata: BTClientMetadata

// MARK: - Internal Properties

/// Used to fetch and store configurations in the URL Cache of the session

var configurationHTTP: BTHTTP?

var http: BTHTTP?
var graphQLHTTP: BTGraphQLHTTP?

var session: URLSession {
let configurationQueue: OperationQueue = OperationQueue()
configurationQueue.name = "com.braintreepayments.BTAPIClient"

// BTHTTP's default NSURLSession does not cache responses, but we want the BTHTTP instance that fetches configuration to cache aggressively
let configuration: URLSessionConfiguration = URLSessionConfiguration.default
let configurationCache: URLCache = URLCache(memoryCapacity: 1 * 1024 * 1024, diskCapacity: 0, diskPath: nil)

configuration.urlCache = configurationCache

// Use the caching logic defined in the protocol implementation, if any, for a particular URL load request.
configuration.requestCachePolicy = .useProtocolCachePolicy
return URLSession(configuration: configuration)
}

/// Exposed for testing analytics
/// By default, the `BTAnalyticsService` instance is static/shared so that only one queue of events exists.
/// The "singleton" is managed here because the analytics service depends on `BTAPIClient`.
Expand Down Expand Up @@ -94,8 +77,6 @@ import Foundation
}
}

configurationHTTP?.session = session

// Kickoff the background request to fetch the config
fetchOrReturnRemoteConfiguration { configuration, error in
// No-op
Expand All @@ -112,8 +93,6 @@ import Foundation
if graphQLHTTP != nil && graphQLHTTP?.session != nil {
graphQLHTTP?.session.finishTasksAndInvalidate()
}

configurationHTTP?.session.configuration.urlCache?.removeAllCachedResponses()
}

// MARK: - Public Methods
Expand All @@ -138,13 +117,23 @@ import Foundation
// - If fetching fails, return error

var configPath: String = "v1/configuration"
var configuration: BTConfiguration?

if let clientToken {
configPath = clientToken.configURL.absoluteString
}

guard let authorization = clientToken?.authorizationFingerprint ?? tokenizationKey else {
completion(nil, BTAPIClientError.configurationUnavailable)
return
}

if let cachedConfig = try? ConfigurationCache.shared.getFromCache(authorization: authorization) {
setupHTTPCredentials(cachedConfig)
completion(cachedConfig, nil)
return
}

configurationHTTP?.get(configPath, parameters: BTConfigurationRequest(), shouldCache: true) { [weak self] body, response, error in
configurationHTTP?.get(configPath, parameters: BTConfigurationRequest()) { [weak self] body, response, error in
guard let self else {
completion(nil, BTAPIClientError.deallocated)
return
Expand All @@ -153,34 +142,18 @@ import Foundation
if error != nil {
completion(nil, error)
return
} else if response?.statusCode != 200 {
} else if response?.statusCode != 200 || body == nil {
completion(nil, BTAPIClientError.configurationUnavailable)
return
} else {
configuration = BTConfiguration(json: body)

if http == nil {
let baseURL: URL? = configuration?.json?["clientApiUrl"].asURL()
let configuration = BTConfiguration(json: body)

if let clientToken, let baseURL {
http = BTHTTP(url: baseURL, authorizationFingerprint: clientToken.authorizationFingerprint)
} else if let tokenizationKey, let baseURL {
http = BTHTTP(url: baseURL, tokenizationKey: tokenizationKey)
}
}

if graphQLHTTP == nil {
let graphQLBaseURL: URL? = graphQLURL(forEnvironment: configuration?.environment ?? "")

if let clientToken, let graphQLBaseURL {
graphQLHTTP = BTGraphQLHTTP(url: graphQLBaseURL, authorizationFingerprint: clientToken.authorizationFingerprint)
} else if let tokenizationKey, let graphQLBaseURL {
graphQLHTTP = BTGraphQLHTTP(url: graphQLBaseURL, tokenizationKey: tokenizationKey)
}
}
setupHTTPCredentials(configuration)
try? ConfigurationCache.shared.putInCache(authorization: authorization, configuration: configuration)

completion(configuration, nil)
return
}

completion(configuration, nil)
}
}

Expand Down Expand Up @@ -485,4 +458,28 @@ import Foundation
return graphQLHTTP
}
}

// MARK: - Private Methods

private func setupHTTPCredentials(_ configuration: BTConfiguration) {
if http == nil {
let baseURL: URL? = configuration.json?["clientApiUrl"].asURL()

if let clientToken, let baseURL {
http = BTHTTP(url: baseURL, authorizationFingerprint: clientToken.authorizationFingerprint)
} else if let tokenizationKey, let baseURL {
http = BTHTTP(url: baseURL, tokenizationKey: tokenizationKey)
}
}

if graphQLHTTP == nil {
let graphQLBaseURL: URL? = graphQLURL(forEnvironment: configuration.environment ?? "")

if let clientToken, let graphQLBaseURL {
graphQLHTTP = BTGraphQLHTTP(url: graphQLBaseURL, authorizationFingerprint: clientToken.authorizationFingerprint)
} else if let tokenizationKey, let graphQLBaseURL {
graphQLHTTP = BTGraphQLHTTP(url: graphQLBaseURL, tokenizationKey: tokenizationKey)
}
}
}
}
6 changes: 6 additions & 0 deletions Sources/BraintreeCore/BTAPIClientError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ public enum BTAPIClientError: Int, Error, CustomNSError, LocalizedError, Equatab

/// 2. Deallocated BTAPIClient
case deallocated

/// 3. Failed to base64 encode an authorizationFingerprint or tokenizationKey, when used as a cacheKey
case failedBase64Encoding

public static var errorDomain: String {
"com.braintreepayments.BTAPIClientErrorDomain"
Expand All @@ -30,6 +33,9 @@ public enum BTAPIClientError: Int, Error, CustomNSError, LocalizedError, Equatab

case .deallocated:
return "BTAPIClient has been deallocated."

case .failedBase64Encoding:
return "Unable to base64 encode the authorization string."
}
}
}
25 changes: 0 additions & 25 deletions Sources/BraintreeCore/BTCacheDateValidator.swift

This file was deleted.

4 changes: 4 additions & 0 deletions Sources/BraintreeCore/BTConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import Foundation
var fptiEnvironment: String? {
environment == "production" ? "live" : environment
}

/// :nodoc: Timestamp of initialization of each `BTConfiguration` instance
/// Mutable for testing.
var time = Date().timeIntervalSince1970

/// :nodoc: This initalizer is exposed for internal Braintree use only. Do not use. It is not covered by Semantic Versioning and may change or be removed at any time.
/// Used to initialize a `BTConfiguration`
Expand Down
2 changes: 1 addition & 1 deletion Sources/BraintreeCore/BTGraphQLHTTP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class BTGraphQLHTTP: BTHTTP {

// MARK: - Overrides

override func get(_ path: String, parameters: Encodable? = nil, shouldCache: Bool = false, completion: @escaping RequestCompletion) {
override func get(_ path: String, parameters: Encodable? = nil, completion: @escaping RequestCompletion) {
NSException(name: exceptionName, reason: "GET is unsupported").raise()
}

Expand Down
Loading

0 comments on commit dd70d70

Please sign in to comment.