Mark Leonard
Mark Leonard

Reputation: 2096

Working with the same NSManagedObjectContext in multiple tabs

I have a tab bar controller with different view controllers all using the same managed object context, being set up as follows:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

RootViewController *rootVC = [[RootViewController alloc] initWithStyle:UITableViewStyleGrouped];
rootViewController.managedObjectContext = self.managedObjectContext;
UINavigationController *rootNavCon = [[UINavigationController alloc] initWithRootViewController:rootVC];
[rootVC release];

SettingsTableViewController *settingsVC = [[SettingsTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
settingsVC.managedObjectContext = self.managedObjectContext;
UINavigationController *settingsNavCon = [[UINavigationController alloc] initWithRootViewController:settingsVC];
[settingsVC release];

tabBarController = [[UITabBarController alloc] init];
NSArray *controllers = [NSArray arrayWithObjects:rootNavCon, settingsNavCon, nil];
tabBarController.viewControllers = controllers;

[self.window addSubview:tabBarController.view];
[self.window makeKeyAndVisible];

return YES;

}

The idea is similar to the Recipes sample code if there were another tab called Settings which offered an option to managed the Category objects. The problem is that if you navigate to the view where the user can select the Category, but then go to the settings tab and delete, add, or edit a Category, when returning to the Recipes tab the changes will not be immediately reflected. Thus selecting a deleted Category would raise an exception.

What is the best way to deal with this? I was thinking about setting up an NSNotification to alert the views whenever an important change had occurred, but wasn't sure if there is a better way to do this, such as querying [managedObjectContext hasChanges] when a view appears. (Although that wouldn't seem to work if the context had already been saved.)

Upvotes: 0

Views: 1155

Answers (2)

Damian Carrillo
Damian Carrillo

Reputation: 1218

There's really no answer to your question other than "It's a design decision that you must make." Think carefully about your use cases and do the appropriate thing.

In your sample code, you are pushing a global managed object context into each controller. Each controller then does something with that managed object context, presumably. One thing that is not clear from the code is whether you are pushing additional view controllers onto the navigation stack or modally displaying other views. My assumption is that you are, and that any of the controllers and associated views can mutate a managed object.

In this situation, using a single managed object context might not be what you want when used with a tab view controller. Tabs allow the user to freely roam around the application so that he or she may see the appropriate view for whatever action he or she wants to perform. Allowing them to modify managed objects from a single context will potentially show changes in the wrong places, or cause an assumption to fail. It sounds like this is the situation you are in.

My advice to you would be to create multiple managed object contexts for each workflow that could mutate data. Then I would guess that you could share one managed object context for all read-only screens. Another alternative would be to use a managed object context dedicated for each tab. This would ensure that changes are made in isolation, and propagated out whenever necessary, but you will have to register the contexts to receive change notifications and then merge those changes as appropriate.

To illustrate, picture that the following diagram is an iPhone with multiple tabs, and each respective controller is handling some activity at given times.

Tab A:  Controller M |---------------         ,------->
        Controller N                 `-------+
                                             | via NSNotificationCenter
                                             v
Tab B:  Controller O |-----------------------+-------->

Controllers M and O are members of the viewController array in the tab bar controller. The user starts on Tab A looks around, then navigates to Tab B. Then the user goes back to Tab A an pushes Controller N onto the navigation stack of Tab A and makes some change (like your your category deletion example). The deletion may or may not need to be propagated to Tab B. You can make this happen by having two managed object contexts where one listens for change notifications that the other has broadcasted via the notification manager.

Determining these use cases is probably the hard part, because I'm sure you understand how NSManagedObjectContext works in general (if not you should spend some time understanding it more thoroughly). As you can see from that simple diagram, for an extremely simple use case it introduces another dimension of complexity. So my point is that you must plan in advance if you don't want to have to tear down and reconstruct bits and pieces of your code base as it matures.

Upvotes: 2

Cameron Spickert
Cameron Spickert

Reputation: 5200

If you want changes to your managed object context to be propagated to your interface automatically and you're using table views (or even custom views), you could be using NSFetchedResultsController. This class watches a context for changes and triggers its delegate methods, allowing you to reload your views only when necessary.

Upvotes: 1

Related Questions