How to Build a TableView with Multiple Cell Types using Protocols
Understanding how UITableViews (and UICollectionViews) works as an iOS developer is like understanding how salt and pepper works as a cook. They’re in the majority of the popular apps, and despite being relatively simple to use, we can get really intricate with their implementation.
Implementing a simple UITableView can be really simple, thanks to the use of protocols in the UIKit. The primary protocols that we need to hook up our data to our UITableVIew are: UITableViewDataSource, and UITableViewDelegate. But what does that mean that our ViewController conforms to these protocols? (And why are they named delegates?)
Protocols
Interestingly enough, the word somewhat explains itself when we think about it. Something that conforms to a protocol, is something that satisfies the protocols requirements, kinda like a promise. So essentially when we’re saying our ViewController conforms to the UITableView protocols, we’re promising the ViewController will have specified variables and be able to perform specified methods. Looking at the UITableViewDataSource protocol we see:

We can see from the documentation that all we need to do is to implement the two required functions to satisfy the protocol. So where does delegates come from?
Delegates
When we delegate something, we pass off the responsibility to someone else. For example, Friend A might delegate the task of throwing out the trash to Friend B. Friend A doesn’t necessarily care how Friend B gets rid of the trash, they just need to know that Friend B has the ability to throw out the trash.

Bringing it back to iOS development, essentially we’re declaring a method as an action we want to delegate to the responsibility of implementing. This makes sense, because not all friends like to throw trash the same way.
Multiple Cell Types
When we want to get complex with UITableViews, we can create multiple prototype cells. This makes sense for things like the FaceBook feed where you might have different post types (new photo, shared video, etc.). So how do we manage to configure all these different cells when they are taking in different data and configuring differently?
When I first came across this problem, my initial thought would be to have a giant switch statement in the cellForRowAt method like:
The largest reason this will have difficulty scaling is defined by the switch statement. Logic built around an enumeration requires making changes to a model that is shared across multiple objects. We can get around this with protocols and some force casting to simplify things.
Guard Usage
The tableView function dequeueReusableCell has a guard statement because we’re attempting to cast a cell as a certain type. This is a safe way to ensure cell type is correct, but also leaves us in a weird state when it fails. The method dequeueReusableCell will fatalError if it fails to find the cell specified. Doing this largely helps understanding the state of the app because returning a default cell isn’t expected.
That’s a lot nicer, but we’re still stuck with that weird switch statement. Let’s try and remove that using protocols.
Remove the Switch Statement
This is when we get back to throwing the trash. If we look at what the cells are doing inside the cellForRowAt method, they’re all just configuring. We can cast the cell as an object that conforms to a protocol? We’re telling the cells to perform a similar action, update the cell’s information.
This can be a little tricky at first here’s an example:
ViewController
The ViewController is often the delegate/datasource for our UITableView, the dataSource is what is supplying the data, in this case we are going to reference an array of CustomElementModels. Note that this is an array of objects that conforms to CustomElementModel.
CustomElementModel Protocol
This is where you put all the required variables and functions for all of the models. Here, I’m just specifying that I want each model to at least tell me what type it is, restricting the type to the enum: CustomElementType.
CustomElementCell Protocol
Again, this is where you would put all the required stuff across ALL cells, and in this case, I just want to ensure that EVERY object that conforms to the CustomElementCell protocol, has a configure function that takes an object that conforms to CustomElementModel.
Applying That Sweet Knowledge
Now, we can essentially use the type that we know each model has as a variable to determine the cell identifier in the UITableView and then cast it as an object that conforms to CustomElementCell.
Some Sweeter Knowledge
I know some of you guys are probably freaking out, I’m force casting a lot, don’t worry. I know it’s scary, but this is one of those instances where it’s okay to force cast, here’s why.
Like I said earlier, dequeueReusableCell throws a fatalError if it cannot find cell with the specified parameters. If we successfully dequeue a cell, is there any scenario in which it will successfully cast as another cell in an intended way? In development, you want to force cast the cell because you’re expecting it to be that type, if it were to cast as another cell you’re not expecting, you could introduce more bugs into your app.
What about the last line, though?
return customCell as! UITableViewCellWe are safely force casting in this scenario, but why do we have to force cast? If we look back at our protocol, the only information we have is that the cell is able to configure, it doesn’t remember that it’s a UITableViewCell because we casted it as an object that conforms to the protocol.
But that doesn’t mean it is no longer a UITableViewCell. When we are casting something, we are casting within that certain scope and not actually changing what it is at the core. We can safely cast it as a UITableViewCell because, we know the only instance in which we reach this part of code is when dequeueWithIdentifier succeeds. Therefore, the cell must be a UITableViewCell.
An Actual Example
I know, it can be confusing, or at least it was for me when I stumbled upon this solution. Here’s an example of how the profile CustomElementType would work:
Here we can see that the ProfileElementCell satisfies the protocol by including the configure function specified in the protocol. We then take the model which conforms to a CustomElementModel and cast it as a specific model.
That’s it. We did it.
Hope you enjoyed my first Medium article, if you have any questions or feedback, feel free to leave a comment.
Bonus Points: You can use a type alias to simplify and clean up some of the logic.
Edit: Cleaned up word choice.