NSFetchedResultsController cheat sheet

What is NSFetchedResultsController?

Apple says:

You use a fetched results controller to efficiently manage the results returned from a Core Data fetch request to provide data for a UITableView object.

While table views can be used in several ways, fetched results controllers are primarily intended to assist you with a master list view. UITableView expects its data source to provide cells as an array of sections made up of rows. You configure a fetch results controller using a fetch request that specifies the entity, an array containing at least one sort ordering, and optionally a filter predicate. The fetched results controller efficiently analyzes the result of the fetch request and computes all the information about sections in the result set. It also computes all the information for the index based on the result set.

In addition, fetched results controllers provide the following features:

Optionally monitor changes to objects in the associated managed object context, and report changes in the results set to its delegate (see “The Controller’s Delegate”).
Optionally cache the results of its computation so that if the same data is subsequently re-displayed, the work does not have to be repeated (see “The Cache”).

Declare a NSFetcedResultsController

@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;

Make your view controller NSFetcedResultsController delegate

#import "myViewController.h"

@interface myViewController () <... , NSFetchedResultsControllerDelegate>
    ...

    @property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;

How to set a NSFetcedResultsController

- (NSFetchedResultsController *)fetchedResultsController {

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

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Department" inManagedObjectContext:self.managedObjectContext];

[fetchRequest setEntity:entity];

NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"nameOfDepartment" ascending:NO];

[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];

[fetchRequest setFetchBatchSize:20];

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

self.fetchedResultsController = theFetchedResultsController;

self.fetchedResultsController.delegate = self;

return _fetchedResultsController;

}

Perform a fetch from Core Data

- (void)viewDidLoad
{
    [super viewDidLoad];

    ...

    NSError *error;
	if (![[self fetchedResultsController] performFetch:&error]) {
		// Update to handle the error appropriately.
		NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		exit(-1);  // Fail
	}
}

Get a specific object

aViewController.myObject = (CustomObject *)[self.fetchedResultsController objectAtIndexPath:indexPath];

Count the number of objects for the sake of TableView Datasource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id  sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}

Get the total number of fetched objects

[fetchedResultsController.fetchedObjects count];

Perform fetch with criteria using NSPridicate

- (void)viewDidLoad
{
    [super viewDidLoad];

    ...

    [NSFetchedResultsController deleteCacheWithName:@"Root"];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"department = %@", self.myDepartment];
    [[self fetchedResultsController].fetchRequest setPredicate:predicate];

    NSError *error;
	if (![[self fetchedResultsController] performFetch:&error]) {
		// Update to handle the error appropriately.
		NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		exit(-1);  // Fail
	}
}

Don't forget to flush! Hmmm ok lets explain. You cannot simply change the fetch request to modify the results as the criteria change. You must delete the cache (using deleteCacheWithName:) or you should not use a cache if you are changing the fetch request (set cacheName:nil). If you are not follow either, your app will crash.

Delete an object

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {

        CustomObject *objectToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
        [self.managedObjectContext deleteObject:objectToDelete];

        NSError *error = nil;
        if (![_managedObjectContext save:&error]) {/*
                                                    [(AppDelegate *)[UIApplication sharedApplication].delegate presentError:error WithText:NSLocalizedString(@"insert categories", @"Error description in saving products' categories")];*/
        }
    }
    else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }
}

Don't forget to include NSFetchedResultsControllerDelegate Protocol methods

An instance of NSFetchedResultsController uses methods NSFetchedResultsControllerDelegate Protocol to notify its delegate that the controller’s fetch results have been changed due to an add, remove, move, or update operations. So, add the following methods to your .m viewController source file.

/*
 Assume self has a property 'tableView' -- as is the case for an instance of a UITableViewController
 subclass -- and a method configureCell:atIndexPath: which updates the contents of a given cell
 with information from a managed object at the given index path in the fetched results controller.
 */

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )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;
    }
}

- (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];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                       withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView endUpdates];
}

Need a good tutorial for NSFetchedResultsController?
Please read Adam's Burkepile "Core Data on iOS 5 Tutorial: How To Use NSFetchedResultsController" published here.

Is that all?
No, I'll update it as long as I find new tips!

Leave a Reply

Your email address will not be published. Required fields are marked *