Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding how NSFetchedResultsController works

Tags:

ios

iphone

First off, thank you very much if you find time to help me!

I have been learning about NSFetchedResultsControllerand im trying to understand it as much as i can, however, some things about it very much confuse me. This code if from a tutorial i've found online. Basically i have the simplest fetched controller set up with really fancy methods like this

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


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

    UITableView *tableView = self.tableView;

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            // Reloading the section inserts a new row and ensures that titles are updated appropriately.
            [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

 (NSFetchedResultsController *)fetchedResultsController {

    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription 
        entityForName:@"FailedBankInfo" inManagedObjectContext:_context];
    [fetchRequest setEntity:entity];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc] 
        initWithKey:@"details.closeDate" ascending:NO];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

    [fetchRequest setFetchBatchSize:20];

    NSFetchedResultsController *theFetchedResultsController = 
        [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
            managedObjectContext:_context sectionNameKeyPath:nil 
            cacheName:@"Root"];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;

    [sort release];
    [fetchRequest release];
    [theFetchedResultsController release];

    return _fetchedResultsController;    

}
  1. Do i need to create a new fetched controller everytime for different view controllers? For instance if i have a list of businesses in one list view. then i click on one of them and brings me to a list of employees in that ocmpany, i have to use a different fetchedViewController since i need to respecify the entity key right? See above my implementation.

  2. With my current implementation, it works great for listing items. Let's say I have a view controller called VC. init i fully implemented NSFetchedResultsController. I have a barbutton add item and a method called addButtonPressed which when pressed modally adds a view (from bottom up). In this same method i have Entity myEntity = [NSEntityDescription insertNNewObjectForEntityForName:@"Entity" inManagedObjectContext:context_]; Then i tell the code the go into another view. HOWEVER, when the animation is moving the new navigation controller up, the new cell already shows itself mid animation. From my understanding this these two methods - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath AND - (UITableViewCell *)tableView:(UITableView *)tableView get called in conjuction with the top two methods in the code snippet above. how can i fix it? i dont want anything showing up on the current view until i added a new Entity by going to another view. The second another object gets called into the context, the update methods must get called or something

  3. What does this snippet of code do? Particularly the part with id < stuff >

    (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id NSFetchedResultsSectionInfo)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

    switch(type) {
    
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    
        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
    

    } Edit: Holy $#$, formatting this frustrating. there should be didChangeSection:(id (LESS THAN EQUAL SIGN) NSFetchedResultsSectionInfo (GREATER THEN EQUAL SIGN)) in the code above

4 Where can i learn more about this. i.e what the individual content updating methods do and when they are called etc. Also Does the NSFetchedResultcontroller act as a single array? Meaning that if i want to have a tableview with multiple sections i need more ViewControllers? Once again THANKS!

like image 669
bubbles Avatar asked Sep 05 '11 07:09

bubbles


1 Answers

I second Brad's comment, but I'll give you some pointers:

  1. Basically yes, you need a new NSFetchedResultsController for every entity you want to query for. However, you don't need to use and NSFetchedResultsController at all, this is just a nice class to let you do fancy things with your tableView as you are doing in question #2.

  2. Your problem here is that you are calling [NSEntityDescription insertNNewObjectForEntityForName:@"Entity" inManagedObjectContext:context_]; from your tableViewController and as soon as you call that your NSFetechedResultsController will get a message stating that a row was inserted, triggering the NSFetchedResultsChangeInsert path in your controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: method. NSFetchedResultsController is an object that watches the data that is fetches from CoreData for any changes and then gives you the ability to do stuff as the changes happen. To fix your display issue, just move the insertNewObjectForEntityName call into your next viewController, and magically when you dismiss that viewController you'll have a new row.

  3. The didChangeSection: delegate method of NSFetchedResultsController is informing you if a section has been added or deleted from your dataset. See my answer to #4 and this should be clear.

  4. NSFetechedResultsController allows you to "section" the data it fetches using the sectionNameKeyPath: part of the init call. By supplying the name of a data point in your entity you can group your data into sections. So going back to #3 & #2 if you happen to insert a new entity that has a new value in the "section" keypath that you specified when initing the NSFetchedResultsController you'd get the didChangeSection: delegate called.

I recommend reading Apples documentation on NSFetchedResultsController and CoreData and probably their TableView Programming Guide. Don't forget to download and play with the sample code, it's easier to understand in most cases than the theories in the documentation.

like image 199
Rob Booth Avatar answered Nov 07 '22 12:11

Rob Booth