A UI component you might see a lot in iOS apps is a UITableView with multiple sections and different cells per section. This can be difficult to manage and usually results in your code being a mess and difficult to maintain. I wanted to take some time in looking at this to find the best way of dealing with this problem. So first, let’s go over what you should not do.
Unfortunately, I’ve seen the following a lot in some of the codebases I’ve looked at. A giant if else statement on the indexPath.section and cells are dequeued and populated all in the cellForRowAt method. Sometimes I’ve seen the cell population being handed over to the cell which makes things a little cleaner, but still – this will result in issues. For example, if you wanted to collapse a section, because you’re relying on the int value of the section, you’re gonna run into huge issues. So please, don’t do this. Please.
A second method I’ve seen is slightly better but still leaves me feeling weird. The idea is you need to somehow store an array of your data items, but you obviously have different data items for each cell so you can’t just directly plug them all into a single array. So you create a protocol which has some basic variable like an enum of what type of cell it is, you make all your data items conform to this protocol and then you can put all your data items into a single array. Then in cellForRowAt you can get the current item out of the array, switch on its type provided through the protocol and then decide which cell type to dequeue.
Up to this point this is fine. The issue comes next – the protocol abstracted data item is passed to the cell’s population method and then it is casted back to the de-abstracted original data item, which is then used to populate the cell. This is the part I don’t like, since once something has been abstracted to a protocol it really shouldn’t be cast back.
So what’s the way to do this?
With the protocol oriented way, the protocol needs to expose some enum for you to switch on and then you cast that item to what you expect it should be. So why not just plug the data straight into the enum rather than abstracting it to a protocol? Swift’s enums have associated values which allow you to achieve this. Let’s have a look at a simple example:
First define a generic struct that will hold all data for each section. It has a title property and an array of data items, which is an enum.
This enum has a case for each cell type, with a relevant associated value. Now when it comes to using this in cellForRowAt, we can grab the current data item for the current section and pull out the actual data to populate our cell – no casting needed.
Here’s a link to a project that uses this pattern to give you an example of how to do this.
As a side note, there’s also two generic extension functions on UITableView to help make cell dequeue and registering cleaner, which are also in the project.