From 18127f6cc6c1abda8138c6ba877ac1a966176360 Mon Sep 17 00:00:00 2001 From: Nick Cooke Date: Thu, 14 Nov 2024 12:35:05 -0500 Subject: [PATCH] [Auth] Remove AuthDispatcher singleton, prefer DI --- FirebaseAuth/Sources/Swift/Auth/Auth.swift | 8 ++++++-- .../Sources/Swift/Auth/AuthDispatcher.swift | 12 ++++++++---- .../Tests/Unit/AuthDispatcherTests.swift | 16 ++-------------- FirebaseAuth/Tests/Unit/AuthTests.swift | 16 ++++++++-------- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/Auth/Auth.swift b/FirebaseAuth/Sources/Swift/Auth/Auth.swift index e5ccdaafac7..f77fe2aae2c 100644 --- a/FirebaseAuth/Sources/Swift/Auth/Auth.swift +++ b/FirebaseAuth/Sources/Swift/Auth/Auth.swift @@ -1616,7 +1616,8 @@ extension Auth: AuthInterop { init(app: FirebaseApp, keychainStorageProvider: AuthKeychainStorage = AuthKeychainStorageReal(), - backend: AuthBackend = AuthBackend(rpcIssuer: AuthBackendRPCIssuer())) { + backend: AuthBackend = .init(rpcIssuer: AuthBackendRPCIssuer()), + authDispatcher: AuthDispatcher = .init()) { Auth.setKeychainServiceNameForApp(app) self.app = app mainBundleUrlTypes = Bundle.main @@ -1642,6 +1643,7 @@ extension Auth: AuthInterop { heartbeatLogger: app.heartbeatLogger, appCheck: appCheck) self.backend = backend + self.authDispatcher = authDispatcher super.init() requestConfiguration.auth = self @@ -1902,7 +1904,7 @@ extension Auth: AuthInterop { } autoRefreshScheduled = true weak var weakSelf = self - AuthDispatcher.shared.dispatch(afterDelay: delay, queue: kAuthGlobalWorkQueue) { + authDispatcher.dispatch(afterDelay: delay, queue: kAuthGlobalWorkQueue) { guard let strongSelf = weakSelf else { return } @@ -2361,6 +2363,8 @@ extension Auth: AuthInterop { /// The Firebase app name. private let firebaseAppName: String + private let authDispatcher: AuthDispatcher + /// The keychain service. private var keychainServices: AuthKeychainServices! diff --git a/FirebaseAuth/Sources/Swift/Auth/AuthDispatcher.swift b/FirebaseAuth/Sources/Swift/Auth/AuthDispatcher.swift index 6373cdfb4cc..f25e256ad51 100644 --- a/FirebaseAuth/Sources/Swift/Auth/AuthDispatcher.swift +++ b/FirebaseAuth/Sources/Swift/Auth/AuthDispatcher.swift @@ -15,13 +15,17 @@ import Foundation /// A utility class used to facilitate scheduling tasks to be executed in the future. -class AuthDispatcher { - static let shared = AuthDispatcher() - +struct AuthDispatcher { /// Allows custom implementation of dispatchAfterDelay:queue:callback:. /// /// Set to nil to restore default implementation. - var dispatchAfterImplementation: ((TimeInterval, DispatchQueue, @escaping () -> Void) -> Void)? + private let dispatchAfterImplementation: ((TimeInterval, DispatchQueue, @escaping () -> Void) + -> Void)? + + init(dispatchAfterImplementation: ((TimeInterval, DispatchQueue, @escaping () -> Void) -> Void)? = + nil) { + self.dispatchAfterImplementation = dispatchAfterImplementation + } /// Schedules task in the future after a specified delay. /// - Parameter delay: The delay in seconds after which the task will be scheduled to execute. diff --git a/FirebaseAuth/Tests/Unit/AuthDispatcherTests.swift b/FirebaseAuth/Tests/Unit/AuthDispatcherTests.swift index 08f34585ba0..57dc9e394c9 100644 --- a/FirebaseAuth/Tests/Unit/AuthDispatcherTests.swift +++ b/FirebaseAuth/Tests/Unit/AuthDispatcherTests.swift @@ -22,25 +22,15 @@ class AuthDispatcherTests: XCTestCase { let kTestDelay = 0.1 let kMaxDifferenceBetweenTimeIntervals = 0.4 - /** @fn testSharedInstance - @brief Tests @c sharedInstance returns the same object. - */ - func testSharedInstance() { - let instance1 = AuthDispatcher.shared - let instance2 = AuthDispatcher.shared - XCTAssert(instance1 === instance2) - } - /** @fn testDispatchAfterDelay @brief Tests @c dispatchAfterDelay indeed dispatches the specified task after the provided delay. */ func testDispatchAfterDelay() { - let dispatcher = AuthDispatcher.shared + let dispatcher = AuthDispatcher() let testWorkQueue = DispatchQueue(label: "test.work.queue") let expectation = self.expectation(description: #function) let dateBeforeDispatch = Date() - dispatcher.dispatchAfterImplementation = nil dispatcher.dispatch(afterDelay: kTestDelay, queue: testWorkQueue) { [self] in let timeSinceDispatch = fabs(dateBeforeDispatch.timeIntervalSinceNow - self.kTestDelay) XCTAssertLessThan(timeSinceDispatch, kMaxDifferenceBetweenTimeIntervals) @@ -54,15 +44,13 @@ class AuthDispatcherTests: XCTestCase { @c dispatchAfterDelay. */ func testSetDispatchAfterImplementation() { - let dispatcher = AuthDispatcher.shared let testWorkQueue = DispatchQueue(label: "test.work.queue") let expectation = self.expectation(description: #function) - dispatcher.dispatchAfterImplementation = { delay, queue, task in + let dispatcher = AuthDispatcher { delay, queue, task in XCTAssertEqual(self.kTestDelay, delay) XCTAssertEqual(testWorkQueue, queue) expectation.fulfill() } - dispatcher.dispatch(afterDelay: kTestDelay, queue: testWorkQueue) { // Fail to ensure this code is never executed. XCTFail("Should not execute this code") diff --git a/FirebaseAuth/Tests/Unit/AuthTests.swift b/FirebaseAuth/Tests/Unit/AuthTests.swift index f84eb33d70f..9bf57b39dd3 100644 --- a/FirebaseAuth/Tests/Unit/AuthTests.swift +++ b/FirebaseAuth/Tests/Unit/AuthTests.swift @@ -45,20 +45,20 @@ class AuthTests: RPCBaseTests { #else let keychainStorageProvider = AuthKeychainStorageReal() #endif // (os(macOS) && !FIREBASE_AUTH_TESTING_USE_MACOS_KEYCHAIN) || SWIFT_PACKAGE - auth = Auth( - app: FirebaseApp.app(name: name)!, - keychainStorageProvider: keychainStorageProvider, - backend: authBackend - ) - // Set authDispatcherCallback implementation in order to save the token refresh task for later - // execution. - AuthDispatcher.shared.dispatchAfterImplementation = { delay, queue, task in + // Stub the implementation to save the token refresh task for later execution. + let authDispatcher = AuthDispatcher { delay, queue, task in XCTAssertNotNil(task) XCTAssertGreaterThan(delay, 0) XCTAssertEqual(kAuthGlobalWorkQueue, queue) self.authDispatcherCallback = task } + auth = Auth( + app: FirebaseApp.app(name: name)!, + keychainStorageProvider: keychainStorageProvider, + backend: authBackend, + authDispatcher: authDispatcher + ) // Wait until Auth initialization completes waitForAuthGlobalWorkQueueDrain() }