insertNewObject into coreData - iphone

I am trying to insert an NSData object into CoreData, So I have had to add everything to my application after the fact because I never started it with coredata selected. thats okay though as I have started a new project and copied all of the appropriate code across to get it all set up.
From there I am now trying to set up the insertNewObject method. However this is giving me some issues.
I am first of all trying to pass it some NSData from another method like this
[self insertNewObject:myData];
and then I am using the insertNewObject method like this
- (void)insertNewObject:(id)sender
{
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:#"Entity" inManagedObjectContext:context];
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
[newManagedObject setValue:sender forKey:#"manufacturers"]; //not sure if this is correct, but sender has myData, and #"manufactures" is the attribute of my entity.
// Save the 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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
From here what happens is that when the thread gets too the third line "NSManagedObject *newManagedObject..." The application crashes and I get this error
> 2012-04-27 11:18:21.579 thecode[1452:fb03] *** Terminating app due to
> uncaught exception 'NSInternalInconsistencyException', reason:
> '+entityForName: could not locate an NSManagedObjectModel for entity
> name 'Entity''
> *** First throw call stack: (0x14b5022 0x1952cd6 0x61d47 0xa8102 0x1a595 0x199a4 0x18b99 0x15097 0x1136a49 0x1134e84 0x1135ea7
> 0x1134e3f 0x1134fc5 0x1079f5a 0x1c02a39 0x1ccf596 0x1bf9120 0x1ccf117
> 0x1bf8fbf 0x148994f 0x13ecb43 0x13ec424 0x13ebd84 0x13ebc9b 0x215b7d8
> 0x215b88a 0x733626 0x762d 0x1c75 0x1) terminate called throwing an
> exception
I have set up the entity and attribute needed for this inside my xcdatamodeld
any help would be greatly appreciated.

I am not aware of your CoreData model but i guess you don't have a entity with the name "Entity" so this might cause the crash as you are trying to insert a new entity with name "Entity" here:
[NSEntityDescription insertNewObjectForEntityForName:#"Entity" inManagedObjectContext:context];
As you are fetching an entity description one line above but never using it later on, i guess you are trying to insert this entity. So what you have to do is basically:
[NSEntityDescription insertNewObjectForEntityForName:entity.name inManagedObjectContext:context];
Hope this helps!

Also check that your managedObjectContext is not nil. You'll get exactly this error when it is. When you instantiated the fetchedResultsController you associated an NSManagedObjectContext with it. Where did you get that context from?
If this is in a view controller that you've drilled down into, be sure you're passing the managedObjectContext from the app delegate into each subsequent controller so that they all have access to it. You can then pass it to the fetchedResultsController correctly.

Related

Lost data with multiple NSManagedObjectContexts on iOS7

I'm in the progress of updating an existing app for iOS 7 and I've been having some issues with Core Data saving objects. It's a fairly straightforward master-detail style data entry app that uses Core Data for the storage.
When adding a new record I use a second (temporary) managed object context to prevent the record appearing in the list before the record is saved. When a record is added and saved it is visible in the list as expected. However if I exit the app (it doesn't run in the background) and then restart it the record is no longer present. The record is present in the database (visible using the SQLite Manager Firefox plugin anyway), but it just doesn't show in the app.
I've managed to reproduce this using the code that Xcode produces when creating a new project. I've created a new master-detail application and ticked the Use Core Data box to get the example code, then made the following changes:
Add the following to MasterViewController.m
-(void)save:(NSManagedObjectContext*)context
{
if (context != [self.fetchedResultsController managedObjectContext])
{
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
[dnc addObserver:self selector:#selector(addControllerContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:context];
}
NSError *error;
if (![context save:&error])
{
abort();
}
if (context != [self.fetchedResultsController managedObjectContext])
{
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
[dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:context];
}
}
- (void)addControllerContextDidSave:(NSNotification*)saveNotification
{
[[self.fetchedResultsController managedObjectContext] mergeChangesFromContextDidSaveNotification:saveNotification];
}
Replace the supplied insertNewObject in insertNewObject with the following to create a new temporary context for adding
- (void)insertNewObject:(id)sender
{
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
context.persistentStoreCoordinator = [[self.fetchedResultsController managedObjectContext] persistentStoreCoordinator];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
[newManagedObject setValue:[NSDate date] forKey:#"timeStamp"];
// Save the context.
[self save:context];
}
I also set the app to not run in the background.
If I run this against iOS 6 it behaves as expected i.e. I tap Add and a new record appears, then exit and restart the app and the record is still present.
However if I run the same code against iOS 7 it doesn't work correctly. Tapping Add causes the new record to appear, but if I exit and them restart the app the record is not shown. As mentioned above it is present in the database however.
Interestingly, I've discovered that it might be in some way related to the change in the journaling mode of the SQLite database. If I add the following options in the call to addPersistentStoreWithType I get the expected behaviour running on iOS 7
NSDictionary *options = #{ NSSQLitePragmasOption : #{#"journal_mode" : #"DELETE"} };
So, to the questions (and thanks for reading this far!)
Has anyone else seen this behaviour (or is anyone able to reproduce it based on the description above)?
Is there something wrong with the way I am using a temporary context that I was just lucky with prior to iOS 7, or does this look like an issue with the Core Data framework on iOS 7?
Cheers
Neil
Edit 1:
In answer to Wain's question about saving the main MOC, I was under the impression that this isn't actually necessary because the data is already saved, the merge just updates the already saved changes from the temporary context in to the main context. That said the test code does contain the following methods and saveContext is called on shutdown, however [managedObjectContext hasChanges] returns false so nothing actually gets done at this point
-(void)applicationWillTerminate:(UIApplication *)application
{
// Saves changes in the application's managed object context before the application terminates.
[self saveContext];
}
-(void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil)
{
if ([managedObjectContext hasChanges])
{
if (![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.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
}
It seems to be fixed when you save your main context after merging changes:
- (void)addControllerContextDidSave:(NSNotification*)saveNotification
{
[[self.fetchedResultsController managedObjectContext] mergeChangesFromContextDidSaveNotification:saveNotification];
[self save:[self.fetchedResultsController managedObjectContext]];
}
UPDATE: this error was caused by using cache in NSFetchedResultsController. So, the data isn't lost, it's just not displayed by your NSFetchedResultsController. Further investigation is needed to find out why cache isn't updated when its MOC merges changes, but isn't saved.

Why does releasing my CoreData context/entity make my app crash?

I have a simple CoreData app which allows you to add items to a list, displayed in a table view. When the user types in a new item, the following method is called:
- (void)addNewItem:(NSString *)item
{
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
Item *newItem = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newItem setName:item];
// Save the context.
NSError *error = nil;
if (![context save:&error])
{
//error handling code
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[context release];
[entity release];
[newItem release];
The app always allows you to add one item to the list, but then crashes if you try to add a second. If I remove "[newItem release];", the app will allow you to add 4 list items, and then suddenly crash when you try to enter a fifth.
The app will only work properly if all three of those release statements at the end of the method are removed. Can anyone explain why?
The objects are all autoreleased (because you never alloc init anything), so you're not supposed to release them yourself. It's not predictable when your app will crash as far as I can tell, but it will eventually crash.
Just to clarify #BoltClock's answer. It's not about alloc, init only, but there's also new..., copy..., etc.
You should read Memory Management Guide, especially Memory Management Rules.

Core Data error: _Unwind_Resume called from function _PFFaultHandlerLookupRow in image CoreData

I'm getting this weird error from Core Date and I cant understand why.
The code below is executed when I delete a row of a UITableView.
I pass a string and an object to the method below and it fetches the article in a database table that has that string and has a foreign key to that object. Then I delete that object and reload the table.
- (void)deleteFavorite:(NSString *)link inFolder:(Favorites *)f {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *favsDecriptor = [NSEntityDescription entityForName:#"Favorites" inManagedObjectContext:context];
[request setEntity:favsDecriptor];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(belongsTo == %#) AND (link = %#)", f, link];
[request setPredicate:predicate];
NSError *error = nil;
NSMutableArray *fav = [[NSMutableArray alloc] init];
fav = [[context executeFetchRequest:request error:&error] retain];
if (![context save:&error]) {
NSLog(#"Cannot fetch the story from the fetch request.");
}
NSLog([[fav objectAtIndex:0] title]);
error = nil;
[context deleteObject:[fav objectAtIndex:0]];
if (![context save:&error]) {
NSLog(#"Can't delete the fav! %#", error);
}
}
The app instantly crashes and I get this message in the console.
But when I launch the app afterwards, the row has been deleted.
Detected an attempt to call a symbol in system libraries that is not present on the iPhone:
_Unwind_Resume called from function _PFFaultHandlerLookupRow in image CoreData.
Please help!
Thanks in advance to everyone!
This is probably related to a bug within Core Data itself. I had the same error come up (I asked about it here in SO) and my only fix was to change the keywords in the predicate that still allowed the same results. It took some experimenting to find the right combination. Not ideal, but that's the best answer I can offer based on my experience.
Is it possible that you are holding a reference to the delete object or that the deleted object is an observer and is getting a callback after its been deleted? I had something similar to this recently, though slightly different error message. In my case, I also crashed upon deletion (under some conditions) but when I relaunched the object-to-be-deleted had, in fact, been deleted.
If you haven't already done so, under the Run menu select Stop on Objective-C Exceptions. This helped me track down the root cause of my crash. In my case it was KVO observer getting callback of change of value of a property of deleted NSManagedObject.

Saving an instance of an NSManagedObjectContext does not permanently save a mutable transformable attribute

I'm using Core Data + sqlite as a data cache. The app reads in a file into core data, then uses Core Data to run the app. Saves are done to both the NSManagedObjectContext and to the file. I've noticed, though, if I quit the app and reload it without repopulating the Core Data database, some (but not all) of the data saved using -save: is not being saved to the data store.
Changes to my managed object are all done in a single batch on the main thread, with the -save: message being sent after all changes are completed. The data that isn't being saved is a transformable attribute, the only transformable attribute in the core data object. Here's the code that saves the object:
NSInteger columnIndex = [headers indexOfObject:questionID];
if (NSNotFound != columnIndex) {
// parsedLine is a mutable array already
NSMutableArray *parsedLine = person.dataFromFile.parsedLine;
[parsedLine replaceObjectAtIndex:columnIndex withObject:answer];
person.dataFromFile.parsedLine = parsedLine;
person.questionsAnsweredByPerson = [NSNumber numberWithInt:[FileParser questionsAnsweredInRow:person.dataFromFile.parsedLine withRowHeaders:headers]];
person.address.street.questionsAnsweredByPeopleOnStreet = [NSNumber numberWithInt:[self questionsAnsweredByPeopleOnStreet]];
//NSLog(#"rawLineBefore:\n%#", person.dataFromFile.rawLine);
person.dataFromFile.rawLine = [ReconstructCSV composeCSVLineFromArray:person.dataFromFile.parsedLine];
//NSLog(#"rawLineAfter:\n%#", person.dataFromFile.rawLine);
Voter_SurveyAppDelegate *appDelegate = (Voter_SurveyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSError *error;
NSManagedObjectContext *managedObjectContext = [appDelegate managedObjectContext];
if (![managedObjectContext save:&error]) {
// XXX inform the user there was a fatal error opening the file. Low disk space?
NSLog(#"Unresolved error - could not save managedObjectContext - %#, %#", error, [error userInfo]);
abort();
}
return YES;
}
abort(); is not getting called, so I assume -save; is getting called properly.
I doubt it is related, but after this code is run on the main thread, I perform an NSFetchRequest using a new NSManagedObjectContext on a different thread. Nothing else takes place related to Core Data on other threads.
Why isn't the transformable attribute getting saved?
The problem is that transformable properties don't like Mutable objects. As noted in an answer to this question, an NSMutableDictionary wasn't getting saved. In my case, it was an NSMutableArray.

Save object in CoreData

I am using CoreData with iPhone SDK. I am making a notes app. I have a table with note objects displayed from my model. When a button is pressed I want to save the text in the textview to the object being edited. How do I do this? I've been trying several things but none seem to work.
Thanks
EDIT:
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newManagedObject setValue:detailViewController.textView.text forKey:#"noteText"];
NSError *error;
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();
}
The above code saves it correctly but it saves it as a new object. I want it to be saved as the one I have selected in my tableView.
You should check out the Core Data Programming Guide. It's hard to know exactly what you want from the question, but the basic idea is:
-(IBAction)saveNote { //hooked up in Interface Builder (or programmatically)
self.currentNote.text = self.textField.text; //assuming currentNote is an NSManagedObject subclass with a property called text, and textField is the UITextField
}
//later, at a convenient time such as application quit
NSError *error = nil;
[self.managedObjectContext save:&error]; //saves the context to disk
EDIT: If you want to edit a preexisting object, you should get the object from the fetched results controller, e.g. NSManagedObject *currentObject = [fetchedResultsController objectAtIndexPath:[self.tableView indexPathForSelectedRow]], then edit that object. I'd also recommend using a custom subclass of NSManagedObject with property declarations, rather than using setValue:forKey, since it's more flexible.