From 8131fe8658c1f91781070ab387684889e60f31b0 Mon Sep 17 00:00:00 2001 From: Tim Morgan Date: Tue, 23 Jun 2026 01:28:12 -0700 Subject: [PATCH] Stop NavDataLoaderViewModel polling once nav data is loaded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `statePollingTask` ran a 0.5s `while`/`sleep` loop for the entire app session, fetching the airport and NASR-cycle state twice a second on a background `ModelContext`. Because every context on the shared `ModelContainer` serializes on one persistent store coordinator, that perpetual poll contended with the main context's SwiftData faults — the dominant contributor to the launch-time `performBlockAndWait` main-thread hangs (SF50-TOLD-26/-20), which remain active on 3.5.6. Resolve the loader state once at launch and keep polling only while the loader is still needed (no data yet, or a download in progress). Once data is present and current the loader is dismissed and the poll stops, so a returning user — the common case — does zero background SwiftData work after launch. The existing schema-version observer continues to revive state when a reload becomes necessary. Refs SF50-TOLD-26, SF50-TOLD-20. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Loaders/NavDataLoader/NavDataLoaderViewModel.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/SF50 TOLD/Loaders/NavDataLoader/NavDataLoaderViewModel.swift b/SF50 TOLD/Loaders/NavDataLoader/NavDataLoaderViewModel.swift index 39fd4c7..0c1c146 100644 --- a/SF50 TOLD/Loaders/NavDataLoader/NavDataLoaderViewModel.swift +++ b/SF50 TOLD/Loaders/NavDataLoader/NavDataLoaderViewModel.swift @@ -86,9 +86,17 @@ final class NavDataLoaderViewModel: WithIdentifiableError { private func statePollingTask() -> Task { Task.detached { [weak self, container] in let context = ModelContext(container) + // Resolve the loader state at launch, then keep polling only while the + // loader is still needed (no data yet, or a download in progress). Once + // data is present and current the loader is dismissed and the poll + // stops, so it never contends with the main context's SwiftData access + // for the lifetime of the app — the dominant contributor to the + // launch-time main-thread hangs. The schema-version observer revives + // state when a reload becomes necessary. while !Task.isCancelled { guard let self else { return } await self.refreshState(from: context, fingerprint: "airportCheck") + guard await self.showLoader else { return } try? await Task.sleep(for: .seconds(0.5)) } }