Dealing with different kinds of cells in Swift — Part 3 of 3

Javier Valdera
Busuu Tech
Published in
4 min readSep 18, 2017

--

In the last post we generalised an implementation of a Cell Controller to handle both the UITableView and the UICollectionView cases, reducing the amount of boilerplate we will have to write in our day-to-day work.

In this instalment, we will see how to expand this idea further, reducing how much code we write even more and increasing readability. So let’s jump into it!

The ugly bits

Let’s have a look at how the implementation of the PhotoTableCellController looks like for the moment:

There are a couple of things here that are a bit ugly:

First of all, we are having to say twice that we are controlling the PhotoTableViewCell; once in the cellClass method and another time in the configureCell method. This could lead to a mistake, using different cell classes in each case, and we won’t be aware of that until the app crashes at runtime.

Also, in the configureCell, we have a forced downcast from UITableViewCell to PhotoTableViewCell. Although necessary because UITableView returns UITableViewCells, is not something you want to see in your code. It would be much better if the configureCell method were able to receive a PhotoTableViewCell directly.

These bits are also some kind of boilerplate, because even though they are particular for each Cell Controller, they are steps you have to take in every single one of them.

More generalisation, more simplicity!

As mentioned before, in the previous post we used generalisation over the particular View (Collection or Table) to be able to build a generic Cell Controller. We can now build on top of that generalisation and be able to specify the concrete cell the Cell Controller is managing, eliminating the last bit of boilerplate we have!

First, we are going to link the two types of cells to the right ReusableCellHolder, so that we prevent trying to control a UITableViewCell in a UICollectionView. For this we are going to use an old friend of ours, protocols with associated types:

This protocol doesn’t seem to do much, and that is correct. It doesn’t do anything by itself, but in conjunction with this small change to the ReusableCellHolder protocol…

As you can see, the associated CellType now enforces it to be a ReusableCell (which in our case, means that UITableViewCell and UICollectionViewCell will have to implement this protocol), with the additional requirement that its CellHolder must be Self. In other words, we are forcing in the implementation of the protocol by the cells, to tie UITableViewCell with UITableView and UICollectionViewCell to UICollectionView (trust me, everything looks clearer in the code 😉). This feature that was added in Swift 3.2 and Swift 4, so this won’t work if your project hasn’t migrated to one of these versions.

A couple of extensions will do the trick:

Had we tried to do this:

The compiler would have risen an error. Here is why: while inferring the CellType for UITableView’s implementation of ReusableCellHolder, it would have come to the conclusion that it must be UITableViewCell; then, the check that UITableViewCell conforms to ReusableCell will succeed, but checking the restriction that its CellHolder must be UITableView will fail (we have specified it to UICollectionView), rising said error. The compiler is preventing us from making such mistake.

Now we can implement a true generic Cell Controller:

We have two new final overrides, for the methods with the boilerplate we wanted to remove; and a new method for configuring the cell that, as you have probably noticed, will receive the right type of the cell we are controlling.

The pretty implementation

With this new Generic Cell Controller as base class, we can write new versions of our concrete Cell Controllers which are more readable and has no boilerplate.

This is the final implementation of the PhotoTableCellController:

Now everything is type safe: if you try to put different cell classes in the GenericCellController parametrisation and in the argument of the configureCell method, the compiler will throw an error. There are no more forced downcasts and we only need to focus on the bits of code that are really particular for our kind of cell.

Let’s compare this implementation with the implementation we had for the same Cell Controller in the first part of the series:

As you can see, the newer version is far more simple, far more readable, far more safe and far more pleasant to use in general. Nice, isn’t it?

It is important to highlight that these two implementation have the exact same functionality; we have just stripped the annoyance out of it! Also, the change we have introduced in this iteration doesn’t require any more changes in the View Controller or the Cell Controller Factory.

What now?

Now that we have reached this point, what else can we do? Well, there are a lot of different ways of expanding your Cell Controllers.

We at busuu have added other methods to the CellControllerType protocol, such as willDisplayCell() because we needed to do some work in the equivalent methods of UITableViewDelegate and UICollectionViewDelegate.

We have also added more methods to the ReusableCellHolder protocol, making more features of the respective views available for the cell controllers, such as cellForItem(at indexPath: IndexPath) -> CellType? in order to access the associated cell if it is displayed.

As you can see, this system is very flexible and scalable, so you can build things as complex as you want on top of it, expanding its functionalities and capabilities.

This series of blogposts have been a reflection of the exact journey we have followed at busuu in order to deal with this problem.

Here you can find our GenericCellControllers as an Open Source library alongside an example project in which you can see a “real world” usage of this pattern. Feel free to collaborate extending and improving it!

See you soon with more cool iOS development stuff!

--

--