views:

810

answers:

2

I can't figure out why, for the life of me, the NSFetchedResultsControllerDelegate methods are not firing when I add data to the underlying datastore. The data shows up immediately if I restart the iPhone application.

I have subclassed UITableViewController and conform to NSFetchedResultsControllerDelegate:

@interface ProjectListViewController : UITableViewController <NSFetchedResultsControllerDelegate> {
    NSFetchedResultsController* fetchedResultsController_;
    NSManagedObjectContext* managedObjectContext_;
}

I instantiate the NSFetchedResultsController and set the delegate to self:

// Controller
fetchedResultsController_ = [[NSFetchedResultsController alloc] initWithFetchRequest:request
 managedObjectContext:self.managedObjectContext 
                  sectionNameKeyPath:@"Client" 
                     cacheName:@"ProjectsCache"];
fetchedResultsController_.delegate = self;

I implement the delegate methods:

- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller {
    NSLog(@"ProjectListViewController.controllerWillChangeContent");
    // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    [self.tableView beginUpdates];
}

- (void)controllerDidChangeContent:(NSFetchedResultsController*)controller { ... }
- (void)controller:(NSFetchedResultsController*)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { ... }
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { ... }

I create the entity I wish to save:

Project* newProject = [NSEntityDescription insertNewObjectForEntityForName:@"Project" inManagedObjectContext:self.managedObjectContext];
ProjectDetailViewController* detail = [[ProjectDetailViewController alloc] initWithStyle:UITableViewStyleGrouped 
                    delegate:self 
                    selector:@selector(finishedAdding:) 
                     project:newProject];

And later, I save it:

- (void)save {
    // NSLog(@"ProjectDetailViewController.save");
    self.project.name = projectNameTextField_.text; 
    NSError* error;
    BOOL b = [self.project.managedObjectContext save:&error];
    if (!b) {
        NSLog(@"Error saving project!");
    } else {
        NSLog(@"Project was successfully saved.");
        [delegate_ performSelector:selector_ withObject:self.project];
    }
    [self dismissModalViewControllerAnimated:YES];
}

It all works just fine except for the fact that my delegate methods don't fire. Obviously my table view doesn't get updated and the only way to see the new data is to explicitly refresh or restart the app.

I've looked through the CoreData Recipe app - but can't seem to find what I'm missing. Thoughts?

-Luther

A: 

My read of the above looks like you are using 2 NSManagedObjectContexts, one in ProjectListViewController, and seperate one in ProjectDetailViewController (I assume it is created there since I don't see it passed in.

When you save one context it does not automatically propagate changes into another, so saving the one in ProjectDetailViewController will not cause the changes to appear in the context of ProjectListViewController, which means there are no changes in that one to tell the delegate about it.

If you want to push the changes between contexts, look at NSManagedObjectContextDidSaveNotification and mergeChangesFromContextDidSaveNotification: (they are the end of the NSManagedObjectContext documentation).

Louis Gerbarg
Your point sure makes sense given the behavior I'm seeing and yes, I have been able to use notifications successfully to merge changes but, I can't understand is 'why' it is necessary. I am missing something.I use the same managedObjectContext to create the fetchedResultsController_, the 'newProject' and also to save the 'newProject' (the context is a property of 'newProject' which was created from the original managedObjectContext). [self.project.managedObjectContext save:I think the delegate methods should have fired when I created AND when I save 'newProject'.
Luther Baker
A: 

If I change the sectionNameKeyPath from @"Client" to nil when I create the original fetchedResultsController_, both the Project entity creation and save indeed invoke the delegate methods!

fetchedResultsController_ = 
    [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                        managedObjectContext:self.managedObjectContext 
                                          sectionNameKeyPath:nil
                                                   cacheName:@"ProjectsCache"];

Upon hyper scrutiny, that is what the CoreData Recipes example does. As my data gets more complex - I think I'm going to need that argument to help break the results up into sections but for now, it is nice to see the delegate handlers being invoked.

Luther Baker