Synchronizing Main and Background Core Data Threads (Swift 3)

Let’s say we have two different managedObjectContext (with one persistentStoreCoordinator). 

The first one is used across the app for most quick fetches:

var mainManagedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.mainQueueConcurrencyType)

 

        mainManagedObjectContext.persistentStoreCoordinator = coordinator

 

 

And the second, running in the background, for the queries that take a long time:

var backgroundManagedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.privateQueueConcurrencyType)

        backgroundManagedObjectContext.persistentStoreCoordinator = coordinator

 

When they are initialized both contain the same data. However once changes are applied to one of them -in this example the main- like adding, changing or deleting rows, the other will not know -in this example the background/private one-.

 

Screen Shot 2016 11 27 at 13 19 12

 

Therefore if we add objects to the main managedObjectContext, when we fetch the backgroundManagedObjectContext we will not get them. 

How do we fix this? Well first we have to save the changes of the objectContext that was modified. We do so by adding once we’ve finalized the changes:

       //Commit changes

        do {

            try managedObjectContext.save()

        }catch{

            print(“ERROR: Cound not save Core Data”)

        }

        

At this point we have saved the changes in the Persistent Store. We still have to ask the backgroundObjectContext to get them from the persistent store thought. For that we will use the automatic notification “NSManagedObjectContextDidSave”.

In the class where the backgroundThread is contained we will add an observer for the notification (note that we’re passing as object the context that was modified):

//Handle changes in core data for background thread

        NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.handleMainCoreDataChangeInBackgroundManagedContext(notification:)), name: NSNotification.Name.NSManagedObjectContextDidSave, object: managedObjectContext)

    }

And then we will add the function that will syncronize the backgroundThread, in this example: 

func handleMainCoreDataChangeInBackgroundManagedContext(notification: Notification){

        managedObjectContextBackgroundThread?.mergeChanges(fromContextDidSave: notification)

    }

That’s it! Now when you query the background thread you’ll get the same information as you have in the main one. 

You can read more about NSManagedObjectContext in Apple Docs

 

Questions / comments? I’m at @MarcMasVi 

 

Marc

 

 

Note: using two contexts is very powerful when used in combination with dispatchqueues

       DispatchQueue.global().async {

            //Query here in background context & save if applicable

            DispatchQueue.main.async {

                //Refresh UI & query main context & save if applicable

            }

        }