Hi everyone! Stumbled upon the same issue recently. When trying to delete an object from its DetailsPage - getting a crash "Fatal error: Context is missing for"
It seems to be very much related to what properties of the object are being used on the View that suppose to disappear after deletion. DetailsPage in my case, but it can be a ListItemView just as well.
What I figured, that there are 3 things that together lead to this crash:
The View that "holds" the object must use some of the object's relationship(s) propertiers. For example, count of children objects, or the name of the parent object, etc.
There must be try context.save() after deletion of model object.
It must be iOS 18. Yes, this behavior is new. More on that later.
Test setup
Is quite simple. You can download SampleTrips SwiftData app from AppleDeveloper
Navigate to BucketListView.swift and modify a little a struct BucketListItemToggle so that it displays some of the item's parent values.
In order to see the crash in action easier, I'd also suggest to make bucket-list-item deletion upon button press (not swipe action).
With swipe-action, crash is also reproducible - just add try? modelContext.save() to deleteItems(at:).
The crash will happen not exactly after deletion, but after subsequent addition of new bucket-list-item. This is probably related to the way how swipeAction removes the view from hierarchy.
Run on iOS18 device OR simulator. Create a Trip, go to Trip details > View Bucket List > Create a BucketListItem (feel free to create several) > Press on any item. Observe the crash.
Note: with this demo project setup, crash happens in about 80% cases when deleting an item. Suspicion is that the more "relational-properties" the child view consumes - the higher chance for a crash.
PS: I've also added Text("\(item.persistentModelID)") to the item row, just to see that the crash indeed happens because of the view being deleted.
Explanation (at least an attempt)
As you can see, crash happens upon trying to access trip? property of the item. This is a "relational" (parent) property of the BucketListItem model.
👉 Seems like iOS18 started to be more strict when accessing parent/children properties when the object itself was removed from the context.
Indeed, if we do po item.modelContext in that breakpoint - we will see that it is nil. In previous iOS versions, it would just fallback and return nil when asking for such trip? property. But on iOS18 - it seems to try to unfault a relation, sees that context is missing and throws a fatal error...
Now, to the question why the view which should disappear is trying to refresh? Here is just my assumption based on observation of Self._printChanges(). Basically that is how SwiftUI always worked. For one reason or another, it recognized a change and needed to refresh the view before parent view recognized that this view should no longer exist in the first place...
Possible solutions
There are some options that I guess one can try:
Consider removing context.save(). If you have a context with autosaveEnabled -- forcing it to save might not be necessary. This seems to give SwiftUI some time to refresh the "view-being-deleted" without an issue. Then parent view will recognise the change and remove the child view completely as expected.
Don't use "relational-properties" in child views. This is much easier said than done. You can pass all relevant values "from above" and make you child views as discrete as possible.
Add if object.modelContext == nil { EmptyView() } else { ... } in bodies of your views which object can get removed.
Simulate fallback to pre-iOS18 by writing safe getters and use them in views to read data from relationships. E.g. var safeTrip: Trip? { guard modelContext != nil else { return nil }; return trip }
For my project, I was lucky with being able to use option 1. But I'm considering adding safe getters anyway just in case 🫣
Would be awesome to hear from Apple if our observations are correct or not 🤞
Thank you all & have fun coding 🙂