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

Javier Valdera
Busuu Tech
Published in
3 min readSep 5, 2017

--

In the previous post we learned how Cell Controllers help us reduce complexity and code in our View Controllers, isolating it from the particulars of each cell.

In this entry we will see how we can write even less code making use of one of Swift’s cooler features (spoiler alert): protocols with associated types!

One protocol to bind them

At the end of last instalment, we ended up with an example of a TableCellController that looked like this:

Similarly, we can create an implementation of a CollectionCellController, for example, for a video cell, like this:

Have you noticed how the implementations for a TableCellController and a CollectionCellController are very similar? They basically differ on the specifics of their cells, which make sense, and the APIs for UITableView and UICollectionView, which don’t make so much sense, since they are very, very similar.

If only there were a way to unify their APIs so we could reuse more code… Well, there is! We can make use of protocols with associated types to achieve this goal:

This is the protocol that we will use to unify the APIs of the UICollectionView and the UITableView. As you can see, it uses the same signature as the methods in UICollectionView; this will save us some code.

The associated type CellType is the bit that will let us implement this protocol in both views. Let’s see how:

You can see that the compiler was able to infer that in the case of the UITableView implementation, the CellType is UITableViewCell, because that is the cell we are returning.

In the case of UICollectionView, since the protocol uses method signatures that the class already has, we don’t need to implement anything, and the compiler is able again to infer the right CellType.

One implementation to rule them all

Now that we have a unified API for UITableView and UICollectionView, we can write a Cell Controller that is able to handle any of those, with a unified code and making use of generics.

First of all, we will create a unified CellController protocol that will make use of our ReusableCellHolder as an associated type:

Now we can create an abstract implementation that, with the use of generics, will be able to handle both UITableView and UICollectionView:

Notice the use of static (vs class) and final methods. These ones are boilerplate code that we don’t want to repeat or change in subclasses. Instead we use the template pattern where classes just provide the type of cell by overriding cellClass and the logic to configure the cell by overriding configureCell.

Again, the compiler was able to infer that our associated type was indeed the generic T: ReusableCellHolder that we have defined.

How will the implementation of our PhotoTableCellController look like using this abstract class? Something like this:

As you can see, all the boilerplate and all references to the UITableView have disappeared from the code, maintaining the same functionality.

We don’t have to bother about the Bundle the Nib file is in, we have the registration of the cells for free. We don’t have to dequeue cells anymore, just do the configuration (if) needed. All this means less code and less points in which we can make a mistake.

Similarly, the implementation of the VideoCollectionCellController from before is reduced to:

The view controller, on the other hand, doesn’t change much, just a few changes to accommodate the new API from our generic Cell Controller:

On a side note, the Cell Controller Factory will have to change its return type to [CellController<UITableView>] , but we won’t replicate that code for simplicity.

In conclusion , using protocols with associated types we were able to unify the API of UITableView and UICollectionView; this enabled us to create an abstract generic implementation of a Cell Controller that takes care of the boilerplate, such as registering and dequeueing cells; inheriting from this abstract Cell Controller allows us to write less code in our specific Cell Controllers, increasing productivity and reducing the possibility of errors.

On the next and final part of the series, we will take this generalisation even further, thinning the Cell Controllers and making our code more readable and safe. See you soon!

--

--