Persist SwiftData within a background task on suspension (0xdead10cc)

On backgrounding, the app flushed pending CloudKit changes in an unguarded Task
while SwiftData autosave could leave an open SQLite transaction. iOS terminates
apps that hold the database lock across suspension (0xdead10cc), which was the
largest crash group on build 261. On iOS, commit pending changes synchronously
to release the lock, and run the async flush inside a beginBackgroundTask
assertion that ends when the flush completes.

Fixes 0xdead10cc SIGKILLs seen in TestFlight build 261 (17 reports).
This commit is contained in:
Arkadiusz Fal
2026-05-13 22:05:04 +02:00
parent ba33823048
commit 579df26284

View File

@@ -306,14 +306,38 @@ struct YatteeApp: App {
// Flush pending CloudKit changes when entering background // Flush pending CloudKit changes when entering background
if newPhase == .background { if newPhase == .background {
appEnvironment.cloudKitSync.stopForegroundPolling() appEnvironment.cloudKitSync.stopForegroundPolling()
Task {
await appEnvironment.cloudKitSync.flushPendingChanges()
}
#if os(iOS) #if os(iOS)
// Persist pending SwiftData changes and run the CloudKit flush
// inside a background-task assertion. iOS terminates apps with
// 0xdead10cc when they hold the SwiftData/SQLite database lock
// across suspension; committing now releases the lock, and the
// assertion keeps the app alive until the async flush finishes.
var backgroundTask: UIBackgroundTaskIdentifier = .invalid
backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "PersistOnBackground") {
if backgroundTask != .invalid {
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
}
// Commit any pending changes synchronously so no open SQLite
// transaction is left behind at suspension.
appEnvironment.dataManager.save()
Task {
await appEnvironment.cloudKitSync.flushPendingChanges()
if backgroundTask != .invalid {
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
}
if appEnvironment.settingsManager.backgroundNotificationsEnabled { if appEnvironment.settingsManager.backgroundNotificationsEnabled {
appEnvironment.backgroundRefreshManager.scheduleIOSBackgroundRefresh() appEnvironment.backgroundRefreshManager.scheduleIOSBackgroundRefresh()
} }
#else
Task {
await appEnvironment.cloudKitSync.flushPendingChanges()
}
#endif #endif
} }
} }