4
votes

I have an architectural question. My App uses a TabBarController right in the application window. The ApplicationDelegate creates the managedObjectContext, although it actually doesn't need it.

Each ViewController in the TabBarController is a NavigationViewController. The first view controller for each NavigationController are my custom views. All is createde an linked via Interface Builder.

Now, how do I pass the managedObjectContext around the right way? Actually I need my views to load the data as soon as possible so that when the user chooses a tab or navigates through the NavigationControllers, the data is already there.

So my questions are:

  1. How to I pass the context properly?
  2. When should I fetch my data, i.e. in which method? "viewDidLoad" or "viewDidAppear"?

Thanks for all ideas!

4

4 Answers

10
votes

You should generally stay away from getting shared objects from the app delegate. It makes it behave too much like a global variable, and that has a whole mess of problems associated with it. And singletons are just fancy global variables, so they should be avoided unless really necessary, too.

I would add a managedObjectContext property to each of your view controllers and assign that when you're creating them. That way, your view controllers don't have a tight linkage with the app delegate.

As for when to fetch the data, you should do it lazily. Core Data is really fast, so I would wait until viewWillAppear: to do your fetching. If you wait until viewDidAppear:, the view is already on the screen and there will be a flicker when the data loads. Do be aware, though, that viewWillAppear: is called every time your view will become visible (e.g. when the user taps the back button on the navigation bar, or a modal view controller is dismissed) so you might want to track whether you've already loaded the data and skip the loading on subsequent calls.

2
votes

I've ran into this same problem, i'll share my solution.

First you need a reference to the Nav Controller in the Tab Bar in the nib file, make sure you connect it up.

IBOutlet UINavigationController *navigationController;

Then, get the Controller as recommended in the support docs and send it the managedObjectContext:

SavedTableViewController *saved = (SavedTableViewController *)[navigationController topViewController];
saved.managedObjectContext = self.managedObjectContext;

Alex is right, "You should generally stay away from getting shared objects from the app delegate. It makes it behave too much like a global variable, and that has a whole mess of problems associated with it."

0
votes

You can get it from the app delegate at any time like this:

myApp *d = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext = d.managedObjectContext;

Or variations of the above. Other than that you can add a property to all your viewcontrollers and pass it around or you can create a singleton and reference that globally.

0
votes

Swift

You should not share a NSManagedObjectContext, but you can share the NSPersistentStoreCoordinator.

Thus, you can create a new managed object context for each view, each sharing the same store. It is the preferred method, and allows concurrent, multithreaded access. In the example below, I am assuming that your AppDelegate, *if created with a recent version of Xcode with Use Core Data checked*, has a property named persistentStoreCoordinator:

lazy var managedObjectContext:NSManagedObjectContext? = {
    // This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.

    if let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate {
        let coordinator = appDelegate.persistentStoreCoordinator
        var managedObjectContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
        managedObjectContext.persistentStoreCoordinator = coordinator
        return managedObjectContext
    }
    }()