From 8f8b5ab315c85182df5803b888b62d307c639658 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Wed, 7 Aug 2024 15:35:28 -0500 Subject: [PATCH] WIP - Use while loop to prevent duplicate config GET requests & bool flag Signed-off-by: Jax DesMarais-Leder --- Sources/BraintreeCore/BTAPIClient.swift | 18 ++++----- Sources/BraintreeCore/BTHTTP.swift | 13 +++++++ .../ConfigurationCallbackStorage.swift | 3 ++ .../Configuration/ConfigurationLoader.swift | 38 +++++++++++++++---- 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/Sources/BraintreeCore/BTAPIClient.swift b/Sources/BraintreeCore/BTAPIClient.swift index 4479a94a05..92ddfce900 100644 --- a/Sources/BraintreeCore/BTAPIClient.swift +++ b/Sources/BraintreeCore/BTAPIClient.swift @@ -93,19 +93,15 @@ import Foundation /// cached on subsequent calls for better performance. @_documentation(visibility: private) public func fetchOrReturnRemoteConfiguration(_ completion: @escaping (BTConfiguration?, Error?) -> Void) { - configurationLoader.getConfig { [weak self] configuration, error in - guard let self else { - completion(nil, BTAPIClientError.deallocated) - return - } - - if let error { + // TODO: - Consider updating all feature clients to use async version of this method? + Task { + do { + let configuration = try await configurationLoader.getConfig() + setupHTTPCredentials(configuration) + completion(configuration, nil) + } catch { completion(nil, error) - return } - - setupHTTPCredentials(configuration) - completion(configuration, nil) } } diff --git a/Sources/BraintreeCore/BTHTTP.swift b/Sources/BraintreeCore/BTHTTP.swift index ef72f275e9..b7df2773f1 100644 --- a/Sources/BraintreeCore/BTHTTP.swift +++ b/Sources/BraintreeCore/BTHTTP.swift @@ -77,6 +77,19 @@ class BTHTTP: NSObject, URLSessionTaskDelegate { completion(nil, nil, error) } } + + func get(_ path: String, configuration: BTConfiguration? = nil, parameters: Encodable? = nil) async throws -> (BTJSON, HTTPURLResponse) { + try await withCheckedThrowingContinuation { continuation in + get(path, configuration: configuration, parameters: parameters) { body, response, error in + if let error { + continuation.resume(throwing: error) + } else if let body, let response { + continuation.resume(returning: (body, response)) + } + } + // TODO: - How do we want to handle case of nil body or response + } + } // TODO: - Remove when all POST bodies use Codable, instead of BTJSON/raw dictionaries func post( diff --git a/Sources/BraintreeCore/Configuration/ConfigurationCallbackStorage.swift b/Sources/BraintreeCore/Configuration/ConfigurationCallbackStorage.swift index a2ccbb430a..1b6b5ce132 100644 --- a/Sources/BraintreeCore/Configuration/ConfigurationCallbackStorage.swift +++ b/Sources/BraintreeCore/Configuration/ConfigurationCallbackStorage.swift @@ -1,8 +1,11 @@ import Foundation +// TODO: - Do we still need this class? It no-longer wraps an array to provide thread-safety + /// Used to store, access, and manage an array of to-be-invoked `BTConfiguration` GET result callbacks class ConfigurationCallbackStorage { + // TODO: - Remove. Not an option to store array of completions. private var pendingCompletions: [(BTConfiguration?, Error?) -> Void] = [] /// The number of completions that are waiting to be invoked diff --git a/Sources/BraintreeCore/Configuration/ConfigurationLoader.swift b/Sources/BraintreeCore/Configuration/ConfigurationLoader.swift index ecc15220f6..5b667a3b3f 100644 --- a/Sources/BraintreeCore/Configuration/ConfigurationLoader.swift +++ b/Sources/BraintreeCore/Configuration/ConfigurationLoader.swift @@ -8,7 +8,8 @@ class ConfigurationLoader { private let configurationCache = ConfigurationCache.shared private let http: BTHTTP private let pendingCompletions = ConfigurationCallbackStorage() - + private var isConfigCached = false // TODO: - Rename; should this bool live here? + // MARK: - Intitializer init(http: BTHTTP) { @@ -69,17 +70,38 @@ class ConfigurationLoader { } } } - + func getConfig() async throws -> BTConfiguration { - try await withCheckedThrowingContinuation { continuation in - getConfig { configuration, error in - if let error { - continuation.resume(throwing: error) - } else if let configuration { - continuation.resume(returning: configuration) + if let cachedConfig = try? configurationCache.getFromCache(authorization: http.authorization.bearer) { + isConfigCached = true + return cachedConfig + } + + while !isConfigCached { + print("While loop body") + + do { + print("🤞GET request made") + let (body, response) = try await http.get(configPath, parameters: BTConfigurationRequest()) + + if response.statusCode != 200 { // || body == nil { + throw BTAPIClientError.configurationUnavailable + } else { + let configuration = BTConfiguration(json: body) + + try? configurationCache.putInCache(authorization: http.authorization.bearer, configuration: configuration) + + NotificationCenter.default.post(name: Notification.Name("ConfigGet"), object: configuration) + isConfigCached = true + + return configuration } + } catch { + throw error } } + print("Exited while loop") + throw BTAPIClientError.configurationUnavailable } // MARK: - Private Methods