0

New to OS X programming. Started with an Xcode template for a document-based app with Core Data.

In my default Document.xib I've created a View which I'm controlling with a custom ViewController. I then created a Managed Object Context in Document.xib and created two outlets, one to Document.h:

@property (strong) IBOutlet NSManagedObjectContext *myManagedObjectContext;

and one to ViewController.h:

@property (weak) IBOutlet NSManagedObjectContext *myManagedObjectContext;

In windowControllerDidLoad in Document.m, I then added self.myManagedObjectContext = [self managedObjectContext].

Following ghostfly's advice (could not locate an NSManagedObjectModel for entity name) I added:

NSLog(@"Context: %@",self.myManagedObjectContext);
NSLog(@"PS Coord : %@",self.myManagedObjectContext.persistentStoreCoordinator);
NSLog(@"MOM : %@", self.myManagedObjectContext.persistentStoreCoordinator.managedObjectModel);
NSLog(@"Entities : %@",[[self.myManagedObjectContext.persistentStoreCoordinator.managedObjectModel entities] valueForKey:@"name"]);

to my Document.m in windowControllerDidLoad and also in my ViewController's awakeFromNib. In Document.m everything seems to work fine: all the NSLog statements look right, and I can also add Entities into my NSManagedObjectContext but in the ViewController, only the first NSLog statement works, and the rest return (null).

My question: What's going wrong here, and am I even going about this the right way?

Various other questions seem to suggest either adding an AppDelegate to MainMenu.xib, but I'm not sure how this would work in practice in a Document-based application (for example, I'd expect each Document to have a separate NSManagedObjectContext, but if I use an AppDelegate, then surely they're all the same?), or even if that's recommended because some tutorials suggest this is explicitly not how to do it (e.g. here http://franck.verrot.fr/blog/2012/01/18/best-way-to-pass-nsmanagedobjectcontext-around-in-ios-applications/). Help is much appreciated! Thanks.

Community
  • 1
  • 1
Matthew
  • 1,366
  • 2
  • 15
  • 28
  • 1
    Why do you create a managed object context in the XIB? The `NSPersistentDocument`already sets one up for you to use! – Volker Mar 20 '14 at 07:56
  • Volker: Probably because I don't understand what I'm doing! I can see that the NSPersistentDocument sets one up for me, but I don't know how to access that from a ViewController that I'm using in my Document.xib ... – Matthew Mar 20 '14 at 18:30

2 Answers2

1

Edit: Correct answer is to use MagicalRecord (https://github.com/magicalpanda/magicalrecord) and save yourself a bunch of time!

My previous answer below:

Okay, I think I've got this working, but I'm not sure if this is the "best" or "most idiomatic" way of doing it. I'm going to spell it out in extreme detail in case someone else stumbles across this question and is equally stumped by how all this fits together...

First, forget about the ManagedObjectContext in the .xib; I'm not sure when it's appropriate to use that.

I created an application delegate called AppDelegate. In AppDelegate.h:

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate>

@property (retain) NSManagedObjectContext *managedObjectContext;
@property (retain) NSPersistentStoreCoordinator *persistentStoreCoordinator;

@end

Then, in MainMenu.xib, I drag in an NSObject and set its class to AppDelegate and connect the File's Owner's delegate to this object.

In Document.m, in the init method, I add:

AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
appDelegate.managedObjectContext = [self managedObjectContext];
appDelegate.persistentStoreCoordinator = [self managedObjectContext].persistentStoreCoordinator;

Note that I originally tried putting it in the windowControllerDidLoadNib method but this didn't work, because it seems like this is being called after my ViewController's awakeFromNib method.

Then, in my ViewController.m in awakeFromNib I can access the context by, e.g.:

AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
MyEntity *test = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[appDelegate managedObjectContext]];

I'll accept this answer if I don't find anything wrong with it over the next few days.

An open question is what will happen when I have multiple Documents open. Will they all share the same context?

Matthew
  • 1,366
  • 2
  • 15
  • 28
  • 1
    Just to answer your last open question. Each NSPersistentDocument has its own context, because each has its own persistent store. – Micha Mazaheri Jul 09 '14 at 00:53
1

The above answer would create problems when you start to use multiple documents - it would not work as you would be saving just to the last opened document. You shouldn't try to create your own managed object context, because then you have to set up all the stuff for where to store the data etc (which is done already). It's much simpler:

Your main document class should be a subclass of NSPersistentDocument. If not, then you can literally just substitute that instead of NSDocument. The NSPersistentDocument has it's own managedObjectContext and persistentStoreCoordinator that you can use with in the document (to store document related data).

This therefore means each document has it's own managedObjectContext and persistentStoreCoordinator. To access these in other classes, you should pass the reference to the document. E.g. on your view controller subclass, create an @IBOutlet to the document, attach the document in interface builder and then in the constructor, copy the document's managedobjectcontext to it's own pointer in the view controller to use.

James Alvarez
  • 7,159
  • 6
  • 31
  • 46