Unome
Unome

Reputation: 6900

How to access managedObjectContext if UIApplication.delegate must be used from main thread only?

So for ages I've been doing the following to access my universal instance of managedObjectContext.

let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext

Now when I want to do some CoreData work on a background thread I do the following:

let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
                privateContext.persistentStoreCoordinator = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext!.persistentStoreCoordinator
                privateContext.perform { //Do work

Well, I get the following error now in Swift 4.

UIApplication.delegate must be used from main thread only

The point is to do the work on background thread, whats the best practice here now? Create a new managedObjectContext for my background thread?

Upvotes: 1

Views: 772

Answers (2)

tdimeco
tdimeco

Reputation: 856

Actually your error does not seem to be related to Core Data. I guess you are just calling UIApplication.shared.delegate on a background thread. A better solution could be to put Core Data stuff out of the App Delegate (inside another singleton, or better, by passing around the context through controllers).

About Core Data stacks, your solution seems to be the best. It's better to perform background work on a private context directly attached to the coordinator. But then you will have to handle managedObjectContextDidSave notification in order to update your main context if needed:

[Private] → [Coordinator] ← [Main]

Another option is make the main context a child of the private one:

[Coordinator] ← [Private] ← [Main]

The last one is not really interesting, because fetch/save operations will fall through the main context and then block the UI.

[Coordinator] ← [Main] ← [Private]

Upvotes: 1

Aaron Brager
Aaron Brager

Reputation: 66242

If you want to do Core Data work on a separate thread, you should have a separate managed object context for each thread. See the Using a Private Queue to Support Concurrency section of the Core Data Concurrency docs. Make sure to set its parentContext:

let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
privateMOC.parentContext = moc

This will help you ensure data integrity. There are a few caveats. Here's the big one, from the parent store section of the NSManagedObjectContext docs:

When you save changes in a context, the changes are only committed “one store up.” If you save a child context, changes are pushed to its parent. Changes are not saved to the persistent store until the root context is saved. (A root managed object context is one whose parent context is nil.) In addition, a parent does not pull changes from children before it saves. You must save a child context if you want ultimately to commit the changes.

In terms of where to store it — it could be anywhere on any object that makes sense, but avoid storing it on UIKit objects to avoid the main thread warning.

Upvotes: 3

Related Questions