Core Data background fetching via new NSPrivateQueueConcurrencyType
Core DataIos5Core Data Problem Overview
Is it really so simple now in iOS5?
I used to perform a background fetch using this code in my AppDelegate:
dispatch_queue_t downloadQueue = dispatch_queue_create("DownloadQueue", NULL);
dispatch_async(downloadQueue, ^{
self.myDownloadClass = [[MyDownloadClass alloc]initInManagedObjectContext:self.managedObjectContext];
[self.myDownloadClass download];
});
dispatch_release(downloadQueue);
My download class performs an NSURLConnection to fetch some XML data, uses NSXMLParser to parse the data, and then updates a complex schema in core data. I would always switch to the main thread to actually update the core data. Messy code, with lots of calls to dispatch_sync(dispatch_get_main_queue()....
My new code looks like this:
NSManagedObjectContext *child = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[child setParentContext:self.managedObjectContext];
[child performBlock:^{
self.myDownloadClass = [[MyDownloadClass alloc]initInManagedObjectContext:child];
[self.myDownloadClass download];
}];
along with a small change to some other code in my AppDelegate to set the parent model object context type to NSMainQueueConcurrencyType:
- (NSManagedObjectContext *)managedObjectContext
{
if (__managedObjectContext != nil)
{
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return __managedObjectContext;
}
It seems to work very well. The entire update process still runs in a separate thread, but I did not have to create a thread. Seems like magic.
Just remember if you want to commit your changes to the physical core data files, you have call save: on the parent context as well.
I didn't really ask a question here. I'm posting this so it helps others because everything I found when searching for the new iOS5 managed object context methods only gave high level details with no code examples And all the other searches for fetching core data in the background are old, sometimes very old, and discuss how to do it pre-iOS5.
Core Data Solutions
Solution 1 - Core Data
Yes - it is really that easy now (in iOS 5.0). For iOS 4 compatibility, the previous hurdles remain, but the documentation is not too bad on thread confinement. Maybe you should add this to a wiki section?
Solution 2 - Core Data
I'm trying to understand how this new API is implemented. My usual pattern for multithreaded core data is something like this:
Usually in a NSOperation
but simplified using dispatch_async
in this example:
dispatch_queue_t coredata_queue; // some static queue
dispatch_async(coredata_queue, ^() {
// get a new context for this thread, based on common persistent coordinator
NSManagedObjectContext *context = [[MyModelSingleton model] threadedContext];
// do something expensive
NSError *error = nil;
BOOL success = [context save:&error];
if (!success) {
// the usual.
}
// callback on mainthread using dispatch_get_main_queue();
});
Then the main thread will respond by updating the UI based on NSManagedObjectContextDidSaveNotification
to merge the main context.
The new API's seem to be a wrapper around this pattern, where the child
context looks like it just takes the persistent coordinator from its parent to create a new context. And specifying NSPrivateQueueConcurrencyType
on init will make sure the performBlock
parameter is executed on the private queue.
The new API doesn't seem to be much less code to type. Any advantages over the 'traditional' threading?