Greg Smalter
Greg Smalter

Reputation: 6699

Best practice for a long-running foreground operation that uses Core Data?

I have an app that imports a potentially large amount of data from the web after the user explicitly presses a Sync button, and stores that data using Core Data. Since I want to show feedback and I don't want the user interacting with the rest of the app while this happens, pressing the Sync button brings up a Modal dialog. Since I want the operation to happen immediately, the operation executes in the viewDidAppear method. I'm sure this is frowned upon.

There are a bunch of problems with the approach right now:

I am reading the Apple documentation on this, but I'm asking this in hopes of finding more concise guidance on this particular combination of needs. Thanks.

Upvotes: 3

Views: 745

Answers (1)

Jody Hagins
Jody Hagins

Reputation: 28409

You really should not freeze the main thread. You can still "prohibit" certain UI actions.

Create a separate context, as a child, and do all your work in there. When done (or at certain intervals), save the context to the main context, and notify the main thread to do some UI update interaction... maybe a progress bar or something...

NSManagedContext *backgroundContext = [NSManagedContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroudContext.parentContext = [self mainManagedObjectContext];
[backgroundContext performBlock:^{
    // This block is running in a background thread.
    // Go get your data from the web

    // Call this to push data to the main MOC (either at end, or at intervals)
    [backgroundContext save:&error];

    // When you want to do something on the main thread...
    dispatch_async(dispatch_get_main_queue(), ^{
        // This block is running on the main queue... I can do anything with the UI...
    }];
}];

Couple of things to note... your mainMOC needs to be private or main queue concurrency type. If you are using the Core Data template, where it is in the app delegate, just change the alloc/init to initWithConcurrencyType:NSMainQueueConcurrencyType.

I would, however, suggest using the canonical main/parent relationship. Create a private MOC, assign it to the persistent store, then create a main MOC, set its parent to be that private MOC. Now you are ready to handle any I/O with background operations, without blocking your UI.

Still, when loading from the web, use the pattern above: create a child MOC, then load objects into the main MOC.

Note, that the data is not saved to disk until the "root" MOC calls save.

Upvotes: 2

Related Questions