I'm introducing Unique Constraints to an existing app, in a new model version.
The model has entity Person which has an optional one-to-one relationship with entity Pet. Person's delete rule is cascade, and Pet's inverse relationship delete rule is nullify. There's another entity, Job, which Person also has a to-one relationship. But that deletion rule, and the inverse deletion rule, are both nullify.
Person has a "personID" property which I've made unique.
In a main queue context I've got an instance of Person, with ID xxx, that has no Pet set. Let's call that person "Charlie". I create a Job as well, and set that as "iOS Dev", and assign it to Charlie's job property.
Then in a child private queue context, I insert a new "Charlie" also with ID xxx (the unique constraint) and create a Pet and set it. I don't set a Job. I save the child context with no issue.
Immediately after, I attempt to save the Main Queue context. But just before that, I inspect the insertedObjects property of the Main Queue context and see, as I expect to, an instance of Person with ID xxx. What I'm expecting is that this second instance will be handled by unique constraints and be "merged" with the original instance of Person from the prior paragraph. But, when I actually call the MOC's save: method, it throws an exception and I inspect the NSError object:
Error Domain=NSCocoaErrorDomain Code=1550 "The operation couldn’t be completed. (Cocoa error 1550.)" UserInfo={Dangling reference to an invalid object.=null
Any ideas on how to avoid the exception? I'm used to "Dangling references" in Core Data meaning a relationship is misconfigured, but I've gone over it a number of times and it looks fine, so I'm not sure what else to troubleshoot. I have all contexts involved using the NSMergeByPropertyObjectTrumpMergePolicy merge policy.
Obviously this is a little hard to follow, so I threw a sample project up on Github in case anybody is interested: https://github.com/bpapa/core-data-unique-bug
I wound up burning a Code-Level Support ticket on this, and an Apple engineer confirmed that there is a bug. As a workaround, it was recommended to write my own merge policy that calls super and then manually makes sure the relationship is set on both ends.
If you want to employ the new iOS 9 Unique Constrains strategy, you have to make sure that both contexts have the same merge policy set.
Note that this will not help you if you have a scenario where the newer instance of the object has an attribute/relationship that is nil
and your intention is to always "augment" the object, i.e. fill any missing attributes and update existing ones.
Otherwise, for pre-iOS9, the following is valid.
Perhaps there is some misunderstanding about the meaning of the merge policy you employ (NSMergeByPropertyObjectTrumpMergePolicy
). It refers to a situation where you have one version of an object in memory and another in the persistent store, not two versions in memory in different contexts. From the header files:
This singleton policy merges conflicts between the persistent store's version of the object and the current in memory version.
Thus, your process of creating new object is not the correct approach.
Instead, you should fetch the Person
in the child context using the unique ID.
Alternatively, you could make the person's objectID
(an opaque Core Data artifact) available to the child context to get a reference to the same object in the object graph using objectWithID:
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With