Question

Please be gentle, this is my first ever post.

When I update a subclassed NSManagedObject and perform a save using a fetched results controller, instead of calling NSFetchedResultsChangeUpdate in "didChangeObject" it calls NSFetchedResultsChangeDelete. The save works and it updates the objects, but then immediately deletes it from the UITableView. If I quit and return, the object re-appears in the tableview with the updates so I know it is saving it to the DB.

I am developing code for a simple task app using XCODE 5. I am using an iPhone 4S and not the simulator to test it. It was working fine until I made some mods to another part of the code (I thought unconnected and now it doesn't work.

I have a sort order and when I update an object and it changed in the sort order, there was a nice animation for the moving of the UITableViewCells....now I have to force a fetch again and do [self.tableView reloadData]. This is a hack as I do not get any animations, but it is the only way I can get it to update:

I have a prepare for segue method:

- (void) prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender
{
// configure the destination view controller:
if ( [segue.destinationViewController isKindOfClass: [ShowEditTaskTableViewController class]])
{

    if ([sender isKindOfClass:[UITableViewCell class]] )
    {


        NSIndexPath *indexPath = [self.taskTableView indexPathForCell:sender];
        [self.taskTableView deselectRowAtIndexPath:indexPath animated:YES];

        // Pass the selected task to the new view controller.
        ShowEditTaskTableViewController *detailViewController = segue.destinationViewController;

        Task *info = [self.fetchedResultsController objectAtIndexPath:indexPath];
        detailViewController.editedObject = info;
    }
    else
    {

        NSIndexPath *indexPath = [self.taskTableView indexPathForCell:sender];
        [self.taskTableView deselectRowAtIndexPath:indexPath animated:YES];
        ShowEditTaskTableViewController *detailViewController =           segue.destinationViewController;

        Task *info = (Task *)[NSEntityDescription insertNewObjectForEntityForName:@"Task" inManagedObjectContext:self.managedObjectContext];
        detailViewController.editedObject = info;




    }

}

}

There is nothing special here. "Task" is my NSManagedObject.

I have a rewind (EDIT: changed reverse to rewind after comment) segue and before it is called, I set the variables which will be set stored in the editedObject.:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{

if ([segue.identifier isEqualToString:@"addNewTaskSave"]) {
    // Note: This is an unwind segue to go back to the previous screen.

    self.dueDate = self.taskDatePicker.date;
    self.description = self.taskDescriptionTextView.text;
    self.priority = self.taskPrioritySegment.selectedSegmentIndex;
    }
}

and in the RootViewController it calls:

- (IBAction)addNewTaskSave:(UIStoryboardSegue *)segue
{

ShowEditTaskTableViewController *controller = segue.sourceViewController;

controller.editedObject.shortDesc = controller.description;
controller.editedObject.priority = @(controller.priority);

controller.editedObject.dueDate = controller.dueDate;
controller.editedObject.completed = @0;

NSError *error;
if (![self.managedObjectContext save:&error]) {
    ...
}

..... }

It uses the standard didChangeObject delegate methods. This worked fine until I clearly changed something.

Now after the save, it sets the NSFetchedResultsChangeDelete option and deletes the table row.

The fix is to add:

self.fetchedResultsController = nil;

if (![[self fetchedResultsController] performFetch:&error]) {
   ...
}
[self.taskTableView reloadData];

However, this means that what is actually observed is just a straight update of the Table View with no animation.

Without this code, you can observe an animated delete of the table row. If I quit and restart, my edited object is there.

I have scoured SO which has been my constant companion for the last 4 weeks (the time I have been coding for IOS) and cannot find anything on this behaviour.

Any help would be greatly appreciated.

EDIT:

before the save, controller.editedObject isDeleted = NO, isUpdated = YES, so I can't see why it is setting NSFetchedResultsControllerChangeDelete.

Was it helpful?

Solution 2

It appears my scouring of SO was not very good. see here https://stackoverflow.com/a/18998335/3482632

I added a UISegmentControl to filter and used the int value of its segment index.

 int index = self.filterSegment.selectedSegmentIndex;
 NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"filter == %d", index]];
 [fetchRequest setPredicate:predicate];

"filter" in my NSManagedObject subclass is an NSNumber. when I changed the NSPredicate to

 NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"filter == %@", @(index)]];

everything works fine.

OTHER TIPS

I'm just going to discuss one piece of your code; and this may not have anything to do with the fetch results controller issue described.

Your so-called unwind/reverse segue has me stumped. I don't think prepareForSegue:sender: is relevant for an official unwind segue. So maybe you're not really using an official unwind segue (which is created by control-dragging from a button to the "exit" icon beneath a storyboard scene).

Unless you're referring to an unwind segue, there's no such thing as a "reverse" segue that takes you back to a previous screen. A push segue, for example, doesn't have a separate counterpart called a "pop" segue.

I suspect that you have segues crisscrossing between two scenes in storyboard. In other words, you created a segue from scene A to scene B, and you created a segue from scene B to scene A. If that's really the case, I would avoid doing that because it's unconventional. Maybe spend some time reviewing segues.

Ok, so I'll briefly address the fetched results controller issue. If there are user-driven changes to the data in a table view populated by a fetched results controller, then you will need to set a flag and temporarily "turn off" the fetched results controller delegate methods. This issue is mentioned briefly in the docs for NSFetchedResultsControllerDelegate protocol under "User-Driven Updates".

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top