NSConstraintConflicts occur on save even when merge policy is set

929 views Asked by At

I have a core data stack that only explicitly uses the main context, and only makes changes to/creates persistent objects within a context.performAndWait block. If I ever need a background context, I always use the following code block:

persistentContainer.performBackgroundTask()
{context in
    context.performAndWait
    {
        defer
        {
            context.save()
            persistentContainer.context.save()
        }
        //make changes here
    }
} 

The main context's merge policy is set on the creation of the persistentStore, to NSOverwriteMergePolicy, which, when I save the background context, I expected to overwrite any "old" objects on the main context with any "new" objects created in a background context. Instead what is happening is I'm getting a NSConstraintConflict error, because the background context uses the default merge policy (not sure why, I expected it to inherit the merge policy of the main context.) When I explicitly set the background context's merge policy, I end up with duplicates of objects that there should not be duplicates of according to the constraints I'm setting. In this case, the clientID attribute must be unique for any given Client entity.

I feel like I'm somehow not merging the contexts properly. The way I'm doing it now is just by saving the background context, and then the main context, more or less as shown above. But if I wasn't merging properly, why would a constraint error occur when there are objects created on a background context that have the same attributes as objects in the main context? And why would the duplicates be appearing when I do a fetch request on the main context?

I'm only about two months in on using core data and everything I know came from documentation or here, so I'm sure there is something I'm just ignorant about. Any ideas as to what I'm doing wrong?

1

There are 1 answers

0
A. L. Strine On

Turns out I had not set the constraint for the entity I was seeing duplicates of. Setting the constraint got rid of the duplicate objects.

To solve the issue with the NSConstraintConflict errors whenever I saved on the background contexts, I made all background contexts created with the function I was using have the same merge policy, using the following custom class:

class MergePolicedNSPersistentContainer: NSPersistentContainer
{
    override func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void)
    {
        super.performBackgroundTask()
        { context in
            context.mergePolicy = PersistenceManager.shared.mergePolicy
            //PersistenceManager is a custom singleton class where I do lots of custom coreData stuff, mostly sanity checks and locks. In this case I use it to store a merge policy instance to use here.
            block(context)
        }
    }
}

When initializing my data store in the custom PersistenceManager class:

lazy var persistentContainer: MergePolicedNSPersistentContainer = {

    let container = MergePolicedNSPersistentContainer(name: "MyAppDataModel")
    container.loadPersistentStores()
    { (storeDescription, error) in

        container.viewContext.mergePolicy = self.mergePolicy

        if let error = error as NSError?
        {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    }
    return container
}()