4

I´m completely new to core data programming. i just try to find out where the best place for implementing the core data code would be. i´ve done the apple tutorial Locations and it worked well. now i try to transfer that to my current project what is a bit more complex.

the Locations tutorial shows one RootViewController including a programmatically generated tableView. my project is based on a tabView template. it owns a MainWindow.xib including the TabBarController including three ViewController (MapView, ListView, SettingsView) where each view has it´s own navigationController and xib-file.

The first stumbling block was changing the code that it will run with a xib for the tableView instead of creating it programmatically. I´ve managed that but there is still one error. I can´t connect the managedObjectContext from the appDelegate to the listViewController. I´ve tried the examples and suggestions for that issue from this forum here. but it still doesn´t work.

after looking at the CoreDataBooks sample project i´ve seen that the core data code was implemented in the RootViewController as well. Seems that it would be the wrong way to implement it in the ListViewController. But i don´t have a RootViewController in my project. In the AppDelegate i directly pass the tabBarController as the rootViewController. therefore i don´t know how to reach the listViewController to set the context like it was done in the Locations sample.

As the MapView is the first view i can´t set the context in the appDelegate. And after struggling a long time with the managedObjectContext i wonder if it would be better to invent a RootViewController to be able to place additional code there. the model should be accessible by all three views and it seems that the RootViewController is the right place.

But how do i combine that with a tabBarController which includes three more viewControllers based on xib-files? Could somebody recommend me examples or tutrials including core data based on a tab bar app?

rockstarberlin
  • 1,853
  • 1
  • 18
  • 31
  • The app I'm currently working on uses Core Data with a tabBarController-based architecture, so I feel I may be able to pass on some advice. What exactly is it that you want to do? Pass your context to the different tabs? – justin Jul 11 '11 at 16:12
  • exactly. i just fixed my bug. i´m able to take the context from the appDelegate inside of viewDidLoad in ListViewController. unfortunately i´m no allowed to post the code here as i have >100 posts. but tomorrow i´ll post the changes. anyhow i´m not sure if that is the usual way – rockstarberlin Jul 11 '11 at 16:39
  • I'm not entirely sure if there's a usual way. The way I personally went about it was to create a window-based application with Core Data. Then in the app delegate, I created IBOutlets to my tabBarController and it's tab views, then synthesized them in the .m file. Then you can set `tab1.managedObjectContext = self.managedObjectContext`, which passes your context to the chosen views. You have to set up the outlets in IB, then use `[self.window addSubview:tabBarController.view]` and `[self.window makeKeyAndVisible]`. If your method has issues, I'd be happy to share a more detailed version – justin Jul 11 '11 at 16:54
  • possible duplicate of [Where to place the "Core Data Stack" in a Cocoa/Cocoa Touch application](http://stackoverflow.com/questions/1267520/where-to-place-the-core-data-stack-in-a-cocoa-cocoa-touch-application) – Brad Larson Jul 11 '11 at 17:18
  • seems so! how can i close this thread? – rockstarberlin Jul 11 '11 at 17:21

4 Answers4

3

Please read the following article by Marcus Zarra: Passing around a NSManagedObjectContext on iOS. That should give you an idea how to solve your problem.

In general you should add a NSManagedObjectContext property to all of your ViewControllers and pass the context before adding them to the view stack via pushViewController:animated:. You should not take the context from your app delegate.

If you pass a single NSManagedObject to a ViewController, e.g. to present a kind of detail view, you can access the context from that object, as every NSManagedObject knows about the NSManagedObjectContext it is "living" in.

If you are a registered iOS developer, I'd also recommend the WWDC 2010 and 2011 videos. There are some sessions about mastering Core Data.

Florian Mielke
  • 3,310
  • 27
  • 31
  • thank you for your reply. i read the article already. as the author mentions that he hasn´t done it with IB there is not a lot of info. i tried something like -> self.tabBarController.listViewController.navController.managedObjectContext = context; -> but that didn´t work – rockstarberlin Jul 11 '11 at 16:04
1

ok, now i have the correct solution. it took a while to understand but now it works with dependency injection from application delegate into the view controllers (listViewController).

my problem was that i didn´t know how to reference my view controllers as they are nested into dedicated navControllers and one tabBarController.

after reading a lot of postings here i understood i have to declare my view controllers in the appDelegate.h and synthesize them in appDelegate.m and after that connect them to the appropirate item in IB. that was done fast & easy after understanding :-)

there is no rootViewController needed.

MyAppDelegate.h:

#import <UIKit/UIKit.h>
#import "ListViewController.h"

@interface MyAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {

    UIWindow *window;
    UITabBarController *tabBarController;
    IBOutlet ListViewController *listViewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
@property (nonatomic, retain) IBOutlet ListViewController *listViewController;

@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;

@end

MyAppDelegate.m:

#import "MyAppDelegate.h"
#import "ListViewController.h"

@implementation MyAppDelegate

@synthesize window=_window;

@synthesize tabBarController=_tabBarController;

@synthesize managedObjectContext=__managedObjectContext;

@synthesize managedObjectModel=__managedObjectModel;

@synthesize persistentStoreCoordinator=__persistentStoreCoordinator;

@synthesize listViewController;

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

    NSManagedObjectContext *context = [self managedObjectContext];

    if (!context) {
        // Handle the error.
    }
    // Pass the managed object context to the view controller.
    listViewController.managedObjectContext = context;

    // Override point for customization after application launch.
    // Add the tab bar controller's current view as a subview of the window
    self.window.rootViewController = self.tabBarController;

    [self.window makeKeyAndVisible];
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque animated:NO];  
    return YES;
}

...

ListViewController.h

#import <CoreLocation/CoreLocation.h>


@interface ListViewController : UITableViewController <CLLocationManagerDelegate> {

    UINavigationController *navController;
    NSManagedObjectContext *managedObjectContext;
}

@property (nonatomic, retain) IBOutlet UINavigationController *navController;
@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;

-(NSManagedObjectContext *)managedObjectContext;

@end

ListViewController.m

#import "MyAppDelegate.h"
#import "ListViewController.h"


@implementation ListViewController

@synthesize navController;
@synthesize managedObjectContext;

- (void)viewDidLoad {

    [super viewDidLoad];

    NSLog(@"managedObjectContext: %@",[self managedObjectContext]);

    NSError *error = nil;
    if (![managedObjectContext save:&error]) {
        NSLog(@"error: %@",[self managedObjectContext]);
        return;
    }

...
rockstarberlin
  • 1,853
  • 1
  • 18
  • 31
0

I've coded an app like that some time ago. The way I've solved it is I made a singleton which had a persistentStoreCoordinator property like the one in Apple documentation to hold the access to the database (so I don't have to write it every time). Then in every tab bar view controller I've initiated its own NSManagedObjectContext.

NSPersistentStoreCoordinator *coordinator = [[Singleton sharedSingleton] persistentStoreCoordinator];
        if (coordinator != nil) {
            _managedObjectContext = [[NSManagedObjectContext alloc] init];
            [_managedObjectContext setPersistentStoreCoordinator: coordinator];
        }

That way every controller approaches the database with it's own context, if you understand what I mean.

Please note that if any of your view controllers has a detail view controller, take the standard approach of passing the managed object context to it like in sample code (Books, Locations, Recipes).

Dunja Lalic
  • 752
  • 13
  • 26
  • thank you for your anser! in Books and Locations they both pass the context to the RootViewController. in Books to the topViewController. Seems that would be the MapViewController in my case. Do you have an idea how i can pass the context to the listViewController after the tap on the list icon before the listView is loaded? – rockstarberlin Jul 11 '11 at 16:34
  • You add a property managedObjectContext to a listViewController, and when you want to do something with it you write something like this: `ListViewController *listViewController = [[ListViewController alloc] init]; listViewController.managedObjectContext = self.managedObjectContext; //present it [listViewController release];`. I've been meaning to do a tutorial about this for a while, but I guess I was too lazy. – Dunja Lalic Jul 11 '11 at 17:22
  • ok, i know how to do that when i present the viewController manually. but in my case the ListView will show after interaction on the tap bar button. my three views are controlled by the tabBarController and created with the IB. i could place something in -> tabBarController didSelectViewController but i´m not sure if the ListViewController is initialised already or not. or can i instantiate it anyway in the appDelegate even if it´s defined inside the tabBarController in IB? isn´t that a different reference then as long as the ListViewController isn´t a singleton? – rockstarberlin Jul 12 '11 at 09:52
  • I am really sorry, but I can't help you with that. I always do everything problematically and don't have much experience with nibs. – Dunja Lalic Jul 15 '11 at 10:52
0

i just fixed the bug. i missed out some methods necessary in the appDelegate. it works now if i put following code into viewDidLoad of my ListViewController

if (managedObjectContext == nil) { 
    NSLog(@"managedObjectContext is nil");
    managedObjectContext = [(IntraAppAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}

is that OK concerning proper MVC pattern rules? in my case the ViewController takes the context from the appDelegate now.

trying to set the context in the appDelegate with something like that throws an error:

 NSManagedObjectContext *context = [self managedObjectContext];

    if (!context) {
        // Handle the error.
    }
    // Pass the managed object context to the view controller.

    self.tabBarController.listViewController.navController.managedObjectContext = context;
    self.window.rootViewController = self.tabBarController;

how can i gather the reference of other viewControllers which are controlled by the tabBarController and are not the topView/superView after app start? the first view is the MapView. do i have to instantiate or declare the listViewController in appDelegate? how must it be coded that it referes to the listViewController controlled by the tabBarController?

rockstarberlin
  • 1,853
  • 1
  • 18
  • 31
  • my view structure looks like that: http://i5.photobucket.com/albums/y154/rockstar_berlin/iOS/Screenshot2011-07-08at170615.png – rockstarberlin Jul 12 '11 at 10:01