Core data edit function - iphone

I already have a tableView with data in it. IF you tap a cell/row it pushes to an edit type of view. Is there anyway to edit core data's data other than: By edit, i mean i already have data inserted into my context. I have loaded my data into my view, the user can change the existing data, and re save it.
.h
//Below is the entity/entity's class name 'Amm'
Amm *amm;
.m
-(IBAction)save
{
[self.amm setValue:self.nameField.text forKey:#"name"];
[self.amm setValue:self.nicknameField.text forKey:#"nickname"];
[self.navigationController popViewControllerAnimated:YES];
NSError *error;
if (![self.managedObjectContext save:&error]) {
//Handle Error
}
}
I want this code to work, however the design pattern of my app isnt allowing this code to work for me as it does in other parts of my app. Thank you very much for any and all help!

I assume from what you've said you have:
A table view listing your managed objects
A view where you can edit the values of a managed object
A save button bound to the save method
What's the actual issue? I'm assuming when you tap save that:
The values in self.nameField.text isn't setting self.amm.name
The values in self.nicknameField.text isn't setting self.amm.nickname
Is that right? If so perhaps try the following code to set the managed object values:
self.amm.name = self.nameField.text
self.amm.nickname = self.nicknameField.text
If that's not the issue and you are actually setting the managed object values properly, is it that you just need to refresh the table-view? Perhaps use some NSLog commands to log every step of the applications progress.

Related

App crashes when adding names to UIPickerView

When I click a button, a UIAlertView prompts the user to type a name. This name is then created as a new 'Customer' object and inserted into a mutable array.
There is a separate mutable array called 'CustListByName', which stores a list of all names.
The problem im having is that when adding a second or third name, the app crashes. Sometimes it happens on the second try, other times on the third try. There is no information given in the debugger except for (lldb). The program reports EXC_BAD_ACCESS and then it dumps me to a screen with a bunch of assembly code.
The crash is happening in these lines of code:
Essentially, it clears the array of names and then repopulates it based upon the object array. I've studied in step by step with a breakpoint but everything seems correct up until the point of crash. It is also confusing why this happens on the second or third try, never the first.
[custListByName removeAllObjects];
for (Customer *object in custListByObject) {
[custListByName addObject:object->name];
}
Here is the code where a customer is created and inserted everytime the new customer button is clicked:
Customer *tempCust = [[Customer alloc] init];
tempCust->name =[[alertView textFieldAtIndex:0] text];
[custListByObject addObject:tempCust];
[tempCust release];
I would really appreciate help with this, thanks!
What I suspect is happening is that the UIPickerView is attempting to load a row using information from your customer array after you have already cleared it, and before you repopulate it. This would cause a bad access error.
What you may consider doing instead, is keeping two arrays, an NSMutableArray for loading the customers, and an NSArray as the actual data source for the UIPickerView. Then right before you reload the UIPickerView, you say:
dataSourceArray = [loadingArray copy];
[pickView reloadAllComponents];
Hopefully this helps.
Edit:
Here's what your updated code would look like if your loading array was called loadingCustListByName:
[loadingCustListByName removeAllObjects];
for (Customer *object in custListByObject) {
[loadingCustListByName addObject:object->name];
}
custListByName = [loadingCustListByName copy];
[pickView reloadAllComponents];
Doing this will ensure that the UIPickerView's datasource array always matches up with the number of rows it thinks it has.

iOS Core Data - Updating Multiple records

I've scoured and still haven't found anything that quite works. Either the question/answer is too old or it simply hasn't worked for me. This is my first attempt at "my own" app. As it seems a right of passage, I'm making a checklist app. Here's what I'm looking for:
My Data Store contains 4 attributes: name, category, isChecked, isActive (more will surely follow as I expand)
When my View Controller initially loads, the NSFetchedResultsController has an NSPredicate that only retrieves the records whose attribute isActive is YES (or [NSNumber numberWithBool:YES). It then takes those records and displays them into the appropriate cells for the user. When a user clicks on a cell, the Data Store updates and changes the isChecked attribute accordingly. Everything works good to this point.
What I need to do now is to be able to remove the items (1 or more) from the list. Specifically, I need it to update the Data Store attributes isChecked and isActive to NO only if it's current isChecked attribute is YES. (I'm not looking to delete the record from the data store as they will be used to build up the database for the users future use.)
I've used, among other things:
[[[self fetchedResultsController] fetchedObjects]
setValue:[NSNumber numberWithBool:NO]
forKey:#"isChecked"];
This does actually work, it removes the checkmark(s) and updates the store accordingly. Problem is, not only am I making another request to the data store for the isActive items, it also searches the entire "Active List" that was fetched and sets each of their isChecked attributes to NO. This may not be too big of an issue for small lists, but as the list(s) expand this can be an issue.
The other problem is, if I add:
[[[self fetchedResultsController] fetchedObjects]
setValue:[NSNumber numberWithBool:NO]
forKey:#"isActive"];
It sets ALL of my list items to NO (as well as a second data store request within the same method.)
So my question is: How can I get through the list, find only the items that are checked and update only those records (set both the isChecked && isActive attributes = NO) whose isChecked attribute is YES rather than working through the entire list?
I've tried creating a separate fetchedResultsController specifically for this buttons action, and it did work (that is to say, it didn't crash) but the debugger popped out a rather large 'Serious Application Error'. I won't post the error message as it's long and most likely irrelevant to any solution.
Any assistance would be greatly appreciated. Thanks in advance and please be gentle :-].
EDIT
I have tried using a for loop, for (NSString *item in fetchedResultsController) but I get the error ...may not respond to 'countByEnumeratingWithState:objects:count'
It seems a loop of sorts is what's needed here, but again, nothing I can find is relevant or it's outdated. Again, thanks for any assistance.
Edit 2
Here is the original error I got when I ran a second separate fetchRequestController for this button/method:
An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (4) must be equal to the number of rows contained in that section before the update (4), plus or minus the number of rows inserted or deleted from that section (0 inserted, 3 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)
You can just loop over the fetchedObjects collection and change the managed objects. After changing them you'll need to reload your list (I guess you use a tableview).
I don't know what your classes are named, but in general you can just loop over the collection of managed objects and change them. Remember that you need to save your managed object context if you want to keep these changes for when the app closes.
NSArray* myCollection = [[self fetchedResultsController] fetchedObjects];
for(ActiveListData *managedObject in myCollection)
{
if(managedObject != nil && managedObject.isChecked)
{
managedObject.isChecked = NO;
managedObject.isActive = NO;
}
}
If you want to do the check on all object in the database you'll need a new method in your NSFetchedResultsController that has a predicate checking on isChecked and then loops over and edits the result collection.
You might want to post your error code as we could be able to point out what you did wrong.
Edit: If you're not familiar with using Core Data the apple documentation provides a lot of information: http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/Articles/cdBasics.html
Thanks to #ggfela for his answer. The processes of his answer were spot on. Here is the actual code I put into my button/method, in hopes of it helping someone else in the future:
NSArray *moc = [[self fetchedResultsController] fetchedObjects];
for (ActiveListData *item in moc) {
if (item != nil && item.isChecked.boolValue == 1) {
item.isChecked = [NSNumber numberWithBool:NO];
item.isActive = [NSNumber numberWithBool:NO];
}
}
// Call to Data Store to update the list
NSError *error;
if (![self.managedObjectContext save:&error]) {
FATAL_CORE_DATA_ERROR(error);
return;
Explanation:
Load the contents of the result from calling the fetchedResultsController method into a temporary variable named moc
Use a for loop to cycle through the array of moc. ActiveListData is the NSManagedObject subclass that I created for my Core Data and is the proper place to insert the separated values/attributes from the data store. From there, it's pretty simple, I ensure that item is not nil AND that the item's attribute is the value I need.
NOTE
Core Data does not store the bool values YES and NO but rather 1 and 0, respectively but when you call or compare the values, you simply can not compare the value of item.isChecked because it is being passed back to you as a bool not as an integer. You can not simply compare item.isChecked == YES either since the #property of isChecked is an NSNumber. So, in the case of the if I put item.isChecked.boolValue as this will give me the representing integer for it's bool value, in this case I have it check for a 1 (YES). (Sorry if my explanation is wrong and/or confusing, but this is how I understand it and is the only way this code works.)
Then, setting the new values of these attributes is like you would expect when setting any other variable. The only "tricky" difference with this is that the NSManagedObject subclass sets the #property of the isChecked and isActive to an NSNumber (as mentioned earlier) so in order to send the proper values back to Core Data you use the method numberWithBool of the NSNumber class.
And just in case anyone gets confused by my FATAL_CORE_DATA_ERROR(error) call this is simply a macro that was defined inside the Prefix.pch file to handle my errors from the managedObjectContext. You can use any (or none) error handling you choose.
Thanks again #ggfela for your help!! If anyone else has any other suggestions on how this code should be applied, then please let me know!
You can use NSBatchUpdateRequest to update multiple records
Examples:
https://www.bignerdranch.com/blog/new-in-core-data-and-ios-8-batch-updating/
http://code.tutsplus.com/tutorials/ios-8-core-data-and-batch-updates--cms-22164

Restoring Annotations via Core-Data

Currently, I have an application where a user clicks on a map and adds annotation points with certain subtitles. However, when the phone is power-cycled, all their added points are gone. I'm interested in making these annotations persistent. I've been trying to figure out how to do this with Core-Data, but after reading this tutorial here: http://www.raywenderlich.com/934/core-data-tutorial-getting-started, I'm a bit lost on where to start.
Any help would be appreciated, thanks.
If you have a core data application set up, you will only need to create an entity in the xcdatamodel file. Add attributes for whatever you may want to store.
latitude - double
longitude - double
title - NSString
etc. until you have what you want.
When you want to add an annotation, you should create a new core data object for your entity. It will look something like this
Location *newLocation = (Location *)[NSEntityDescription insertNewObjectForEntityForName:#"Location" inManagedObjectContext:self.managedObjectContext];
Location.latitude = ....
// and so on to store the information you want in its attributes;
You can set the attributes at different point if you change the values at a later point. You just need to be able to access the right object to go with the annotation. You should be able to do this by using NSFetchRequest in your managedObjectContext. You can use NSPredicate to filter the objects to the one you want. Then when you are ready to exit the app, save your context using
NSError *error = nil;
if ([managedObjectContext save:&error]) {
// handle the error;
}
which will store all the objects you've added to be used next time you open the app. You will then be able to create your annotations based on the objects in your managedObjectContext. I hope this is what you were looking for, or at least gives you an idea how to approach what you'd like to do.

iphone sql - data corrupted/ missing when reading

I'm using SQL to store data in my application. I read data in the app delegate and store it in an array like so.
(First I read from the database and store in aFlashcardSet and then this)
// Add the flashcardSet to the main Array
[mainSetsArray addObject:aFlashcardSet];
In my next view I then copy the data from the app delegate.
flashcardsAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
self.setsArray = delegate.mainSetsArray;
Then I pass one object from the set to the final view.
cardDetailViewController.thisCardSet = [setsArray objectAtIndex:row];
The problem is when I read the data in the final view and use to set UI elements the app crashes, the code works fine with data hard coded during the second phase and the database data is shown fine during the second phase (to populate a table view). I've tried outputting the data at all stages and it is all correct until the final view where it either crashes or shows incorrect values (file names or random letters rather than the actual text).
I have also tried to read the database data inside the final view and set it to thisCardSet but it still suffers the same problem.
Any ideas?
Thanks.
The problem was inside one of my custom classes I wasn't using self, so I was writing.
aString = string;
Rather than -
self.aString = string;
So I was losing data down the line, feel really stupid but hope it help's someone else =].

EXC_MEMORY_ACCESS when trying to delete from Core Data ($cash solution)

I have an application that downloads an xml file, parses the file, and creates core data objects while doing so. In the parse code I have a function called 'emptydatacontext' that removes all items from Core Data before creating replacements items from the xml data. This method looks like this:
-(void) emptyDataContext
{
NSFetchRequest * allCon = [[NSFetchRequest alloc] init];
[allCon setEntity:[NSEntityDescription entityForName:#"Condition" inManagedObjectContext:managedObjectContext]];
NSError * error = nil;
NSArray * conditions = [managedObjectContext executeFetchRequest:allCon error:&error];
DebugLog(#"ERROR: %#",error);
DebugLog(#"RETRIEVED: %#", conditions);
[allCon release];
for (NSManagedObject * condition in conditions) {
[managedObjectContext deleteObject:condition];
}
// Update the data model effectivly removing the objects we removed above.
//NSError *error;
if (![managedObjectContext save:&error]) {
DebugLog(#"%#", [error domain]);
}
}
The first time this runs it deletes all objects and functions as it should - creating new objects from the xml file. I created a 'update' button that starts the exact same process of retrieving the file the proceeding with the parse & build. All is well until its time to delete the core data objects. This 'deleteObject' call creates a "EXC_BAD_ACCESS" error each time. This only happens on the second time through.
Captured errors return null. If I log the 'conditions' array I get a list of NSManagedObjects on the first run. On the second this log request causes a crash exactly as the deleteObject call does.
I have a feeling it is something very simple I'm missing or not doing correctly to cause this behavior. The data works great on my tableviews - its only when trying to update I get the crashes.
I have spent days & days on this trying numerous alternative methods. Whats left of my hair is falling out. I'd be willing to ante up some cash for anyone willing to look at my code and see what I'm doing wrong. Just need to get past this hurdle.
Thanks in advance for the help!
Did you save the context after removing the objects in the for loop? Be aware that deleteObject: does not delete the object immediately, it simply schedule it for deletion when changes are committed, i.e., when you save the context.
EDIT: Your problem may be related to how you present your data to the user on your table view. Without additional code is difficult to tell exactly (are you using NSFetchedResultsController or not?), but my guess is that the interaction between deleting the data and showing them on the table is not correct. Probably, what is happening is that your table is told to visualize your data, but then, when you delete them, you are not updating correctly the table.
Wow, so after a few days of testing I went down a path that led me to tracking down Zombies & memory mgmt. This was a situation where the errors I was recieving were leftover from issues in another area. When parsing the data and placing it into Core Data I released an object that was to be autoreleased. So any subsequent calls to that item within core data (save, deletec, etc) caused a crash. Thank you all for your help and I vow to pay closer attention with my memory mgmt calls.