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;
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With