My app has a reproducible CoreData error.
I use the viewContext
for display, and a backgroundContext
for object updates. Both contexts belong to the same NSPersistentCloudKitContainer
.
At some point, I save in the backgroundContext
an object after updating its status
attribute from 2 to 1, and its updatedAt
attribute from nil
to Date()
.
Later, I want to fetch back this updated object, and my understanding is that a fetch always returns the content of the persistent store.
Thus, the fetched object should be the same regardless into which context it is fetched. However, this is not the case.
I have also set -com.apple.CoreData.ConcurrencyDebug 1
as a launch argument, so this is not a CoreData multithreading error.
Here is my test code:
The object is saved here:
let context = backgroundContext!
context.performAndWait {
assert(ItemStatus(rawValue: item.status) == .isBought)
item.status = ItemStatus.isToBuy.rawValue
item.updatedAt = Date()
_ = saveContext(context)
}
with
func saveContext(_ context: NSManagedObjectContext) -> Error? {
if !context.hasChanges { return nil }
let inserts = context.insertedObjects; if !inserts.isEmpty { print("Will save inserted objects: (inserts)") }
let updates = context.updatedObjects; if !updates.isEmpty { print("Will save updated objects: (updates)") }
let deletes = context.deletedObjects; if !deletes.isEmpty { print("Will save deleted objects: (deletes)") }
do {
try context.save()
print("(context.name!) saved")
} catch {
fatalError("Unresolved error")
}
return nil
}
Later, I fetch the object into both contexts using:
let mwFetchRequest = NSFetchRequest<Item>(entityName: Item.entityName)
let passwordPredicate = NSPredicate(format: "(Schema.Item.password) == %@", password)
let namePredicate = NSPredicate(format: "(Schema.Item.name) == %@", "Mineral water")
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [passwordPredicate, namePredicate])
mwFetchRequest.predicate = compoundPredicate
mwFetchRequest.returnsObjectsAsFaults = false
backgroundContext.performAndWait {
let bcItem = try! backgroundContext.fetch(mwFetchRequest)
print("backgroundContext: (bcItem)")
}
viewContext.performAndWait {
let vcItem = try! viewContext.fetch(mwFetchRequest)
print(?viewContext: (vcItem)")
}
And here is the log when I set a breakpoint after this code:
Will save updated objects: [<ShopEasy.Item: 0x600000d7cf50> (entity: Item; id: 0x9698d776a7665623 <x-coredata://35BF43D6-4CF7-490D-B944-9DDFF2823AA1/Item/p1617>; data: {
buyPlaces = "<relationship fault: 0x600002e296a0 'buyPlaces'>";
fixedAtTopAt = nil;
howOftenBought = 1;
lastBoughtDate = "2021-01-24 13:02:09 +0000";
name = "Mineral water";
password = "PW_1";
status = 1;
updatedAt = "2021-01-24 13:32:14 +0000";
})]
backgroundContext saved
…
backgroundContext: [<ShopEasy.Item: 0x600000d7cf50> (entity: Item; id: 0x9698d776a7665623 <x-coredata://35BF43D6-4CF7-490D-B944-9DDFF2823AA1/Item/p1617>; data: {
buyPlaces = "<relationship fault: 0x600002e296a0 'buyPlaces'>";
fixedAtTopAt = nil;
howOftenBought = 1;
lastBoughtDate = "2021-01-24 13:02:09 +0000";
name = "Mineral water";
password = "PW_1";
status = 1;
updatedAt = "2021-01-24 13:32:14 +0000";
})]
viewContext: [<ShopEasy.Item: 0x600000d75ae0> (entity: Item; id: 0x9698d776a7665623 <x-coredata://35BF43D6-4CF7-490D-B944-9DDFF2823AA1/Item/p1617>; data: {
buyPlaces = (
"0x9698d776b10e5621 <x-coredata://35BF43D6-4CF7-490D-B944-9DDFF2823AA1/Place/p971>"
);
fixedAtTopAt = nil;
howOftenBought = 1;
lastBoughtDate = "2021-01-24 13:02:09 +0000";
name = "Mineral water";
password = "PW_1";
status = 2;
updatedAt = nil;
})]
Obviously, the object is first correctly saved using the backgroundContext
, and should thus be in the persistent store.
It is then fetched back correctly into the backgroundContext
.
But after fetching the same object into the viewContext
, the two changed attributes, status and updatedAt, have the values as they were before the save.
My questions:
Are my assumptions wrong? Is something wrong with my code?
question from:
https://stackoverflow.com/questions/65871404/nsmanagedobject-fetched-into-2-different-contexts-has-different-attribute-values 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…