FetchedResultsController(FRC) – Part 1

NSFetchedResultsController(FRC)

Fetched results controller is an effective way for binding Core Data with UITableView or UICollectionView. As we all know both UITableView and UICollectionView is constructed with sections and rows for each of those section. And the fetched results controller also constructed in such a way with both sections and rows for each section which will match for the UITableView / UICollectionView data source and can be used for it.

Why its so effective ?

Lets list out why Fetched result controller is so effective when using CoreData,

  • If we want to display core data content in a UITableView then we have to fetch respective contents of entities present in the core data and display it. We may get a case where we want to update the table view data when new values are added in core data. In this case if we want to load the newly inserted data in the UITableView then we want to fetch the core data contents again and reload the table view. But in this method we may not know when to perform the synchronize process again and again.
  • To overcome this Core Data comes with the following solution.
  • Whenever a record is inserted, deleted or updated in the Managed Object Context(MOC), It will post three types of notifications through notification center. They are,

1) NSManagedObjectContextObjectsDidChangeNotification:

Posted when new record is inserted, deleted or updated in managed object context.

2) NSManagedObjectContextWillSaveNotification:

Posted before the pending changes are commited to the backing store(database).

3) NSManagedObjectContextDidSaveNotification:

Posted after the changes are commited in the backing sore(database).

Note: The above notification contents consists of the records that was inserted, deleted or updated.

  • Now lets come to our scenario, now we know that we will get the above notifications whenever there is some change in the coredata. So now we can make the synchronization process whenever any of the above notification is received.
  • But there is an another way to handle this scenario, this is when Fetched results controller comes in, FRC is specifically suited for managing core data data with UITableView or UIColletionView and to do the above synchronization process by itself i.e., it takes care of all the synchronization process and updates the UI data whenever any change is made in core data and it does it in efficient manner by making use of the userInfo that the above listed notifications comes with.

Ok, now I hope this explains better about FRC’s effectiveness. Lets step into the process involved in configuring FRC.

How to configure FRC ?

The following steps are followed for configuring a Fetched results controller,

  • Create an instance of NSFetchedResultsController.
//CODE

@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;

  • Create a NSFetchRequest with an entity name in the database for which we need to construct fetched result controller for and

– (Mandatory) : The above fetcth request should contain atleast one sort descriptor which is used to sort the results.
– (Optional) : You can also set “setFetchBatchSize” value to specify the number of objects returned i.e, it splits into batches when the fetch request is executed. By default the batch size is 0 which is treated as infinite.

//CODE
– (void)viewDidLoad {
          [super viewDidLoad];
          // Initialize Fetch Request
          NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@”TimeSheet”];
          // Add sort descriptors(Mandatory)
          [fetchRequest setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@”updatedAt” ascending:YES]]];
          // Add batchSize(Optional)

          [fetchRequest setFetchBatchSize:20];

Initialize the fetch result controller instance. You do this by passing four parameters namely,

  1. The fetch request that we constructed above.
  2. Managed object context object.
  3. (Optional) : You could specify “sectionNameKeyPath” the fetch result controller uses this keypath to split the results into sections like in a tableview/collecitonview. If you pass nil for this argument then it will create all the results under a single section.
  4. (Optional) : You could specify “cacheName”, Using this cache can avoid the overhead of computing the section and index information. If you pass nil for this argument then it prevents caching.
//CODE
// Initialize FetchedResultsController

self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];

  • Configure the fetched result controller by setting its delegate as below,
//CODE
// Assign delegate for FetchedResultsController

[self.fetchedResultsController setDelegate:self];

  • Perform Fetch action
//CODE
// Perform Fetch action
NSError *error = nil;
[self.fetchedResultsController performFetch:&error];
// Handle error
if (error) {
           // Handle error here. (eg., alert, logging, retry option,…)

}

  • Now set the data for tableview/collectionview with fetchedresultcontroller instance as shown below(shown how to use in a tableview),

Implementing : UITableViewDataSource :-

//CODE
//The sections in fetchedResultController holds all the section info, these sections are formed based on sectionNameKeyPath value we mentioned while initializing
fetchedResultController.
– (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
          return [[self.fetchedResultsController sections] count];
}
//Under each sections there may be any number of objects based on the content present in your database.
– (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
          NSArray *sections = [self.fetchedResultsController sections];
          id<NSFetchedResultsSectionInfo> sectionInfo = [sections objectAtIndex:section];
          return [sectionInfo numberOfObjects];
}
– (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;
}
// Method for retrieving data for cell at particular index path, This method will get the NSManagedObject for respective index path and we get the details from it.
– (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
          // Get respective record from fetchedResultController
          NSManagedObject *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
          // Update cell contents
          [cell.taskTiming setText:[record valueForKey:@”timing”]];
          [cell.taskDescription setText:[record valueForKey:@”description”]];

}

Implementing the NSFetchedResultsControllerDelegate Protocols:

NSFetchedResultsController comes with few delegate methods which are used for denoting the status of updation of data in coredata, they are,

  • controllerWillChangeContent:
  • controllerDidChangeContent:
  • controller:didChangeSection:atIndex:forChangeType:
  • controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:

We can make use of these delegates for updating the tableView with respect to the coredatabase changes.

//CODE
//controllerWillChangeContent : Denotes before any changes are made to the data which is managed by FetchedResultsController.
– (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
//controllerDidChangeContent : Denotes after all the changes are made to the data which is managed by FetchedResultsController.
– (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
//controller:didChangeObject:atIndexPath:forChangeType:newIndexPath : Denotes the change made in core data at particular

//Arguments for the above delegates are as follows

  • An instance of NSFetchedResultsController.
  • The changed/updated NSManagedObject instance.
  • Current index path of object in fetched results controller.
  • Change type : The changes are of many types namely Insert, Update, Delete or Move.
  • New index path of the object in the fetched results controller.

Type of changes that can take place are as follows,

  • NSFetchedResultsChangeInsert
  • NSFetchedResultsChangeDelete
  • NSFetchedResultsChangeUpdate &
  • NSFetchedResultsChangeMove
//CODE
– (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
          switch (type) {
               case NSFetchedResultsChangeInsert: {
                    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                    break;
               }
               case NSFetchedResultsChangeDelete: {
                    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                    break;
               }
               case NSFetchedResultsChangeUpdate: {
                    [self configureCell:(UITableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
                    break;
               }
               case NSFetchedResultsChangeMove: {
                    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                    [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                    break;
               }
       }
}
 In the next post we will see the usage of multiple fetchedResultsController in the same view controller.
Bharath R,
iOS Developer,
Mallow Technologies.

1 Comment

  1. […] the previous post we saw what is fetched results controller and its usage of single fetchedResultsControllers in a […]

Leave a Reply

%d bloggers like this: