OK. Filed FB9017523.
Post
Replies
Boosts
Views
Activity
After throwing just about everything I could think of at the wall, I did end up making a change that seems to have totally stopped the crashing.
For the record, I have a thread going with DTS that seems skeptical of that change fixing anything, so please take this with a grain of salt. They're saying that it's likely to be the result of either concurrency violation or memory corruption, and recommending debugging the app with both the -com.apple.CoreData.ConcurrencyDebug 1 app argument and Xcode's Guard Malloc turned on.
That disclaimer aside, here's what happened with our app. We have a particular managed object subclass in our app with a couple of transient properties whose value are derived from another (persistent) property. In order to ensure that the transient values are reset when changes are merged from other contexts (often an update from our API), that subclass overrode -awakeFromSnapshotEvents: like so:
- (void)awakeFromSnapshotEvents:(NSSnapshotEventType)flags
{
[super awakeFromSnapshotEvents:flags];
[self setPrimitiveFoo:nil];
[self setPrimitiveBar:nil];
}
I was able to get a semi-reproducible case where I observed that merging the changes from the other context reset the affected objects, turning them back into faults (and resetting the transient properties). I added a basic conditional to that method to check whether the receiver was already a fault before clearing the transient properties' values.
- (void)awakeFromSnapshotEvents:(NSSnapshotEventType)flags
{
[super awakeFromSnapshotEvents:flags];
if( !self.isFault ) {
[self setPrimitiveFoo:nil];
[self setPrimitiveBar:nil];
}
}
I still don't fully understand why this change fixed anything, but we haven't seen the app crash trying to get a snapshot since. I confess I haven't had time yet to go back to the previous state and try running with Guard Malloc enabled to see whether that would shed more light on the issue.
I finally got a little time to try the suggestion from DTS: I reverted the changes that had appeared to fix the issue, turned on guard malloc, and ran the app again. I got it to crash in what seemed the same exact way it had been, with no apparent difference from having guard malloc active. I reported that to DTS, but haven't heard back.
Follow up: it appears that in the beta iOS 17 SDK, the UIContentView protocol, its members, and the method UIContentConfiguration.makeConfiguration() have all been marked with the @MainActor attribute.
For batch deletes, you're probably looking for ModelContext.delete(model:where:includesubentities:).
For individual objects, there's ModelContext.delete(_:).
Does it work if you don't try to set the book relationship in the Recipe initializer?
I think I remember seeing a post somewhere saying that both objects in the relationship need to be added to the context first. So rather than making that part of the Recipe initializer, you might need to do it in the method that creates the context and adds the objects. Maybe something like:
SampleData.books.forEach(container.mainContext.insert(object: ))
SampleData.tags.forEach(container.mainContext.insert(object: ))
SampleData.recipes.forEach { recipe in
container.mainContext.insert(object: recipe)
recipe.book = SampleData.books.first!
}
I'm fairly certain that SwiftData simply doesn't support inheritance (at least not in beta 1).
I posted about that a few days ago, and I've also filed FB12336064 asking for the feature to be added.
I think it would be useful to be able to see your model code, so the full sample would be nice.
Looking again at what you posted originally, I just noticed that your model container lists these types for the schema, which don't include Book: [Project.self, Recipe.self, Tag.self].
Perhaps ModelContext.object(with:) ?
Does it make any difference if you change this code:
let person = Person(name: "Willy")
person.group = group
ctx.insert(person)
to this?
let person = Person(name: "Willy")
ctx.insert(person)
person.group = group
That is, to insert both objects into the context before attempting to establish their relationship?
How are you creating your ModelContainer? Is there only one container in your app?
You said you reverted your model changes. Did you change any of those classes after saving that first item?
What have you tried? What issues have you encountered?
Generally speaking, the most straightforward way to back up a Core Data store is to make a copy of its file(s), which would then contain all of the data, including all of the relationships.
If you can explain a little more about your use case, that might also help to suggest a solution.
I've come back to my test a couple of times over the last two months, and I'm still seeing the same exact issue in the final released version of Xcode 15. I sure wish I knew whether this was actually a bug, or whether I'm using this (under-documented!) API incorrectly.
Because the only sample code I can find anywhere (from the WWDC presentation) uses NSPersistentContainer, I thought I'd try re-writing my tests to do the same, rather than creating the NSPersistentStoreCoordinator directly.
Unfortunately, while that initially appears to work — loadPersistentStores(_:) calls its completion handler with a nil error — it appears that it's failing silently somewhere along the way. The container's persistentStoreCoordinator.persistentStores is empty, and attempting to save changes to its viewContext throws "This NSPersistentStoreCoordinator has no persistent stores (unknown). It cannot perform a save operation."