Question

Using storyboards you have no easy access to the first view controller in appDelegate (though once you do prepareForSegue makes it easy to pass the ManagedObjectContext down the navigation stack.

I've settled on giving each view controller (or superclass of each view controller) requiring Core Data access a moc member:

@synthesize moc = _moc;
@property (nonatomic) __weak NSManagedObjectContext *moc;

I'm uneasy about it because it doesn't seem a very elegant way to do it - too much code. But assigning directly requires specifying absolute indexes into the viewControllers arrays and changing appDelegate every time the requirement for ManagedObjectContexts change

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;

    // rootView gets a tab bar controller
    for(UINavigationController *navController in tabBarController.viewControllers) {

        for(UIViewController *viewController in navController.viewControllers) {

            if([viewController respondsToSelector:@selector(setMoc:)]) {
                [viewController performSelector:@selector(setMoc:) withObject:self.managedObjectContext];
                NSLog(@"Passed moc to %@", [viewController description]); 
            }
        }
    }

    return YES;
}

What are the pitfalls of this approach and is there a better way? Is it better to try and be more generic:

- (void)assignManagedObjectContextIfResponds:(UIViewController *)viewController {

    if([viewController respondsToSelector:@selector(setMoc:)]) {
        [viewController performSelector:@selector(setMoc:) withObject:self.managedObjectContext];
        NSLog(@"Passed moc to %@", [viewController description]); 
    }

}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSMutableArray *viewControllers = [NSMutableArray array];

    UIViewController *firstLevelViewController = self.window.rootViewController;

    if([firstLevelViewController respondsToSelector:@selector(viewControllers)]) {

        NSArray *firstLevelViewControllers = [firstLevelViewController performSelector:@selector(viewControllers)];

        for(UIViewController *secondLevelViewController in firstLevelViewControllers) {

            if([secondLevelViewController respondsToSelector:@selector(viewControllers)]) {

                NSArray *secondLevelViewControllers = [secondLevelViewController performSelector:@selector(viewControllers)];

                for(UIViewController *thirdLevelViewController in secondLevelViewControllers) {

                    [viewControllers addObject:thirdLevelViewController];
                }

            } else {
                [viewControllers addObject:secondLevelViewController];
            }
        }
    } else {
        // this is the simple case, just one view controller as root
        [viewControllers addObject:firstLevelViewController];
    }

    // iterate over all the collected top-level view controllers and assign moc to them if they respond
    for(UIViewController *viewController in viewControllers) {
        [self assignManagedObjectContextIfResponds:viewController];
    }

    return YES;
}
Was it helpful?

Solution

Adam,

Whilst I was exploring storyboards I pretty much did it the same way you did except I made each of my view controllers that had a MOC property conform to a protocol.

There's nothing significantly different there, so I'll move on.

I think the point is Storyboards, IMO, are half-baked. Coming from a .Net background what is obviously missing is an object builder framework coupled with an IoC container. When Apple add that Storyboards will be awesome. When the storyboard framework can look at the destinationViewController, determine it's dependencies and resolve those from a container life will be great. For now, all it can really do is look at the destinationViewController and init you a generic one, which is of limited use.

Unfortunately, because it's a half-baked solution I'm sticking with the traditional approach for now so all my view controllers are alloc'd and init'd manually and more importantly I've added a method to each view controller to initWithMOC:(MOC *)moc;

The architect in me is telling me this code is more robust, I guess it's a matter of opinion as to whether it's worth the trade-off.

Anyone else come up with a better way?

CA.

OTHER TIPS

Don't know if I understood properly, but why don't you left the managed object context directly in AppDelegate class and leave there all the logic for instantiate. And from then you can ask for it.

@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;

then you can recall it anytime from anywhere.

NSManagedObjectContext *moc = [(YourApplicationDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext];

For convenience I declared a define for it:

#define MOC [(YourApplicationDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext]

Therefore this become:

[MOC save:&error];

You can take this everywhere you like. Just try to have a look at the auto generated code for a CoreData application in Xcode, you will see that many accessors with CoreData are in there, and the CoreData itself is lazily initialized at first request.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top