TL;DR Passing an NSManagedObjectContext in each and every NSViewController that needs CoreData can be done by different means, but some are frowned upon. In doubt, inject it.

While pair-reviewing some code with my colleague Vincent Tourraine, we realized we wrote this kind of code into each viewDidLoad: method of each NSViewController subclasses we created:

managedObjectContext = [[[UIApplication sharedApplication] delegate] managedObjectContext];

Doing so is more or less the same as using a global variable, as calling sharedApplication on UIApplication returns the singleton application instance. I kind of felt this was a code smell so I investigated.

After a couple of minute of browsing Apple’s documentation, here’s what we can read

A view controller typically **shouldn’t retrieve** the context from a global object such as the application delegate. This tends to make the application architecture rigid. [...] [...] When you create a view controller, you pass it a context. [...]

If you use the good old nib|xibs (I’m acting as if I was confident with them) and instantiate your controllers programmatically, this sounds dead easy, but what if you use the brand new shiny Storyboards?

The only solution I came up with, that will comply with Apple’s recommendation, is to use prepareForSegue, like this:

#import "MyMainController.h"
#import "MyOtherController.h"
@implementation MyMainController
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if([[segue identifier] isEqualToString:@"SegueToMyOtherController"]) {
        MyOtherController *controller = [segue destinationViewController];
        controller.managedObjectContext = managedObjectContext;
    }
}
@end

If your main controller doesn’t have any NSManagedObjectContext, you might want to inject it from within the method application:didFinishLaunchingWithOptions: of your AppDelegate, as you can see if you create a new CoreData project from the templates.

So next time, no more abuse of the sharedApplication !