
In the previous post we saw what is fetched results controller and its usage of single fetchedResultsControllers in a same view controller. In this post we will see the usage multiple fetched results controller in same view controller.
Using multiple fetchedResultsControllers in same view controller:
You may rise a question wether can we use multiple FRC in same view controller?
- Yes, we can. Let us take a scenario where we can use two FRCs in a single view controller.
- Think about a TODO application in which we need to list two sections in a tableView where first section will list the todo item which are un-completed from “UnCompleted” table, and the second section will list the completed todo list from “Completed” table.
Create two instances of NSFetchedResultsController.
//CODE
//FRC instances
@property (strong, nonatomic) NSFetchedResultsController *completedFetchedResultsController;
@property (strong, nonatomic) NSFetchedResultsController *unCompletedFetchedResultsController;
– (void)viewDidLoad {
[super viewDidLoad];
//Initialize fetchedResultsController
// Perform Fetch action
NSError *completedError = nil;
[self.completedFetchedResultsController performFetch:&completedError];
// Handle error
if (completedError) {
// Handle error here. (eg., alert, logging, retry option,…)
}
// Perform Fetch action
NSError *unCompletedError = nil;
[self.unCompletedFetchedResultsController performFetch:&unCompletedError];
// Handle error
if (unCompletedError) {
// Handle error here. (eg., alert, logging, retry option,…)
}
}
//Initialize the two fetchedResultsController
– (NSFetchedResultsController *)completedFetchedResultsController {
//Return FRC if its already fetched
if (self.completedFetchedResultsController!= nil) {
return self.completedFetchedResultsController;
}
// Initialize Fetch Request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@”Completed”];
// Add sort descriptors(Mandatory)
[fetchRequest setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@”updatedAt” ascending:YES]]];
// Add batchSize(Optional)
[fetchRequest setFetchBatchSize:20];
// Initialize FetchedResultsController
self.completedFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
// Assign delegate for FetchedResultsController
[self.completedFetchedResultsController setDelegate:self];
// Return fetched result controller
return self.completedFetchedResultsController;
}
– (NSFetchedResultsController *)unCompletedFetchedResultsController {
//Return FRC if its already fetched
if (self.unCompletedFetchedResultsController != nil) {
return self.unCompletedFetchedResultsController;
}
// Initialize Fetch Request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@”UnCompleted”];
// Add sort descriptors(Mandatory)
[fetchRequest setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@”updatedAt” ascending:YES]]];
// Add batchSize(Optional)
[fetchRequest setFetchBatchSize:20];
// Initialize FetchedResultsController
self.unCompletedFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
// Assign delegate for FetchedResultsController
[self.unCompletedFetchedResultsController setDelegate:self];
//Return fetched result controller
return self.unCompletedFetchedResultsController;
}
- Now we have initialized two fetchedResultsControllers and has required data in the same view controller. Now we have to be careful while setting tableView delegate and datasource.
- Lets now configure tableView datasource and delegate methods by making use of the two fetchedResultsControllers we have,
Implementing : UITableViewDataSource using two FRCs:-
//Set number of sections
– (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.completedFetchedResultsController sections] count] + [[self.unCompletedFetchedResultsController sections] count];
}
//Set number of rows in each section
– (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
int rowsCount = 0;
if (section < [[self.completedFetchedResultsController sections] count] ) { //First FRC
id<NSFetchedResultsSectionInfo> sectionInfo = [self.completedFetchedResultsController sections][section];
rowsCount = [sectionInfo numberOfObjects];
} else { //Second FRC
id<NSFetchedResultsSectionInfo> sectionInfo = [self.unCompletedFetchedResultsController sections][section];
rowsCount = [sectionInfo numberOfObjects];
}
return rowsCount;
}
//Now configuring cell for row at index path, mostly there wont be any change in this datasource since the object received from “configureCell:atIndexPath” returns NSManagedObject there wont be any changes needed in cellForRowAtIndexPath.
– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = (UITableViewCell*)[tableView dequeueReusableCellWithIdentifier:@”Cell” forIndexPath:indexPath];
// Configure tableViewCell using fetchedResultController
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
//In configureCell:atIndexPath we need to return object based on the section in our case as shown below.
– (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 1) {
// Get respective record from fetchedResultController
//Instances for “Completed”(Subclass of NSManagedObject class) table
Completed *completedObject *record = [self.completedFetchedResultsController objectAtIndexPath:indexPath];
// Update cell contents
[cell.titleLabel setText:[record valueForKey:@“note”]];
} else if (indexPath.section == 2) {
// Get respective record from fetchedResultController
//Instances for “UnCompleted”(Subclass of NSManagedObject class) table
UnCompleted *record = [self.unCompletedFetchedResultsController objectAtIndexPath:indexPath];
// Update cell contents
[cell.titleLabel setText:[record valueForKey:@“note”]];
}
Thats it, this is a sample shown above which shows how to handle multiple fetched results controller in a same view controller, but we can use multiple fetched results controller in many other cases based on your requirement.
Some advanced topics in FetchedResultsController:
- mergeChangesFromContextDidSaveNotification:
It merges the changes specified in a given notification. This method refreshes any objects which have been updated in the other context, faults in any newly-inserted objects, and invokes deleteObject:: on those which have been deleted. You can pass a NSManagedObjectContextDidSaveNotification notification posted by a managed object context on another thread, however you must not use the managed objects in the user info dictionary directly. For more details, see Concurrency with Core Data.