My App keeps crashing in the background and I don't know why. I'm using SwiftData and SwiftUI. I'm setting up a .backgroundTask
like this:
import SwiftUI
import SwiftData
import TipKit
@main
struct MyAppName: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@State var navManager = NavigationManager.load()
@State var alerter: Alerter = Alerter()
var sharedModelContainer: ModelContainer = {
do {
return try ModelContainer(for: DataController.schema, configurations: [DataController.modelConfig])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
SetupView()
.accentColor(.myAccentColor)
.environment(alerter)
.alert(isPresented: $alerter.isShowingAlert) {
alerter.alert ?? Alert(title: Text(verbatim: ""))
}
.task {
try? Tips.configure([
.displayFrequency(.immediate),
.datastoreLocation(.applicationDefault)
])
}
.onAppear {
setUpAppDelegate()
}
}
.modelContainer(sharedModelContainer)
.backgroundTask(.appRefresh(Const.backgroundAppRefreshId)) { @MainActor in
let container = sharedModelContainer
await RefreshManager.handleBackgroundVideoRefresh(container)
}
.environment(navManager)
}
func setUpAppDelegate() {
appDelegate.navManager = navManager
}
}
The RefreshManager.handleBackgroundRefresh(...)
goes on to load data and then insert models for them via SwiftData. It only happens occasionally and afaik only in the background. Weirdly enough the issue seems to be there even when I only print something in the background task. Even when I don't schedule/set a background task at all. How can that be? The crashes started in the version that included the .backgroundTask, although perhaps it's related to something else. I'm still trying to further narrow it down.
This is the crash report that I'm getting:
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x00000001a1bb98c0
Termination Reason: SIGNAL 5 Trace/BPT trap: 5
Terminating Process: exc handler [809]
Triggered by Thread: 0
Thread 0 name:
Thread 0 Crashed:
0 libswiftCore.dylib 0x00000001a1bb98c0 _assertionFailure(_:_:file:line:flags:) + 264 (AssertCommon.swift:144)
1 libswiftCore.dylib 0x00000001a1c27d14 swift_unexpectedError + 664 (ErrorType.swift:188)
2 _SwiftData_SwiftUI 0x000000024310cd78 one-time initialization function for empty + 300 (ModelContainer+Extensions.swift:5)
3 libdispatch.dylib 0x00000001ab16add4 _dispatch_client_callout + 20 (object.m:576)
4 libdispatch.dylib 0x00000001ab16c654 _dispatch_once_callout + 32 (once.c:52)
5 _SwiftData_SwiftUI 0x000000024310cdf8 one-time initialization function for empty + 124 (ModelContainer+Extensions.swift:12)
6 libdispatch.dylib 0x00000001ab16add4 _dispatch_client_callout + 20 (object.m:576)
7 libdispatch.dylib 0x00000001ab16c654 _dispatch_once_callout + 32 (once.c:52)
8 _SwiftData_SwiftUI 0x0000000243122170 key path getter for EnvironmentValues.modelContext : EnvironmentValues + 140 (<compiler-generated>:0)
9 libswiftCore.dylib 0x00000001a1ce4628 RawKeyPathComponent._projectReadOnly<A, B, C>(_:to:endingWith:) + 1012 (KeyPath.swift:1701)
10 libswiftCore.dylib 0x00000001a1ce3ddc KeyPath._projectReadOnly(from:) + 1036 (KeyPath.swift:331)
11 libswiftCore.dylib 0x00000001a1ce8348 swift_getAtKeyPath + 24 (KeyPath.swift:2029)
12 SwiftUI 0x00000001a7af4814 EnvironmentBox.update(property:phase:) + 872 (Environment.swift:273)
13 SwiftUI 0x00000001a782a074 static BoxVTable.update(ptr:property:phase:) + 396 (DynamicPropertyBuffer.swift:294)
14 SwiftUI 0x00000001a78297b0 _DynamicPropertyBuffer.update(container:phase:) + 104 (DynamicPropertyBuffer.swift:215)
15 SwiftUI 0x00000001a887fb78 closure #1 in closure #1 in DynamicBody.updateValue() + 104 (DynamicProperty.swift:447)
16 SwiftUI 0x00000001a887fbb8 partial apply for closure #1 in closure #1 in DynamicBody.updateValue() + 28 (<compiler-generated>:0)
17 libswiftCore.dylib 0x00000001a1bcc068 withUnsafeMutablePointer<A, B>(to:_:) + 28 (LifetimeManager.swift:82)
18 SwiftUI 0x00000001a887f9dc closure #1 in DynamicBody.updateValue() + 408 (DynamicProperty.swift:446)
19 SwiftUI 0x00000001a887f5c0 DynamicBody.updateValue() + 712 (DynamicProperty.swift:445)
20 SwiftUI 0x00000001a71e8bf8 partial apply for implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 32 (<compiler-generated>:0)
21 AttributeGraph 0x00000001cbd4c240 AG::Graph::UpdateStack::update() + 512 (ag-graph-update.cc:578)
22 AttributeGraph 0x00000001cbd42f38 AG::Graph::update_attribute(AG::data::ptr<AG::Node>, unsigned int) + 424 (ag-graph-update.cc:719)
23 AttributeGraph 0x00000001cbd42810 AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>, AG::AttributeID, unsigned int, unsigned int, AGSwiftMetadata const*, unsigned char&, long) + 720 (ag-graph.cc:1429)
24 AttributeGraph 0x00000001cbd423a4 AGGraphGetValue + 228 (AGGraph.mm:701)
25 SwiftUI 0x00000001a887f548 DynamicBody.updateValue() + 592 (DynamicProperty.swift:444)
26 SwiftUI 0x00000001a71e8bf8 partial apply for implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 32 (<compiler-generated>:0)
27 AttributeGraph 0x00000001cbd4c240 AG::Graph::UpdateStack::update() + 512 (ag-graph-update.cc:578)
28 AttributeGraph 0x00000001cbd42f38 AG::Graph::update_attribute(AG::data::ptr<AG::Node>, unsigned int) + 424 (ag-graph-update.cc:719)
[...]
107 SwiftUI 0x00000001a7cf79fc static App.main() + 132 (App.swift:114)
108 MyAppName 0x0000000100e6d120 static MyAppNameApp.$main() + 52 (MyAppNameApp.swift:0)
109 MyAppName 0x0000000100e6d120 main + 64
110 dyld 0x00000001c67c2d84 start + 2240 (dyldMain.cpp:1298)
The report also says key path getter for EnvironmentValues.modelContext
, which seems odd. Any idea where I could start to look for the issue? I'm currently just trying things out, pushing them to TestFlight and waiting for crashes to happen. As soon as I can narrow it down further I'll update this.
I think I finally figgured it out!
This Thread discusses a similar issue, it seems to be a SwiftUI bug. When the App launches in the background from being inactive, it’s crashing when an @Environment
variable is used in the root ContentView
(only seems to happen for .modelContext
for me).
I was able to reliably reproduce it by launching the App in the background directly.
This is what I was doing:
@main
struct MyApp: App {
var sharedModelContainer: ModelContainer = {
// ...
}()
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
}
struct ContentView: View {
@Environment(\.modelContext) var modelContext
var body: some View {
MyView()
}
func someFunc() {
modelContext.insert(...)
}
}
I’m now passing the container
to the root view as an argument and adding it there via .modelContainer(...)
, which seems to have fixed the issue:
@main
struct MyApp: App {
var sharedModelContainer: ModelContainer = {
// ...
}()
var body: some Scene {
WindowGroup {
ContentView(container: sharedModelContainer)
}
}
}
struct ContentView: View {
let container: ModelContainer
var body: some View {
MyView()
.modelContainer(container)
}
func someFunc() {
let modelContext = ModelContext(container)
modelContext.insert(...)
}
}