Importing Data into Core Data on a Background Queue

Importing a large amount of data into Core Data can be very time consuming and can leave your application unusable during the import unless you run the import in the background. Here is how to do just that.

In this demonstration, the import is going to be run on a background queue using Grand Central Dispatch. Core Data requires that each queue operates its own separate Managed Object Context, which is created on the queue to which it will belong. As data is imported on this Managed Object Context, it will be merged into the main queue's Managed Object Context, which will update the user interface and save to disk.

First create a background queue using Grand Central Dispatch.

@implementation AppDelegate {
  dispatch_queue_t _backgroundQueue;
}

- (id)init {
  self = [super init];

  if (self) {
    _backgroundQueue = dispatch_queue_create("com.briankoponen.background", NULL);
  }

  return self;
}

- (void)dealloc {
  dispatch_release(_backgroundQueue);
}

This will create a serial background queue. And don't forget to release the dispatch queue in dealloc.

In your import method, you will have this queue create a Managed Object Context and import data into it.

- (void)importData {
  __block NSManagedObjectContext *backgroundMOC = nil;

  dispatch_async(_backgroundQueue, ^{
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (!coordinator) {
      // Error if we don't have a coordinator.
      return;
    }

    // Create the Background Managed Object Context
    backgroundMOC = [[NSManagedObjectContext alloc] init];
    [backgroundMOC setPersistentStoreCoordinator:coordinator];
    [backgroundMOC setUndoManager:nil];

    // Notify the main queue of changes the background queue makes
    [[NSNotificationCenter defaultCenter]
      addObserverForName:NSManagedObjectContextDidSaveNotification
                  object:backgroundMOC
                   queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
                     dispatch_async(dispatch_get_main_queue(), ^{
                       [__managedObjectContext mergeChangesFromContextDidSaveNotification:note];
                     });
                   }];

     // Import Data into backgroundMOC
  };
}

To create the background Managed Object Context, give it the same Persistent Store Coordinator as the main queue's Managed Object Context. Since it will only be importing data, disable the Undo Manager to speed up the process.

Using this notification, when the background Managed Object Context saves, the changes are sent to the main queue and merged in. This updates the UI and can be saved to disk. The import process will happen in the background, updating the UI whenever it saves. While importing the data, periodically call [backgroundMOC save:] to update the UI. You can tune this to your application, but set it up so that after every 1000 records (or whatever) the backgroundMOC saves and updates the UI.

It is so easy to run a Managed Object Context in the background, you may find that there are many other areas of your application where you can do something similar, taking advantage of the multiple cores available and freeing up your main queue, so your application never freezes while doing its work.

For more information about importing data into Core Data, see the Core Data Programming Guide: Efficiently Importing Data.

Question or Comment?