Core Data unique constraints
May 05, 2017 -There's a bug with Core Data such that if you have two entities, A
and B
, with a one-to-one relationship between them, that relationship will not be setup properly. This bug doesn't care wether you do your work in Objective-C or Swift or both.
The steps to reproduce are as follows:
- Create two entities, one
A
and oneB
. - Give each a property, such as
name
.
- Add a one-to-one relationship between the two.
- Generate the class file for each by any method you wish. Doesn't matter which.
- In your app, create an instance of each and link them by their relationship property.
let context = persistentContainer.viewContext
let a = A(context: context)
a.name = "I'm alive"
let b = B(context: context)
b.name = "I'm also alive"
a.thing = b
try! context.save()
- Fetch all
A
entities and print out theirB
's. Do the same the other away around too.
let context = container.viewContext
let request = A.fetchRequest() as NSFetchRequest<A>
let allA = try! context.fetch(request)
print(allA.map({ $0.thing?.name }))
let bRequest = B.fetchRequest() as NSFetchRequest<B>
let allB = try! context.fetch(bRequest)
print(allB.map({ $0.otherThing?.name }))
- Notice how nothing is nil, as you'd expect, in the console output. This is what we expect.
- Every time you run this code, you'll see more items in the output.
- To prevent all these extra duplicates, let's give them a unique constraint, to both the
A
andB
entities. For this to work, you'll also need to change the merge policy of the context.
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
- Now, notice how it's broken. Your entity properties will show as nil even though we know it should work just fine. This is the bug!
- To make things work again, remove the unique constraint on
B
. Now everything works just fine. Tada!
There's an example project displaying this bug on github.