Search code examples
ioscore-dataicloud

CoreData (+ iCloud) produces invalid model state


I'm using CoreData with iCloud to sync data between devices.

After every iCloud import (observing NSPersistentStoreDidImportUbiquitousContentChangesNotification), I run a simple de-duplication algorithm to find and delete duplicated data. After saving the changes I see warnings in the console from CoreData that all properties and relationships of a specific entity instance (which was deleted by the de-dupe algorithm) are replaced with nil/0.

CoreData: warning: An NSManagedObjectContext delegate overrode fault handling behavior to silently delete the object with ID '0xd000000000040006 <x-coredata://ADDDABCD-4891-4DCF-B55B-53AA64D11922/<ENTITY_NAME>/p1>' and substitute nil/0 for all property values instead of throwing.

The problem is that one relationship in this entity is not-optional which produces errors the next time iCloud wants to import those changes on other devices.

-[_PFUbiquityRecordsImporter operation:failedWithError:](979): CoreData: Ubiquity:  Import operation encountered had trouble importing log file, Error Domain=NSCocoaErrorDomain Code=134302 "The operation couldn’t be completed. (Cocoa error 134302.)" 
[...], an error occurred saving changes to the persistent store mutated during the import process. [...] 
"The operation couldn’t be completed. (Cocoa error 1560.)"} User Info: { [...] 
NSValidationErrorObject=<NSManagedObject: 0x1742c7bd0> (entity: <ENTITY_NAME>; [...] 
{NSValidationErrorKey=<NON-OPTIONAL_RELATIONSHIP_NAME> [...] 
"Error encountered while importing transaction log at URL: ...

How can I avoid that all properties are set to nil/0?


Solution

  • How I solved it.

    My biggest mistake was how I handled the NSPersistentStoreDidImportUbiquitousContentChangesNotification notification. I only merged the changes in my "main" context (used in the main thread). But I forgot to merge the changes into my second "root" context (of type NSPrivateQueueConcurrencyType) as well which I use to save the context into the persistent store and is the parent of the "main" context.

    Without knowing the internals, I suspect that because my "root" context don't know about the ubiquitous content changes, the next time I save changes the resulting transaction logs (written to iCloud) were inconcistent. Now I merge the change notification into my "root" and "main" context and things start to work better.