Using Enums to Track TableView Sections/Rows In Objective-C

It's rare in iOS development to build an app without using UITableView at all. Anytime you see a view on an app that is a list of data, a developer likely used a UITableView to build that list. Think of your Contacts app, or the part of the Messages app that shows a list of your conversations.

Because of this, we've developed some standards or best practice recommendations for how we generally implement the necessary methods. With every UITableView you must implement at minimum two methods: cellForRowAtIndexPath: and numberOfRowsInSection:. In practice, you often end up implementing many more than just the two base methods, and its nice to have named ways of keeping track of your count of Sections and/or Rows in your table view. 

For example, say you are building an app for meal tracking. You might have sections for Time of Day, within which you might have rows for meals and snacks. The rows might change but at this point you think you'll always have the same styling for each Section. In this case we'll also be implementing numberOfSectionsInTableView: and titleForHeaderInSection: to indicate the times of day. 

Without using enums you might hard code the section number just using an integer:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 
{
    switch (section) {
    case 0:
        return [VOKLocalizedStrings morning];
    case 1:
        return [VOKLocalizedStrings afternoon];
    case 2:
        return [VOKLocalizedStrings evening];
    default:
        NSAssert(NO, @"Unexpected section in Time Of Day TableView");
        return nil; 
    }
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 3;
}

Imagine how difficult this makes refactoring, for the original dev, and especially for future developers who might work on this project. If a design change comes along to rearrange the sections or add a section you have to remember to change it in many places. In this example we are only showing two methods, but in practice this can be 5-10 methods or more. We centralize this by keeping track of sections using an integer-based enum like so:

typedef NS_ENUM(NSInteger, VOKTimeOfDaySection) {
    VOKTimeOfDaySectionMorning, //Value: 0
    VOKTimeOfDaySectionAfternoon, //Value: 1
    VOKTimeOfDaySectionEvening, //Value: 2
    VOKTimeOfDaySectionCount //Value: 3
}

Integer based enums default to having an initial value of 0, so adding on the final value as VOKTimeOfDaySectionCount will always give us the total number of sections, helping avoid the all too often “off by one” issue. Now we can return VOKTimeOfDaySectionCount for numberOfSectionsInTableView:, and in our titleForHeaderInSection: method, we can switch over our enum. I really like this because instead of just switching on a number we have a name that means something. VOKTimeOfDaySectionMorning needs the morning string, instead of “section 0” needing the morning string. Here's an example for how titleForHeaderInSection: is implemented using our enum:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    switch (section) {
    case VOKTimeOfDaySectionMorning:
        return [VOKLocalizedStrings morning];
    case VOKTimeOfDaySectionAfternoon:
        return [VOKLocalizedStrings afternoon];
    case VOKTimeOfDaySectionEvening:
        return [VOKLocalizedStrings evening];
    default:
        NSAssert(NO, @"Unexpected section in Time Of Day TableView");
        return nil;
    }
}

And how we would implement numberOfSectionsInTableView:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{ 
    return VOKTimeOfDaySectionCount
}

This allows you to reorder the sections much more easily since the order is only tied to the enum value. Hard to imagine reordering the time of day, but in practice it’s quite often that product or clients might decide a different section has higher priority, and ask you to reorder sections. This technique also makes it easier to add another section since if you add a fourth section, the count would get auto-incremented:

typedef NS_ENUM(NSInteger, VOKTimeOfDaySection) {
    VOKTimeOfDaySectionMorning, //Value: 0
    VOKTimeOfDaySectionAfternoon, //Value: 1
    VOKTimeOfDaySectionEvening, //Value: 2
    VOKTimeOfDaySectionNight, //Value: 3  
    VOKTimeOfDaySectionCount //Value: 4
}

Because we implemented numberOfSectionsInTableView: with the enum, there is now no extra work we need to do to have the correct number of sections returned: VOKTimeOfDaySectionCount is still the correct value for numberOfSectionsInTableView. And we need to add just one more case for VOKTimeOfDaySectionNight in titleForHeaderInSection:. If we forgot to add that case and ran the project we would hit the NSAssert and be reminded to add handling for the new section. 

This pattern can be used the same way for numberOfRowsInSection: and cellForRowAtIndexPath: by switching on indexPath.row. All in all, it makes for more readable code with better naming, and easier refactoring, which means less headaches for everyone.