Jul 27 - Update code for scheduled tasks 4

This commit is contained in:
Nguyen Ngo
2026-07-27 16:43:42 -04:00
parent 4132373fee
commit 9926739cb6
9 changed files with 162 additions and 49 deletions
+42 -3
View File
@@ -128,7 +128,11 @@ struct JanitorialQCApp: App {
}
private func registerBackgroundTasks() {
BGTaskScheduler.shared.register(
// register() returns false when the identifier is not declared in
// BGTaskSchedulerPermittedIdentifiers or the `processing` background
// mode is missing. Both are easy to lose in a project settings change
// and the failure is otherwise completely silent, so it is logged.
let registered = BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.jqc.sync",
using: nil
) { task in
@@ -138,16 +142,44 @@ struct JanitorialQCApp: App {
}
handleBackgroundSync(task: processingTask)
}
if !registered {
print("[JQC] BGTaskScheduler.register FAILED for com.jqc.sync — "
+ "check UIBackgroundModes contains 'processing' and "
+ "BGTaskSchedulerPermittedIdentifiers contains com.jqc.sync")
}
}
private func handleBackgroundSync(task: BGProcessingTask) {
// Re-arm first: if anything below throws or the task is killed, a
// request is already queued for the next opportunity.
scheduleBackgroundSync()
let syncTask = Task {
let syncTask = Task { @MainActor in
// A BGTaskScheduler launch does not render ContentView, so the
// `.task { restoreSession() }` there never runs and
// AuthManager.isAuthenticated is still false. triggerSync() guards
// on it and would return having done nothing at all.
if !AuthManager.shared.isAuthenticated {
await AuthManager.shared.restoreSession()
}
// The SwiftData container is created by the `.modelContainer`
// scene modifier, so on a COLD background launch (process was
// terminated, no scene connected) there is no context to drain.
// The common case app suspended but still resident has one.
guard SyncManager.shared.modelContext != nil else {
print("[JQC] background sync skipped — no model context "
+ "(cold launch, no scene)")
return
}
await SyncManager.shared.triggerSync()
}
task.expirationHandler = {
syncTask.cancel()
}
Task {
await syncTask.value
task.setTaskCompleted(success: !syncTask.isCancelled)
@@ -178,5 +210,12 @@ func scheduleBackgroundSync() {
let request = BGProcessingTaskRequest(identifier: "com.jqc.sync")
request.requiresNetworkConnectivity = true
request.requiresExternalPower = false
try? BGTaskScheduler.shared.submit(request)
do {
try BGTaskScheduler.shared.submit(request)
} catch {
// Was `try?`. Submitting without the `processing` background mode fails
// with BGTaskSchedulerError.notPermitted, which is exactly how this
// whole path stayed dead unnoticed. Never swallow it again.
print("[JQC] BGTaskScheduler.submit failed: \(error)")
}
}