FPMultiFetchedResultsController
FPMultiFetchedResultsController copied to clipboard
An extended NSFetchResultsController that supports multiple underlying NSFetchRequests.
FPMultiFetchedResultsController
FPMultiFetchedResultsController is a NSFetchedResultsController that supports multiple NSFetchRequest objects and correctly handles section and row mutations when transitioning between underlying fetch requests.
A FPMultiFetchedResultsController extends the NSFetchedResultsController interface. On inititialization a FPMultiFetchedResultsController accepts a dictionary of NSFetchRequest objects. Each NSFetchRequest is uniquely named by its key in this dictionary.
Example
Consider a Task NSManagedObject with three fields: name, complete, and ordering.

We will initialise a FPMultiFetchedResultsController with a dictionary of NSString => NSFetchRequest pairs containing distinct fetch requests for all, complete, and incomplete tasks.
// Task objects have an ordering field that specifies the user's custom ordering.
NSSortDescriptor *sortByOrdering = [NSSortDescriptor sortDescriptorWithKey:@"ordering" ascending:YES];
// All tasks.
NSFetchRequest *requestAll = [NSFetchRequest fetchRequestWithEntityName:@"Task"];
requestAll.sortDescriptors = @[sortByOrdering];
// Incomplete tasks.
NSFetchRequest *requestIncomplete = [NSFetchRequest fetchRequestWithEntityName:@"Task"];
requestIncomplete.sortDescriptors = @[sortByOrdering];
requestIncomplete.predicate = [NSPredicate predicateWithFormat:@"complete == FALSE"];
// Complete tasks.
NSFetchRequest *requestComplete = [NSFetchRequest fetchRequestWithEntityName:@"Task"];
requestComplete.sortDescriptors = @[sortByOrdering];
requestComplete.predicate = [NSPredicate predicateWithFormat:@"complete == TRUE"];
NSDictionary *fetchRequests = @{
@"all" : requestAll,
@"incomplete" : requestIncomplete,
@"complete" : requestComplete
};
_resultsController = [[FPMultiFetchedResultsController alloc] initWithFetchRequests:fetchRequests
initialFetchRequestName:@"incomplete"
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:nil];
_resultsController.delegate = self;
After implementing the delegate methods detailed below you can animation transitions between fetch requests.
// Show all tasks.
[_resultController transitionToFetchRequestByName:@"all" error:nil];
// Show only complete tasks.
[_resultController transitionToFetchRequestByName:@"complete" error:nil];
During the transition the controllerWillChangeContent:, controller:didChangeSection:, controller:didChangeObject:, and controllerDidChangeContent: NSFetchedResultsControllerDelegate delegate methods are called.
NSTableViewDataSource Delegate
In your UITableViewController subclass implement the NSTableViewDataSource interface
@implementation ExampleTableViewController {
FPMultiFetchedResultsController *_resultsController;
}
#pragma mark - UITableViewDataSource delegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return _resultsController.sections.count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [_resultsController.sections[section] name];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_resultsController.sections[section] numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"cell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"cell"];
}
Task *task = [_resultsController objectAtIndexPath:indexPath];
cell.textLabel.text = task.name;
return cell;
}
NSFetchedResultsController Delegate
To handle transitions between fetch requests you must implement the NSFetchedResultsControllerDelegate interface in your view controller
#pragma mark - NSFetchedResultsController delegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
switch (type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:@[newIndexPath]
withRowAnimation:UITableViewRowAnimationLeft];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
[self.tableView moveRowAtIndexPath:indexPath
toIndexPath:newIndexPath];
break;
case NSFetchedResultsChangeUpdate:
// Do nothing when non-sorting related properties change.
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationLeft];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
Limitations
- If
sectionNameKeyPathis notnilthen all NSFetchRequest objects passed toinitWithFetchRequests:must have identical sort descriptors. - Calls to delegate methods
didChangeSectionanddidChangeObjectmay be computationally expensive for fetch requests with a large number of results.
Installing
CocoaPod
Install and configure CocoaPods.
In the root of your project create a file named Podfile containing
platform :ios, '6.0'
xcodeproj '<# YOUR PROJECT #>.xcodeproj'
pod 'FPMultiFetchedResultsController', :git => "https://github.com/brotchie/FPMultiFetchedResultsController.git"
then run
pod install
Note: After running pod install open <# YOUR PROJECT #>.xcworkspace in xcode, not <# YOUR PROJECT #>.xcodeproj.
Git
Clone the FPMultiFetchedResultsController repostiory
git clone https://github.com/brotchie/FPMultiFetchedResultsController.git
then drag FPMultiFetchedResultsController.h and FPMultiFetchedResultsController.m from the FPMultiFetchedResultsController subdirectory into your XCode project.
Git Submodule
From the root of your project clone the FPMultiFetchedResultsController repostiory into a git submodule
git submodule add https://github.com/brotchie/FPMultiFetchedResultsController.git Submodules/FPMultiFetchedResultsController
git submodule update --init
then drag FPMultiFetchedResultsController.h and FPMultiFetchedResultsController.m from the Submodules/FPMultiFetchedResultsController/FPMultiFetchedResultsController subdirectory into your XCode project.