Start the app coordinator from background tasks

A background launch doesn't always connect a scene, so nothing started
the session restore and both background app refresh and search backfill
gave up without a session.

Store optimizations are skipped while in the background, as vacuuming can
outlast the task and get the app killed before the remaining migrations run.
This commit is contained in:
Stefan Ceriu
2026-10-02 17:31:46 +03:00
parent f2eaa8e80d
commit dafda65cbe
@@ -213,7 +213,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
func start() {
guard stateMachine.state == .initial else {
MXLog.error("Received a start request when already started")
MXLog.info("Received a start request when already started")
return
}
@@ -486,9 +486,12 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
MXLog.info("Migrating user session from \(oldVersion)")
MXLog.info("Performing client store optimizations.")
await userSession.clientProxy.optimizeStores()
MXLog.info("Finished optimizing client stores.")
// Vacuuming can outlast a background task, getting the app killed before the migrations below run.
if appMediator.appState != .background {
MXLog.info("Performing client store optimizations.")
await userSession.clientProxy.optimizeStores()
MXLog.info("Finished optimizing client stores.")
}
if oldVersion < Version(1, 6, 0) {
MXLog.info("Migrating to v1.6.0, marking identity confirmation onboarding as ran.")
@@ -1348,8 +1351,13 @@ private extension AppCoordinator {
}
}
/// When iOS relaunches a terminated app for a background task, the session is still being restored asynchronously.
/// When iOS relaunches a terminated app for a background task, the session needs restoring before the task can run.
private func waitForSessionRestore() async {
// A background launch doesn't necessarily connect a scene, which is what normally calls `start()`.
if stateMachine.state == .initial, userSessionStore.hasSessions {
start()
}
for await isSessionRestored in isSessionRestoredSubject.timeout(.seconds(5), scheduler: DispatchQueue.main).values where isSessionRestored {
return
}
@@ -1394,6 +1402,8 @@ private extension AppCoordinator {
func handleSearchBackfill(_ task: BGProcessingTask) async {
MXLog.info("Started search backfill task")
await waitForSessionRestore()
guard let clientProxy = userSession?.clientProxy else {
task.setTaskCompleted(success: false)
return