Where do I get a managed object context? - iphone

In my AppDelegate.m, in the application: didFinishLaunchingWithOptions: method, I put the following line:
NSManagedObjectContext *context = [self managedObjectContext];
But it says: AppDelegate may not respond to managedObjectContext. I saw this in a tutorial online, what am I doing wrong? I put #import <CoreData/CoreData.h> in my App_Prefix.pch file (see Adding Core Data to existing iPhone project) but that didn't help.
The goal is to then set myViewController.context = context and then use that context to fetch some data in the view controller.
EDIT: Please see my comment to the answer of O. Begemann.

Create an empty sample app and make sure you check the Core Data checkbox. Then look at the boilerplate code for Core Data that has been generated in the application delegate. You need corresponding pieces of code in your app.

It is likely that the tutorial you are looking at used an iPhone project template that included Core Data. When you create a new project, most templates have a checkbox option to "Use Core Data for storage". Selecting that option creates three methods in your app delegate to retrieve a managedObjectContext, managedObjectModel and persistentStoreCoordinator. You would access those methods using [self managedObjectContext] etc, like in the tutorial you mention.

If you decide to add core data to an existing project and you didn't check off that box mentioned in the tutorial, you'll want to add the properties into the appdelegate header file as well as this important piece in your prefix.pch
#import <Availability.h>
#ifndef __IPHONE_3_0
#warning "This project uses features only available in iOS SDK 3.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#endif

Do you have a method with the signature -(NSManagedObjectContext *) managedObjectContext; or a #property(...) NSManagedObjectContext *managedObjectContext; in your AppDelegate.h?

Tim,
Thanks a lot for your suggestion. After creating this boilerplate app I have realized that XCode generates some additional code in the app delegate IF you select CoreData option. I could not understand why Apple's dev guide has relatively long multi-step process for initializing the core data stack and most of the examples just refer to this (non-existing by default!) property. It turns out that all these examples assume that the application was created in certain way.

Related

Using core data with an existing single view application

I'm trying to learn about Core Data and am following the "Locations" tutorial from Apple. One immediate stumbling block i've come across is that I have already started constructing the application that i want to use Core Data with. It is a single view application.
What are the steps I need to take in order to utilize Core Data with this application? The tutorial says to select the checkbox "use core data for storage" when creating the project but there must be a way to enable core data after the project creation.
I'd really appreciate some help. Thanks.
1.) Create a View-based Application named CoreDataTutorial.
2.) Add the core data framework to the project. Right click on Frameworks, select Add > Existing Frameworks … find CoreData.frameworks and click add.
3.) Add a Data Model to the project. Right click on Resources, select Add > New File … under iOS choose Resource and then select Data Model and hit Next.
Name the file CoreDataTutorial.xcdatamodel, and hit next.
4.) Double click on the file we just created, CoreDataTutorial.xcdatamodel. This opens up the model object editor.
In the Top left pane click the + symbol to add a new Entity.
Name the entity “SomeName” by entering the name in the top right pane.
While the entity is still selected, click the + symbol in the top middle pane and choose Add Attribute.Name this Attribute “some_attribute_name” and set it to type String.
5.) Now we are going to create relationships between our two entities. Select your entity in the entity pane. Click the + symbol in the property pane and select Add Relationship. Name the relationship “creation”, set the Destination to Release and the Delete Rule to Cascade.
To do the inverse we select Release in the entity pane. Click the + symbol in the property pane and select Add Relationship. Name the relationship “creator”, set the Destination to Artist, set the Inverse to release and the Delete Rule to Cascade.
You can now close the object editor.
6.) Expand Other Sources and double click CoreDataTutorial_Prefix.pch. Add an import for CoreData.
#ifdef __OBJC__
#import <foundation foundation.h="">
#import <uikit uikit.h="">
#import <coredata coredata.h="">
#endif
This saves us from having to import it into each file.
7.) Next we are going to set up the app delegate header file and then the implementation file.
First the header file. We need to create variables for our NSManagedObjectContext, the NSManagedObjectModel, and the NSPersistentStoreCoordinator.
We are also going to declare an action named applicationDocumentsDirectory, this action gets the path to the condiments directory where our data will be stored in a SQLite file. And an action that saves the context when the app quits.
Here’s what the header file looks like when we’re done. Remember we added the import statement to the CoreDataTutorial_Prefix.pch file so we don’t need to import it here.
#import <uikit uikit.h="">
#class CoreDataTutorialViewController;
#interface CoreDataTutorialAppDelegate : NSObject <uiapplicationdelegate>
{
UIWindow *window;
CoreDataTutorialViewController *viewController;
#private
NSManagedObjectContext *managedObjectContext;
NSManagedObjectModel *managedObjectModel;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet CoreDataTutorialViewController *viewController;
#property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (NSURL *)applicationDocumentsDirectory;
- (void)saveContext;
#end
8.) If you are not using an ARC,take care about deallocating memory
9.) Implement applicationDocumentsDirectory method.
/**
Returns the URL to the application's Documents directory.
*/
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
10.) Next, implement saveContext method:
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil)
{
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
11.) Finally implement your accessors methods for your variables and that's it.
Go to Targets -> Build Phases -> Link Binary with libraries and add CoreData.framework. You should be good to go...
Of course you can add core data support after you already started or created you app.
I would recommend you to do the following:
Add core data framework to your target
Add a object model "Add new File -> Core Data -> Data Model"
Create your initial object model
Create NSManagedObject Subclasses for all your models in your object model
Create a Controller (subclass NSObject) where you do a singleton / static instance (singleton)
In your controller add some data-layer method like "getObjectsXYZ", "saveData", etc.
In your existing app you can then load objects through your controller class like "[[mycontroller sharedInstance] getObjectYByName:#"some text"]"
Of course you need to dig into core data. :)
If you used to ORM's then you might look at: mogenerator vim rentsch.
http://rentzsch.github.com/mogenerator/
This makes data models very very easy. I swear on that tool!
If you need to see the kind of code additions that you'll need to make to access Core Data, create a dummy project that includes Core Data and look at the kind of methods that are in the app delegate to support it.
Or, you can go with a nicer approach of having your Core Data access code in its own class. A good description of that strategy can be found here: http://nachbaur.com/blog/smarter-core-data
Honestly, if you only have a single view it's probably easiest just to create the application from scratch as the tutorial says. There are quite a few bits and pieces that you need to include and it could save you a lot of time in the long run.
However, you can do it by hand:
Add the Core Data framework to your project
Add a data model
Add code in your app delegate to create the managed object context (including all the error conditions)
From my experience the better way to include Core Data in your project is to select a project type that has an option 'Use Core Data for storage'. You can include files from your existing project in the new one. Of course you can include Core Data manually, but it will create a lot of problems.

Want to create a container class which will only exist once

I want to make a class that can hold settings for my app. Its pretty basic. I will access it from a few different classes, but only want 1 version of this container class to exist so they all see the same data.
Is there something special i need to flag for this?
Here is what I've done so far:
Filters.h
#import <Foundation/Foundation.h>
#interface Filters : NSObject
{
NSString *fuelType;
}
#property (nonatomic, copy) NSString *fuelType;
#end
Filters.m
#import "Filters.h"
#implementation Filters
#synthesize fuelType;
#end
Is there some flag i need to use when i alloc an instance of this class or how should I work this if I need to access the fuelType value from 2 different classes?
Thanks
-Code
For global application settings a better way would be to use NSUserDefaults or if you want to store data for use you should look up using core data and sqlite.
Lastly, if you want to go for ease of use you could do a core data style app delegate class and grab it by using:
[[[UIApplication sharedApplication] delegate] myClass] that way you'll always have that version of the class.
You need a singleton:
you can create your singleton by your own or use AppDelegate object which is an object that is always alive and never released while your application in the frontground (just put the vars needed there and initialize them dynamically).
Here are some links on how to create a singleton.
Cocoa fundamental Guide: Creating a Singleton
and
CocoaDev Singleton Pattern
What you're looking for is a singleton. Most people advise against using singletons though as it is often considered "dirty". See "Singleton" in this apple doc to learn more about it.

Can't add iAd delegate to ViewController

I'm trying to add iAd into my project. I'm following this tutorial http://bees4honey.com/blog/tutorial/how-to-add-iad-banner-in-iphoneipad-app/
I just added the iAd.framework package to my project and set it as Weak.
I added #import <iAd/iAd.h> into my .h file but whenever I try to add the ADBannerViewDelegate it doesn't show up in the intellisense, which means it's not accessible to me.
Same thing for ADBannerView class and the delegate methods (in case I add the delegate anyway).
Any ideas?
Restart XCode and set your target to "Device". The framework might not show up on intellisense but should work nevertheless.

Why is the CoreData stack in XCode's CoreData enabled template treated as private?

In regards to XCode templates with CoreData enabled, I've read unclear use of #property in window app using core data which goes over the 'what' in the templates. But I am having an issue with the 'why'. By declaring the category in the implementation file, the CoreData accessors act like private methods. The problem with that is whenever you want to use CoreData elsewhere in your app, you need some extra code.
I've figured you need to either supply your own method that exposes the managed object context, such as...
- (NSManagedObjectContext *)getManagedObjectContext
{
return self.managedObjectContext;
}
...which will allow other parts of your app to use it.
Or you would need to jam pack your app delegate with specific methods to return managed objects, ie getProducts or setUser.
Can anyone shed light on the reasoning here?
The reason for this is because you should be using dependency injection in your designs. This is the recommended design by the Core Data team. What is expected is that your app delegate will set the NSManagedObjectContext reference in your root view controller(s). From there the controllers will set or inject the necessary dependencies in the following view controllers.
This will lead to a more flexible design. I discussed it in depth in my article on the MDN (http://www.mac-developer-network.com/articles/cd0004.html).
If your project is big and needs to access the managed object context from outside of the AppDelegate,
I would just move the property declaration of managedObjectContext to the header file, as in:
#interface myAppDelegate : NSObject <UIApplicationDelegate> {
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
}
#property (retain,nonatomic) NSManagedObjectContext*managedObjectContext;
#end
Then the other parts of the app can just use appDelegate.managedObjectContext.
There's no reason to expose managedObjectModel or persistentStoreCoordinator outside the app delegate, though.
By the way I have a few comments about your usage of Objective-C:
Don't use get in front of the getter. For a property called foo, the getter should be
-(Foo*)foo;
and the setter should be
-(void)setFoo:(Foo*)_foo;
By convention, get... is used when a pointer is fed as a method argument, as in -[NSString getCharacters:range:] (See the Apple doc).
Follow the proverb, when in Rome, do as the Romans do.

Why do I get this error "error: expected specifier-qualifier-list before 'NSManagedObjectContext'?

1) I have the CoreData.framework imported. In Groups & Files I see it in the Framworks list together with UIKit.framework, Foundation.framework, CoreGraphics.framework.
2) I have this code, which should actually work. Don't know what that error means...
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface RootViewController : UITableViewController <CLLocationManagerDelegate> {
NSMutableArray *dataArray;
NSManagedObjectContext *managedObjectContext; // HERE's THE ERROR LINE
}
Edit: After importing CoreData, the error disappeared. BUT: Actually UIKit contains it, or not??
#import <CoreData/CoreData.h>
I have an Apple example code and they NEVER import CoreData, and it works.
Apple is taking advantage of the precompiled header (.pch file). Your projects do too when you start with a template and check the "Use Core Data for storage" option.
Also, you can use the precompiled header file to add any header files you wish to import for all source files in your target and project.
You need to link the CoreData framework and import CoreData/CoreData.h in the header file. UIKit does not contain the Core Data framework.