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 one B.
  • Give each a property, such as name.
  1. Add a one-to-one relationship between the two.
  2. Generate the class file for each by any method you wish. Doesn't matter which.
  3. 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 their B'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.
  1. Every time you run this code, you'll see more items in the output.
  2. To prevent all these extra duplicates, let's give them a unique constraint, to both the A and B 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!
  1. 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.