Elegantly prepare data source for Table View and Collection View
Table View and Collection View have been the pillars on which almost all iOS applications have been built. Building these collections is pretty easy and straight forward. The basic flow for populating any Table View or Collection View is:
The data source is generally the list of data that is supplied to the table view. The table views data source methods then iterate on this list and instantiate cells.
The datasource methods that take part in this:
There are a lot of different ways to configure your datasource for the table view. Starting from a simple list of data models to more complex generic approaches using protocols.
When we have a single cell the datasource remains pretty simple. Let us look at the following response which has a list of your friends and their contact number:
When we parse this response to a model named
Contact, our data source method and subsequent implementation would look something like this:
So far, so good. Everything looks neat.
Multiple cell types
Let us take the example of the below screen. This screen is from the Nearby app from my blogpost on MVVM. The Nearby app fetches JSON from Google APIs.
For simplicity, we will not take the Google API JSON directly. Rather we will use a simplified JSON for understanding:
If you observe closely, you can see there are three categories of cells in this screen. Let us name them
TopPickCell: This cell is the pagination cell which will hold the first Place from every category
CategoriesCell: Lists all the categories
PlacesCell: Lists the places grouped under a single category
There is a huge problem when it comes to implementing
table view having multiple cell types. We end up in lots of if-else checks to determine the cell type based on the data.
Let us look at different ways of configuring our datasource and subsequent implementations.
Enum based datasource implementation
Preparing the data source:
Though there are a lot of ways to configure the datasource for the above cell, one would generally prefer to configure datasource based on the cell type as it would help to determine which cell to display and what data to show. Or else you can go for a lot of if-else checks.
I will not be listing the complete implementation details rather will give a high-level implementation detail.
What did we do here?
Place- Model to hold detail for a place.
CategoryType - Enum to hold the types of categories of places we have.
CategoryDetail- Model to hold a category detail
HomeListCellType-Enum to specify the type of cells that are present in the home page list. We have specified data of cells as associated values within these enums.
TopPicksCellcan be populated with a list of
Placethereby the associated value is an array of
CategoriesCellis a cell with a collection view that can be populated using a list of categories.
- We have multiple places cells grouped under a common category type. A
PlacesCellcan be build using a list of
Populating the data
A typical implementation of this approach will be something like this:
What did we do here?
- We have declared a
homeListDatasourceproperty that holds all the cell types and their associated values. I believe the logic to prepare the
homeListDatasourceis pretty straightforward based on the details of the cell data that we discussed earlier.
- In the table view’s datasource method, we distinguished between the cell with the help of our
- We created methods to provide cells based on the cell data that we got from our associated value.
Great … !! We did it. We composed our datasource in a pretty clean way and avoided any if-else checks in our implementation.
If you are using MVVM most often you can check out the code of Nearby app to have a glimpse of this approach implemented in MVVM.
- We have switch cases spread everywhere.
ViewControllerwill bloat with logic to configure the cell.
- Repetitive cell configuration methods in
- If cell types increase, you can imagine how ugly our
Protocols to the rescue
If you look closely, you would find many repetitive patterns in our implementation. The creation of cells is duplicated and even the configuration calls for the cells might seem duplicated as well except the data that we are passing to each cell.
If we can somehow remove these repetitions by abstracting the data that is needed by every cell, then our problem will be sorted.
Let us abstract all details needed by the cells in Presentation models which would confirm a common protocol.
The common protocol
TableCellRepresentable contains an identifier property, which will help to determine the cell type from the identifier of the cell.
Now you might think that here the presentation model is being pretty tightly coupled with the cell. But it's okay. Presentation models of a particular view should be specific to the view and not reused across. It is always the UI component that needs to be reused.
Cells can also be abstracted in a similar manner.
TableCellViewable protocol holds the configuration method call in it. Every cell conforms to this and implements its own configuration methods.
View Controller Implementation
Now we can have a list of
TableCellRepresentable as our datasource.
We need not do anything fancy to prepare the
datasource here. We have to just map our data into our presentation models. An example could be the following:
HomResponse is the data model that is got after parsing the Nearby home response.
Isn’t this clean and reusable also. You can also reuse this similar approach for other cells and presentation models as well.
Typecasting and type safety might be a problem that comes as a trade-off for making your code short. For logging any abuse of the protocols, we can log the errors if any as done in the above implementation.
You have pros and cons in both the above approaches, you have to decide what is best suited for you.
I would love to hear from you
You can reach me for any query, feedback or just want to have a discussion by the following channels:
Please feel free to share with your fellow developers.