error when using core data - iphone

hello i'm trying to use core data to read and write user data.
the code i'm using is as follows:
AppDelegate interface
#interface PopAdsAppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>
{
#private
NSManagedObjectContext *managedObjectContext_;
NSManagedObjectModel *managedObjectModel_;
NSPersistentStoreCoordinator *persistentStoreCoordinator_;
}
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UITabBarController *tabBarController;
#property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#end
the code i'm getting error in is this in the .m file:
PopAdsAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"User" inManagedObjectContext:context];
this line in specific
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"User" inManagedObjectContext:context];
the .xcdatamodeld file i have is called UserData.xcdatamodeld and i have entity called "User".
To be honest i don;t know where in the code i should provide the UserData.xcdatamodeld file name?!! all i see in the examples is only the entity name!
the error i'm getting is:
[PopAdsAppDelegate managedObjectContext]: unrecognized selector sent to instance 0x180d60
2011-12-25 13:36:37.008 PopAds2[15645:707] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[PopAdsAppDelegate managedObjectContext]: unrecognized selector sent to instance 0x180d60'
* First throw call stack:
(0x3435f8bf 0x345af1e5 0x34362acb 0x34361945 0x342bc680 0x3c87 0x41a9 0x35d6bc39 0x35cc36e9 0x35cc36b3 0x35cc35d5 0x323718a5 0x32366545 0x32366639 0x32366243 0x32366179 0x34333b03 0x343332cf 0x34332075 0x342b54dd 0x342b53a5 0x30b39fcd 0x37736743 0x305d 0x2ff4)
terminate called throwing an exceptionkill

Your appDelegate have no managedObjectContext method as it seems from the error.
Edit:
Take a look at those links:
Ray Wenderlich Blog
Cocoa Dev Central
Mobile Tuts
And if those aren't enought, you could always Google it :)

Why don't you create a new project in Xcode and select the "Core Data" checkbox in the wizard? Xcode will then create a new project for you that uses Core Data. You usually don't have to setup Core Data yourself.

It seems that you have not synthesized your property
Add
#synthesize managedObjectContext;
to your AppDelegate.m file
EDIT
I thought you just forgot to initialize your property but it seems that you are just beginning to learn CoreData.
The more efficient approach to learn how to initialize CoreData is to study the code of the defaut implementation provided by Apple. Create a new project and in the wizard chose to use CoreData. You will get a working code and see the two setters being overridden. At their first call the private attributes are initialized. This is lazy loading, initialize when first need it.
You should also read the CoreData starting point and especially CoreData programming guide

Related

Property not found in object type

I am working on a simple core data app that uses a tableview and a detail view. I am getting the error message stating that the property managedObjectContext is not found in the object type ChildrenTVC. The problem is that it really is there. I have cleaned the project and deleted the derived data. There must be something else going on.
Here is the code for the object header:
#interface ChildrenTVC : CoreDataTableViewController <AddChildTVCDelegate>
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
#end
and the code for the implementation file:
#import "ChildrenTVC.h"
#implementation ChildrenTVC
#synthesize managedObjectContext = _managedObjectContext;
#synthesize fetchedResultsController = _fetchedResultsController;
Here is the app delegate file where the error registers:
#import "AppDelegate.h"
#import "ChildrenTVC.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize managedObjectContext = __managedObjectContext;
#synthesize managedObjectModel = __managedObjectModel;
#synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: ( NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
ChildrenTVC *controller = (ChildrenTVC *)navigationController.topViewController;
controller.managedObjectContext = self.managedObjectContext;
return YES;
}
The error is at the controller.managedObjectContext. A lot of this is boilerplate code so there is not much to it. I just can't figure out why it is not seeing the property for the managed object context. The property is in the code.
UPdate:
I ended up recreating the file entirely. I am not sure what references are still in place when a file is changed, but something was pointing to the wrong file. I had to empty the trash to get the new file work properly. It seems to work now, though. All the research I did ended up revealing that there are some things that are unexplained in Xcode.
I had solved this issue by entirely recreating the header file, but I never really understood why it happened. What I did not understand then were the effects of the changes that I had made to the app. The real problem was an issue in the sequence that the header files were imported. I had changed the #import in two implementation files and that caused the compiler to not read one of the header files. I could see that the code was there, but the compiler could not read the code because it was not importing it. That gave way to the error I was receiving. My solution simply reversed the change that I had made. It would more easily have been solved by simply removing the #import of the view controller header file on the app delegate. I recently tested this and it was the correct solution.
You are type casting the navigationController.topViewController but i assume it isn't one in reality. Try this
ChildrenTVC *controller = [[ChildrenTVC alloc] init];
NSArray *vcArray = NSArray *vcArray = [NSArray arrayWithObject:controller]:
[self.window.rootViewController setViewControllers:vcArray animated:NO];
What happens if you make following changes to ChildrenTVC.h
#interface ChildrenTVC : CoreDataTableViewController <AddChildTVCDelegate>
{
NSManagedObjectContext *managedObjectContext;
NSFetchedResultsController *fetchedResultsController;
}
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
#end

Objective-C: Many to many relationship with CoreData

I've an iPhone applications with 2 models, Category and Content, which have a many-to-many relationship.
This is the code:
Content
#interface Content : NSManagedObject {
}
#property(readwrite, retain) NSString *type;
#property(readwrite, retain) NSString *mainText;
...
#property (copy) NSSet * categories;
#end
Category
#interface Category : NSManagedObject {
}
#property (nonatomic, retain) NSNumber * id;
#property (nonatomic, retain) NSNumber * active;
...
#property (copy) NSSet * contents;
#end
And then this operation:
...
NSSet *tmp_set = [NSSet setWithArray:some_array_with_contents objectsAtIndexes:custom_indexes]];
cat.contents = tmp_set;
[[DataModel managedObjectContext] save:&error];
...
On the last line, the app crashes badly saying:
-[__NSCFSet _isValidRelationshipDestination__]: unrecognized selector sent to instance 0x5c3bbc0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet _isValidRelationshipDestination__]: unrecognized selector sent to instance 0x5c3bbc0'
Your relationship properties should not use copy. They should retain e.g:
#property (nonatomic, retain) NSSet* categories;
You don't want to copy a set of managed objects because you would end up with duplicate objects in the object graph. That is going to cause big problem.
However, that is not the immediate problem. The immediate problem is that something is causing a selector intended for a managed object to be sent to the set itself.
Most likely this is caused by directly assigning the copied set to the relationship directly instead of using one of the accessor methods defined in the .m file. The #dynamic directive will not create a setCategories method because this is a managed object so you don't get proper KVO notifications and the context does not update properly. When it tries to save it sends validation messages to the set object instead of the objects it contains.
You should have a method like addCategoryObjects: in the implementation file. Removing the copy and using those methods should resolve the problem.

where to implement Core Data?

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?
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.
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;
}
...
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).
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?

Release of NSManagedObject

I have based a portion of an app on Apple's CoreDataRecipes example code attainable at
http://developer.apple.com/library/ios/#samplecode/iPhoneCoreDataRecipes/Introduction/Intro.html
After some modifications I spent a good few hours tracking down a bug which I must have introduced, but which I solved by removing two lines of code present in apple's code.
I added an author attribute to the NSManagedDataObject recipe, identical in implementation - as far as I could tell - to other string attributes which recipe already had. My new attribute became a zombie after entering and leaving the modal view controlled by IngredientDetailViewController. The dealloc method of IngredientDetailViewController was
- (void)dealloc {
[recipe release];
[ingredient release];
[super dealloc];
}
Having tracked down the bug, I commented out the releases on the recipe and the ingredient (another NSManagedObject) and my app now seems to be functioning. I have now discovered that my code works with or without those release calls; the bug must have been fixed by another change I made. I am now wondering
Why was apple's example code written like this originally?
What was it about the original attributes of the NSManagedObject recipe which meant that they were not susceptible to zombification from the dealloc calls?
If the above hasn't displayed my ignorance enough, I should point out that I am new to Objective C and iPhone development but I would really like to understand what's going on here.
EDITED IN RESPONSE TO COMMENTS AND UPDATED:
I now cannot replicate the zombie creation by uncommenting those lines, obviously another change during bugshooting did the trick. Some of what I originally asked is now invalid but this has left me further confused as to the use of release for NSManagedObjects, since now functionality seems identical with or without those calls. My main question now is just whether or not they should be there. The crash was occuring upon saving in the IngredientDetailView. Here is the header:
#class Recipe, Ingredient, EditingTableViewCell;
#interface IngredientDetailViewController : UITableViewController {
#private
Recipe *recipe;
Ingredient *ingredient;
EditingTableViewCell *editingTableViewCell;
}
#property (nonatomic, retain) Recipe *recipe;
#property (nonatomic, retain) Ingredient *ingredient;
#property (nonatomic, assign) IBOutlet EditingTableViewCell *editingTableViewCell;
#end
and the save method:
- (void)save:(id)sender {
NSManagedObjectContext *context = [recipe managedObjectContext];
/*
If there isn't an ingredient object, create and configure one.
*/
if (!ingredient) {
self.ingredient = [NSEntityDescription insertNewObjectForEntityForName:#"Ingredient"
inManagedObjectContext:context];
[recipe addIngredientsObject:ingredient];
ingredient.displayOrder = [NSNumber numberWithInteger:[recipe.ingredients count]];
}
/*
Update the ingredient from the values in the text fields.
*/
EditingTableViewCell *cell;
cell = (EditingTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
ingredient.name = cell.textField.text;
cell = (EditingTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]];
ingredient.amount = cell.textField.text;
/*
Save the managed object context.
*/
NSError *error = nil;
if (![context 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();
}
[self.navigationController popViewControllerAnimated:YES];
NSLog(#"in ingredient detail save after ingredient pop; - recipe.author is %#", recipe.author);
}
since I'm a new user I can't put the screenshot of the data model here, so here is a link to it: data model screenshot
and finally the Recipe header:
#interface ImageToDataTransformer : NSValueTransformer {
}
#end
#interface Recipe : NSManagedObject {
}
#property (nonatomic, retain) NSString *instructions;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *overview;
#property (nonatomic, retain) NSString *prepTime;
#property (nonatomic, retain) NSSet *ingredients;
#property (nonatomic, retain) UIImage *thumbnailImage;
#property (nonatomic, retain) NSString *author;
#property (nonatomic) BOOL *isDownloaded;
#property (nonatomic) BOOL *isSubmitted;
#property (nonatomic, retain) NSString *uniqueID;
#property (nonatomic) float averageRating;
#property (nonatomic) float numberOfRatings;
#property (nonatomic, retain) NSManagedObject *image;
#property (nonatomic, retain) NSManagedObject *type;
#end
#interface Recipe (CoreDataGeneratedAccessors)
- (void)addIngredientsObject:(NSManagedObject *)value;
- (void)removeIngredientsObject:(NSManagedObject *)value;
- (void)addIngredients:(NSSet *)value;
- (void)removeIngredients:(NSSet *)value;
#end
Thanks again.
Please take a look at Core Data documentation, since Core Data “owns” the life-cycle of managed objects you should not be releasing them at all.
The only time you would release a managed object would be if you had retained it yourself. Seeing as your property definition says that it is retaining the recipe and ingredient objects, when your ingredientviewcontroller is deallocated, it needs to release the recipe and ingredient objects.
When you do something like myIngredientViewController.ingredient = anIngredient, it's like calling a method which would look something like:
- (void)setIngredient:(Ingredient *)ing {
[self willChangeValueForKey:#"ingredient"];
Ingredient *oldIngredient = ingredient;
ingredient = [ing retain];
[oldIngredient release];
[self didChangeValueForKey:#"ingredient"];
}
So in your save method, when it assigns self.ingredient = ..., that is retaining your object yourself - you now have an ownership interest in that object, so you need to release that in your dealloc.
If you think about it in another way, the managed object context has added 1 to the retain count because it has an ownership interest in it, and you have added 1 to the retain count because you want to maintain an ownership interest in it. When you relinquish your ownership interest, by releasing it during dealloc, the retain count goes down 1 and when the managed object context releases it, the retain count would go to zero and it would be deallocated.
That is how normal objects operate, and how you would treat managed objects in most circumstances, but there are a few caveats for managed objects - as the previous poster indicated, the lifecycle of managed objects is controlled by the managed object context, and there are various things that can happen to a managed object that may mean that although the object still exists, it may be deleted in the context, or a fault in the context, or maybe even reused with different data.
You don't usually have to worry about that, but if you use custom managed objects which have their own instance variables that you need to manage the memory for, or other things you want to do when they are created, fetched, turned into faults etc, then you would need to look at the awakeFromInsert, awakeFromFetch, willTurnIntoFault, didTurnIntoFault etc.
But all that is advanced stuff that you won't need until you get into more complex scenarios.
HTH

Core Data Strings

Im new to using core data and having really basic problems. Im trying to have the user enter a string and then be able to save that string and allow it to be returned to them at some point. But i cannot seem to get it to save. In fact the program quits when I attempt to run the following method. I can post the rest of my project, but i thought maybe that would be annoying so let me know if seeing it in greater detail would help. Thanks so much.
James
.h: file
#import <UIKit/UIKit.h>
#import "People.h"
#class rootViewController;
#interface data : UIView <UITextFieldDelegate>{
rootViewController *viewController;
UITextField *firstName;
UITextField *lastName;
UITextField *phone;
UIButton *saveButton;
NSMutableDictionary *savedData;
//Used for Core Data.
NSManagedObjectContext *managedObjectContext;
NSMutableArray *peopleArray;
}
#property (nonatomic, assign) rootViewController *viewController;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) NSMutableArray *eventArray;
- (id)initWithFrame:(CGRect)frame viewController:(rootViewController *)aController;
- (void)setUpTextFields;
- (void)saveAndReturn:(id)sender;
- (void)fetchRecords;
#end
.m file:
-(void)saveAndReturn:(id)sender{
People *userEnteredName = (People *)[NSEntityDescription insertNewObjectForEntityForName:#"People" inManagedObjectContext:managedObjectContext];
[userEnteredName setName:firstName.text];
//NSError *error;
//if (![managedObjectContext save:&error]) {
// This is a serious error saying the record could not be saved.
// Advise the user to restart the application
//}
[peopleArray insertObject:userEnteredName atIndex:0];
}
From the error you gave you must have named the People object differently - in the model are you using "People" for both class and entity name (those can be the same)?
Edit:
After reviewing your code, you had multiple problems:
1) In the app delegate you did "[data alloc]" but no init. That was where you set the managed object context, but it was never used... not just because of the lack of an init but because...
2) The place where the data controller was really built and used from was the rootViewController. That's the one that is actually doing all the work, the one in the app delegate is just discarded.
3) So where to get the context then? Honestly the best spot is in the data controller, one fix I know worked was putting this line before every time the context was accessed:
#import "UserProfileAppDelegate.h"
// Then in the method before the use of context........
self.managedObjectContext = [((UserProfileAppDelegate *)[[UIApplication sharedApplication] delegate]) managedObjectContext];
When that was in place, the project ran. I think though you should put that into something like a viewDidLoad on the data controller (if it has a view that is ever used).