Await the session restore instead of polling for it

Replace the 100ms polling loop in the background app refresh with a
subject that the state machine's transition handler sets as soon as the
app is no longer restoring a session, whatever the outcome. The refresh
now awaits it once, with the same 5 second timeout, instead of waking
up repeatedly.

Move the wait back into the class body and silence type_body_length for
AppCoordinator rather than keeping a single-function extension around
to stay under the limit.
This commit is contained in:
Stefan Ceriu
2026-09-30 15:01:38 +03:00
parent a56a5c2819
commit 11dc19f7ed
@@ -15,6 +15,7 @@ import Sentry
import SwiftUI
import Version
// swiftlint:disable:next type_body_length
class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDelegate, NotificationManagerDelegate, SecureWindowManagerDelegate {
private let stateMachine: AppCoordinatorStateMachine
private let navigationRootCoordinator: NavigationRootCoordinator
@@ -60,6 +61,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
private var softLogoutCoordinator: SoftLogoutScreenCoordinator?
private var appDelegateObserver: AnyCancellable?
private var userSessionObserver: AnyCancellable?
/// Becomes `true` once the app is no longer waiting on a session restore, whatever the outcome.
private let isSessionRestoredSubject = CurrentValueSubject<Bool, Never>(false)
private var clientProxyObserver: AnyCancellable?
private var cancellables = Set<AnyCancellable>()
@@ -618,6 +621,10 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
stateMachine.addTransitionHandler { [weak self] context in
guard let self else { return }
if context.toState != .restoringSession {
isSessionRestoredSubject.send(true)
}
switch (context.fromState, context.event, context.toState) {
case (.initial, .startWithAuthentication, .signedOut):
startAuthentication()
@@ -1336,6 +1343,13 @@ private extension AppCoordinator {
}
}
}
/// When iOS relaunches a terminated app for a background task, the session is still being restored asynchronously.
private func waitForSessionRestore() async {
for await isSessionRestored in isSessionRestoredSubject.timeout(.seconds(5), scheduler: DispatchQueue.main).values where isSessionRestored {
return
}
}
}
// MARK: Search backfill
@@ -1397,15 +1411,3 @@ private extension AppCoordinator {
task.setTaskCompleted(success: true)
}
}
private extension AppCoordinator {
/// When iOS relaunches a terminated app for a background task, the session is still being restored asynchronously.
func waitForSessionRestore() async {
let deadline = ContinuousClock.now + .seconds(5)
while userSession == nil,
stateMachine.state == .initial || stateMachine.state == .restoringSession,
ContinuousClock.now < deadline {
try? await Task.sleep(for: .milliseconds(100))
}
}
}