I'm having some issues getting Core Data to save new rows that I add when using a UITextField. Here is my method for inserting objects into my table view. What should happen is when I click the add button, a textfield should be added, and then go right into edit mode. Then when the user clicks done on the keyboard the textfield should end editing and then the textfield should save the entry into core data.
Edit: removed call to textFieldDidEndEditing in the insertNewObject:(id)sender method. It was crashing the app
- (void)insertNewObject:(id)sender {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
TehdaItem *item = [NSEntityDescription insertNewObjectForEntityForName:#"TehdaItem" 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.
// Putting the cell in edit mode
TehdaTableViewCell *editcell;
for (TehdaTableViewCell *cell in [self.tableView visibleCells]) {
if (cell.itemLabel.text == item.itemTitle) {
editcell = cell;
break;
}
}
[editcell.itemLabel becomeFirstResponder];
// The cell needs to call the method textfield did end editing so that it can save the new object into the store
// 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();
}
}
Here is my textFieldDidEndEditing method:
- (void)textFieldDidEndEditing:(UITextField *)textField {
TehdaTableViewCell *cell = (TehdaTableViewCell *) textField.superview.superview;
TehdaItem *item = [self.fetchedResultsController objectAtIndexPath:[self.tableView indexPathForCell:cell]];
//TehdaTableViewCell *cell;
item.itemTitle = cell.itemLabel.text;
}
Not really sure where to go from here. Any help would be appreciated.
Thanks.
You can use
NSError *error;
[item.managedObjectContext save:&error];
if (error) {
// Triage the problem and respond appropriately
}
in the - (void)textFieldDidEndEditing:(UITextField *)textField method. But if I were you I'd do some validation before you save the object.
Related
The program is very simple: it has a masterView and a detailView. In the detail view I should be able to edit the attributes from the corresponding object (basically adding a number to the already stored number). The problem is that I'm not sure how to save the changes.
I have this in an IBAction in the detailViewController:
- (IBAction)depositFunds:(id)sender
{
float change = [[self.detailItem valueForKey:#"balance"] floatValue] + [amountTextfield.text floatValue];
[self.detailItem setValue:[NSNumber numberWithFloat:change] forKey:#"balance"];
}
How can I save those changes?
I just tried importing the managedObjectContext from the masterView and saving it. It worked.
Do this, import the context:
- (void) setManagedObject:(NSManagedObjectContext *)managedObject
{
managedObjectContext = managedObject;
}
Pass the context through either the segue method or didSelectRowAtIndex method.
myDetailViewController *viewC = [segue destinationViewController];
[viewC setManagedObject:self.managedObjectContext];
Then add this to your saving method.
NSError *error = nil;
if (![managedObjectContext save:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
Cheers!
Is there any method to do this job? I could not find in the document.
To delete an object from database use the following method:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[managedObjectContext deleteObject:[userResults objectAtIndex:indexPath.row]];
[userResults removeObjectAtIndex:indexPath.row];
// Save the context.
NSError *error = nil;
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. 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.tableView reloadData];
}
Where userResults is the NSMutableArray type, which you use to fetch data and store temporary values
If you mean deleting you can use [context deleteObject:object] where context is your NSManagedContext and object is your NSManagedObject derived object.
I am working on RSS reader code where articles get downloaded and are viewable offline.
The problem is only after all articles are downlaoded the tableview containing headlines gets updated. Core data is used. So everytime NSobjectcontext is saved , [self tableview updatebegins ] is called.The table is getting updated via fetchcontroller core data.
I tried saving NSobjectcontext everytime an article is saved but that is not updating the tableview. I want a mechanism similar to instapaper tableview where articles get saved and tableview gets updated immediately. Please help if you know the solution. Thanks in advance.
Adding code for better understanding
AppDelegate.m contains following code
- (void)feedSuccess:(ZSURLConnectionDelegate*)delegate
NSManagedObjectContext *moc = [self managedObjectContext];
CXMLElement *root = [document rootElement];
CXMLElement *channel = [[root elementsForName:#"channel"] lastObject];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"FeedItem" inManagedObjectContext:moc]];
for (CXMLElement *item in [channel elementsForName:#"item"])
{
// push element in core data and then save context
//Save context
[moc save:&error];
ZAssert(error == nil, #"Error saving context: %#", [error localizedDescription]);
}
this triggers the table change code in RootviewController.m
- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller
{
[[self tableView] beginUpdates];
}
If you use the NSFetchedResults controller, simply use its delegate method
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller;
as follows:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// In the simplest, most efficient, case, reload the table view.
[self.tableView reloadData];
}
Every time you update the underlying NSManagedObjectContext, the table will be automatically updated too.
I hava a UINavigationController. The first level is a UITableViewController, the second level just shows details on one of the items of the table view.
In this detail view, I can delete the item. It deletes the underlying managed object.
When I pop back to the view, I have a crash. I understand why, it's because I didn't update the cached array that contains the data.
I looked at several tutorials and I don't exactly understand how am I supposed to handle deletion. Maybe I don't understand exactly where I should fetch the objects in the model. Should I do a query for every cellForRowAtIndexPath and take the item in the result at position indexPath.row? It doesn't look efficient. Should I check for changes somewhere and recache the whole query in an array. I would think CoreData would provide something more natural but I couldn't find it so far.
Thanks in advance.
It is fairly simple. In the child view you should (really, really should) have a reference to the NSManagedObject you are working with. When you want to delete it then you just:
NSManagedObjectContext *moc = [[self myObject] managedObjectContext];
[moc deleteObject:[self myObject]];
NSError *error = nil;
if (![moc save:&error]) {
NSLog(#"Save failed: %#\n%#", [error localizedDescription], [error userInfo]);
}
This will delete the object. The parent, since it is using a NSFetchedResultsController (which you should also REALLY be doing) will take care of itself.
It seems somewhat non-standard deleting an item in the parent controller from your detail controller, but perhaps it makes sense in your case. I presume you know that you can directly delete items in the tableview. There are many example code projects from Apple which along with the docs should give you an idea how to do that.
To answer your question, you could create a property/variable in your detail controller's class which holds a reference to the tableview controller then send a message to that controller to handle the delete. Creating a protocol for this would be good style but not necessary. When the tableview class receives the delete item message, it updates the array, and when that view is redisplayed you should call reloadData on the tableview. This is the standard paradigm: make changes to your underlying data model and tell the tablview to reload.
In case you don't use NSFetchedResultsController, all you need to do is implement the following method:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object at the given index path.
NSManagedObject *rowToDelete = [currentRows objectAtIndex:indexPath.row];
[managedObjectContext deleteObject:rowToDelete];
// Commit the change.
NSError *error;
if (![managedObjectContext save:&error]) {
// Handle the error.
NSLog(#"Failed to save to data store: %#", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0) {
for(NSError* detailedError in detailedErrors) {
NSLog(#" DetailedError: %#", [detailedError userInfo]);
}
}
else {
NSLog(#" %#", [error userInfo]);
}
}
// Update the array and table view.
[currentRows removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
}
currentRows is a NSArray of the objects you display in the table.
Cheers
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.