Skip to content

Commit 4ddd497

Browse files
authored
Merge pull request #3 from SF50-TOLD/worktree-agent-a7911074
Move NavDataLoader polling to background context (fixes app hang)
2 parents ecbe48c + 38dfa76 commit 4ddd497

1 file changed

Lines changed: 43 additions & 25 deletions

File tree

SF50 TOLD/Loaders/NavDataLoader/NavDataLoaderViewModel.swift

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,23 @@ final class NavDataLoaderViewModel: WithIdentifiableError {
7575
)
7676

7777
addTask(
78-
Task {
78+
Task.detached { [container] in
7979
do {
80-
let context = container.mainContext
81-
try setAnyAirports(context: context)
80+
let context = ModelContext(container)
8281
while !Task.isCancelled {
83-
try setAnyAirports(context: context)
82+
let state = try NavDataStateHelper.fetchState(context: context)
83+
await MainActor.run {
84+
self.applyState(state)
85+
}
8486
try? await Task.sleep(for: .seconds(0.5))
8587
}
8688
} catch {
87-
SentrySDK.capture(error: error) { scope in
88-
scope.setFingerprint(["navData", "airportCheck"])
89+
await MainActor.run {
90+
SentrySDK.capture(error: error) { scope in
91+
scope.setFingerprint(["navData", "airportCheck"])
92+
}
93+
self.error = error
8994
}
90-
self.error = error
9195
}
9296
}
9397
)
@@ -182,25 +186,40 @@ final class NavDataLoaderViewModel: WithIdentifiableError {
182186
Defaults[.ourAirportsLastUpdated] = nil
183187
}
184188

185-
private func outOfDate(expirationDate: Date?) -> Bool {
186-
guard let expirationDate else { return true }
187-
return Date() > expirationDate
189+
private func recalculate() throws {
190+
let state = try NavDataStateHelper.fetchState(context: container.mainContext)
191+
applyState(state)
188192
}
189193

190-
private func outOfDate(schemaVersion: Int) -> Bool {
191-
schemaVersion != latestSchemaVersion
194+
private func applyState(_ state: NavDataStateHelper.State) {
195+
if noData != state.noData { self.noData = state.noData }
196+
if needsLoad != state.needsLoad { self.needsLoad = state.needsLoad }
197+
if canSkip != state.canSkip { self.canSkip = state.canSkip }
192198
}
199+
}
193200

194-
private func recalculate() throws {
195-
let schemaOutOfDate = outOfDate(schemaVersion: Defaults[.schemaVersion])
196-
let nasrExpiration = try fetchNASRExpiration()
197-
let dataOutOfDate = outOfDate(expirationDate: nasrExpiration)
198-
needsLoad = schemaOutOfDate || dataOutOfDate
199-
canSkip = !noData && !schemaOutOfDate
201+
/// File-scope helper for computing nav-data loader state from any `ModelContext`.
202+
///
203+
/// Declared outside `NavDataLoaderViewModel` so it is nonisolated by default
204+
/// and callable from both MainActor and background tasks without annotations.
205+
private enum NavDataStateHelper {
206+
static func fetchState(context: ModelContext) throws -> State {
207+
var airportDescriptor = FetchDescriptor<SF50_Shared.Airport>()
208+
airportDescriptor.fetchLimit = 1
209+
let noData = try context.fetch(airportDescriptor).isEmpty
210+
211+
let schemaOutOfDate = Defaults[.schemaVersion] != latestSchemaVersion
212+
let nasrExpiration = try fetchNASRExpiration(context: context)
213+
let dataOutOfDate = nasrExpiration.map { Date() > $0 } ?? true
214+
215+
return State(
216+
noData: noData,
217+
needsLoad: schemaOutOfDate || dataOutOfDate,
218+
canSkip: !noData && !schemaOutOfDate
219+
)
200220
}
201221

202-
private func fetchNASRExpiration() throws -> Date? {
203-
let context = container.mainContext
222+
private static func fetchNASRExpiration(context: ModelContext) throws -> Date? {
204223
let nasrRawValue = CycleDataSource.nasr.rawValue
205224
var descriptor = FetchDescriptor<Cycle>(
206225
predicate: #Predicate { $0._dataSource == nasrRawValue }
@@ -209,10 +228,9 @@ final class NavDataLoaderViewModel: WithIdentifiableError {
209228
return try context.fetch(descriptor).first?.expires
210229
}
211230

212-
private func setAnyAirports(context: ModelContext) throws {
213-
var descriptor = FetchDescriptor<SF50_Shared.Airport>()
214-
descriptor.fetchLimit = 1
215-
noData = try context.fetch(descriptor).isEmpty
216-
try recalculate()
231+
struct State {
232+
let noData: Bool
233+
let needsLoad: Bool
234+
let canSkip: Bool
217235
}
218236
}

0 commit comments

Comments
 (0)