Adding Convenience Methods to NSFetchedResultsController via Categories

One really useful feature of Objective-C is the ability to extend an existing class without the need to sub-class via inheritance.  This means that existing code can stay in place whilst additional functionality is added for the convenience of future maintenance.

Categories is the feature that makes this possible.  In effect, this says to the compiler, ‘add on these methods to the existing class’.  One place I’ve found them useful is in convenience methods on NSFetchedResultsController.  Getting row counts for sections, or objects from sections is non-intuitive due to a lack of a couple of methods.  You have to go via the sections array, even when you are not using sections in the fetch, and you need to build an NSIndexPath to extract an object, again, even if sections are not relevant.

Defining the Categories

To add a category to an existing class, we use the @interface definition again, but this time put the Category name in brackets, in a similar manner to adopting a protocol. RSFetchedResultsControllerHelpers is the category name, in this case ‘RS’ is an acronym of my company name to keep things from classing with other potential names, and ‘Helpers’ is a nice reminder of why I’m adding them.

Here we are defining two instance methods that create a shortcut to getting row counts for a section, and an object from an indexPath without the need to create an instance of indexPath directly.

@interface NSFetchedResultsController (RSFetchedResultsControllerHelpers)

-(NSUInteger) numberOfObjectsForSection: (NSUInteger) section;
-(id) objectAtRow: (NSUInteger) row inSection: (NSUInteger) section;

@end




The above goes into a separate .h file.  There is a convention for the naming of Category headers and implementation files, which is to name the file after the original class name, then ‘+’ followed by the Category name.  So save that above to NSFetchedResultsController+RSFetchedResultsControllerHelpers.h.

Implementing the Categories

We now create the implementations of the Category methods, and place the following in NSFetchedResultsController+RSFetchedResultsControllerHelpers.m.

@implementation NSFetchedResultsController (RSFetchedResultsControllerHelpers)

-(NSUInteger) numberOfObjectsForSection: (NSUInteger) section {
    id <NSFetchedResultsSectionInfo> sectionInfo;
    sectionInfo = [self.sections objectAtIndex: section];
    return sectionInfo.numberOfObjects;
}

-(id) objectAtRow: (NSUInteger) row inSection: (NSUInteger) section {
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
    return [self objectAtIndexPath: indexPath];
}

@end

In numberOfObjectsForSection we are accessing the NSFetchedResultsSectionInfo information from the sections collection for the appropriate section, and then returning the result.

In objectAtRow:InSection we are constructing the NSIndexPath instance within the function, which helps to keep the callers code cleaner and less cluttered.  Note that there is no need to call [cci]release[/cci] on the NSIndexPath instance as its created via a convenience constructor (that has already autoreleased the instance).

Making Use of the Category Methods

The following code snippet shows how we can now make use of these methods, in this case within the UITableViewDataSource protocol methods of a Table View.

-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
    return [self.fetchedResultsController numberOfObjectsForSection: 0];
}

-(NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    return [[self.fetchedResultsController objectAtRow:section inSection:0] title];
}

-(NSInteger) tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
    Group *group =[self.fetchedResultsController objectAtRow:section inSection:0];
    return [group.datasets count];
}




Leave a Reply

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