Execute NSFetchRequest on application startup - objective-c

In another question ( Accessing an NSApplications delegate in another class? ) I asked about calling the Application's delegate because I needed it's managedObjectContext for a fetch request. However, when I try to let all values be displayed in an NSTableView on application startup, I'm running into problems. DataController, my NSTableViewDataSource, calls it's init-method before my application delegate calls it's applicationWillFinishStartup or any other method to initialize the managedObjectContext. What am I doing wrong? How else can I fill an NSTableView with already existing objects?

You should access managedObjectContext only via its getter, even from DataController, as in [appDelegate managedObjectContext] or appDelegate.managedObjectContext.
Your managedObjectContext method should automatically set up the managed object context; you shouldn't write an explicit moc setup routines in your applicationDidFinishLaunching, etc. And the standard core-data template is written that way.
Now, for this to work, the app delegate needs to be properly set up from the point of view of DataController. However, init is called before all the IBOutlet is set up, so that's the wrong place to perform setup operations of objects inside the nib. Instead, use awakeFromNib to do these things. awakeFromNib is sent to every object after the IBOutlet etc. are all set up.
That said, writing your own DataController is a total waste of time. Just instantiate the standard NSArrayController in the nib file, and use it in the Core Data mode via binding. There's absolutely no need for you to write the fetch request by yourself. Study Apple's own CoreData sample codes and then google "Binding CoreData Tutorial" for many tutorials available on-line.

Related

AppDelegate instantiated last on launch

I'm very comfortable with writing iOS apps, but OS X unexpectedly seems somewhat alien.
Here's the question upfront (read on for context):
When my application launches using the .xib set in the Main Interface field under the deployment info of my apps target, why does the AppDelegate get instantiated after the ViewControllers?
Context (no pun intended):
The reason I ask is because I'm using Core Data (spare me any heckling for this decision), and typically you keep a pointer to the MOC (Managed Object Context) in AppDelegate. One of my controllers is trying to get this MOC instance variable but the AppDelegate instance isn't around yet and therefore my app doesn't present data just after launch.
The AppDelegate and the two ViewControllers are in the .xib. The VCs are hooked to views inside a split view. They're trying to use the MOC in viewDidLoad to make queries. They are accessing the AppDelegate like this:
let delegate = NSApplication.sharedApplication().delegate as AppDelegate
let moc = delegate.managedObjectContext
This will crash as the .delegate property of the sharedApplication() returns nil.
I tried making an NSWindowController from the .xib in applicationDidFinishLaunching and removing the .xib from the Main Interface field, but then applicationDidFinishLaunching doesn't get called at all.
I've ensured that all the connections in IB for from the Application and the Files Owner (NSApplcation) delegate IBOutlets to the AppDelegate have been made.
UPDATE - 31/03/15
Stephen Darlington's answer below offers a good solution for my/this case. And as I understand it's actually better to setup the MOC in the way he's suggested.
If a correct answer arrives that explains why the AppDelegate is being instantiated at some later time in the launch process, I'll mark it correct instead of Stephen's. Thanks Stephen!
The "easy" solution would be to have managedObjectContext create a MOC if one doesn't exist (i.e., change it from a property to a method). That way which ever code gets there first the stack will be available.
(I'll spare the lectures about both creating the Core Data stack in the app delegate and accessing the app delegate like that!)
Here's another option without having to subclass NSApplication:
Don't put your view controllers in the .xib that you set as the Main Interface, just have the Main Menu (menu bar), AppDelegate and Font Manager in there.
Then make your view controllers in other .xibs.
Then in the applicationDidFinishLaunching method init your view controllers from their .xib files.
I also faced this issue with setting up Parse. To get around it, I simply subclassed NSApplication and set it as the Principle class in the Info.plist. In your NSApplication subclass, override the init methods and initialise Parse or anything else you need to, there.

Xcode Cocos2d Can't Create AppDelegate Object

I am working with cocos2d and CoreData. I have imported "AppDelegate.h" but I can't create an object from it. I would think that all I would do is:
AppDelegate *delegate;
When I do this it get an error saying AppDelegate is not defined. The AppDelegate .h and .m files are next to the main file.
Also, when I try to write to a file I don't get an error but it does not write.
The boiler plate code created by XCode (I'm assuming you are using XCode) will create an NSManagedObjectContext for you called *managedObjectContext. If you need to create other objects that are going to interact with your core data model (such as a view controller), you simply pass in that managedObjectContext object as an argument (or link it to a property in your custom class) and interact with it in your custom class. It's worth noting that it's not good practice to be passing around an AppDelegate object in your app. Your app delegate should be at the foundation of your code base and not treated as a typical class. There are definitely times when you will want (or need) to pass the app delegate as an object or reference it in IB, but typically your app will launch in your
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
method and you will launch your primary view controller from there.
Some things in core data also require the persistent store coordinator (such as retrieving managed object ID's), so you may need to pass that in as well. If you need some help working with Core Data, there are a bunch of great articles and books on the subject. I recommend reading as many as you can, since Core Data can be difficult to comprehend at first. It helped me to read a bunch of tutorials at first since everyone explains it a little differently.
Here's a great recent tutorial written on Bindings, Core Data, and working with the app delegate to get you started: http://www.raywenderlich.com/21752/how-to-use-cocoa-bindings-and-core-data-in-a-mac-app
In Cocos2d 2.0, AppDelegate is renamed to AppController
AppController *app = (AppController*)[UIApplication sharedApplication].delegate;

Saving UIViewController in appDelegate

I am using Xcode 4.3 and need to know the parent view controller of the current view.
I am also using storyboard.
self.parentViewController always returns nil.
I saw different answers to save the parent view controller in AppDelegate class as a property. E.g., To create a variable: UIViewController parentViewController in AppDelegate and in the code write:
appDelegate.parentViewController = self;
I am afraid that it will consume memory - as this is a heavy object.
What is the best approach to know aretnViewController when using story board?
Whether or not an object is "heavy" does not matter as long as you store only a reference to it (in your case in the application delegate). Creating a second object would make a difference, but the line
appDelegate.parentViewController = self;
does not do that, it merely stores a reference to the object.
I know that this does not answer your direct question, but I think you should go ahead with the "store a reference in the app delegate" approach.

bind the managedObjectContext outlet for a controller using Interface Builder?

I'm trying to set up a fairly simple view that presents a table to the user. This table is connected to an Array Controller, which I want to use to retrieve records from Core Data. For some reason, I can't seem to connect the 'managedObjectContext' outlet to anything else in my app. When I created my project, there was a property generated in my app delegate which returns the MOC I need, but I can't hook it up in Interface Builder, even after prepending "IBOutlet" to the declaration. Image below showing the available connection on both ends:
http://yada.im/uploads/image/screenshot/1108/7efebc90ca7187a537da9ae003dd5f3e.png
I'm sure that I'm missing some simple step here but I can't tell what piece of glue code I'm supposed to write that will allow me to hook this up more easily. For reference, I've tried dragging a line from the controller's moc outlet to every single source I could think of, and changed the "File's Owner" class to that of my application. Stumped here!
Typically in the template provided by XCode the managedObjectContext comes along with AppDelegate.
You have to bind the managedObjectContext reference of the array controller to the managedObjectContext in AppDelegate.
For this you have to make an object of AppDelegate inside the xib i.e., if its not already present.(Drag an object placeholder from your object library and make its class as AppDelegate)
This makes AppDelegate visible for binding inside that xib.
Next step is actually binding the managedObjectContext. Select your array controller and go to bindings inspector. In the parameters section select App Delegate from the drop down and check on "Bind to".
Fill the "Model Key Path" field with self.managedObjectContext. Now you will find the connection in the connections inspector also.
UPDATE:
The process of creating a new AppDelegate object is to be done only if it is not already present in the main nib file (but the stub generated always has the AppDelegate object in the main nib file).
For a non main nib file, if we follow the above approach, a new AppDelegate object will be created which won't be the NSApplication's delegate. Even though this can be solved by connecting delegate outlet of the application object proxy provided in each nib, the AppDelegate object still won't be the same.
The result is two different managedObjectContext talking to the same store. Although this might appear to work properly when the changes are saved at each step, this is not what we want.
To get the right AppDelegate object, i.e. the one used in the main nib file:
-instead of creating a new AppDelegate object, bind the managedObjectContext of the array controller directly through the application to its delegate. In other words the object to bind to will be the application object and the key path used will be self.delegate.managedObjectContext.
The way to add objects of your entity depends on the specific logic you want to implement.
The generic and easy solution would be, binding the fields for input to the array controller like you might have done for the table and then hooking up the array controller methods to the buttons inside the sheet.
Another option is sub-classing NSArrayController and over-riding the super class methods like add: to write your code (for opening your slide sheet maybe) before calling the super class method, [super add:sender] . Don't forget to specify this sub-class of NSArrayController as the class of your array controller in the xib.

Override Interface Builder instantiation of objects?

I'm developing for the iPhone, and I have a class DataManager, that is used to maintain my application data. When the application launches/exits, the data is read from/written to disk to create an instance of this class, using the NSKeyedArchiver (and Unarchiver) classes, since the DataManager adheres to the NSCoding protocol.
One problem I'm having is that I need the DataManager to be accessible by many of my other IB classes, so it is defined as an object in IB, and those classes have an outlet to it. The DataManager is being created using the standard init: method (or maybe initWithCoder:?), but since IB doesn't have the proper file (or NSData from the file) to instantiate the object, it has no initial contents.
So, is there any way to tell IB not to instantiate the class automatically? This will instead be performed using my application delegate, something like:
AppDelegate.h
IBOutlet DataContext *context;
AppDelegate.m
context = [NSKeyedUnarchiver unarchiveObjectWithData:dataLoadedFromFile];
As you can see, this presents a problem. Wouldn't the context be instantiated twice, once by InterfaceBuilder, then a second time by my application delegate?
I would like to prevent maintaining the context as an ivar in the delegate, since that seems to stray from the MVC paradigm, and leans toward the singleton pattern instead. (The controller should not be responsible for the data in my mind. It can maintain a reference to it obviously, but should not be responsible for offering it to other classes.)
When the application launches/exits, the data is read from/written to disk to create an instance of this class, using the NSKeyedArchiver (and Unarchiver) classes, since the DataManager adheres to the NSCoding protocol.
One problem I'm having is that I need the DataManager to be accessible by many of my other IB classes, so it is defined as an object in IB … As you can see, this presents a problem. Wouldn't the context be instantiated twice, once by InterfaceBuilder, then a second time by my application delegate?
Yup.
First, you should think about whether this is a controller or a model object. It sounds to me like it's a controller.
If it is, then you should move the model to a separate object or objects, and make those NSCoding-compliant, and make the data manager load and save those objects. A bonus of this solution is that you could tell the data manager to save the objects and purge them when you get a low-memory warning, not just at quit time.