UICollectionViews are not historically great at understanding what’s happening with their data. They have a data source and when
reloadData() is called on them they present whatever data is returned from their delegate methods.
If you want to animate insert/delete/reload/move operations you have these options to choose from:
- do it manually with the
- use Swift 5.1 ordered collection diffing
- back the collection with
CoreDataand use it’s utility functions
In this article we’re gonna talk about the 4th option which isto use
UICollectionViewDiffableDataSource as the data source of your collection view. Given that your items are
Hashable, it will figure out the differences between two snapshots of a given data source and do all the animated updates for you.
The iOS Weather app doesn’t let you tap on the upcoming days to show you more refined weather forecasts. I find it unacceptable, so here’s my solution to that, a weather app that tells you random forecasts for as long as you wish, in hourly resolution.
As you can see, the screen is a multi-axis scrolling collection view that displays different items:
- hourly weather in the first section
- daily in the second. You can tap on these to reveal the hourly forecast for that day.
(the layout I used is
- How to handle heterogeneous items with
- How to organise code so we don’t end up with a massive view controller?
- How to make an item selectable and refresh only those items that needs to be refreshed? (tapping on a day will show hourly forecast for that day)
1. How to handle heterogeneous items with
If you take a look at the documentation of
UICollectionViewDiffableDataSource you’ll see its declaration is:
We’ll need sections and items, both conforming to
Hashable protocol. For
SectionIdentifierType we use this:
The data is held together in a view model called
WeatherViewModel , this contains all the information that defines a weather screen. We define a separate entity for
Hour that will be used to configure the cells. These are associated values in the
Item enum cases, so the
UICollectionViewDiffableDataSource can work with this type (as homogenous items).
The data source subclass is defined so for easier usage:
The cell configuration happens in the block that is passed in the init function of the
UICollectionViewDiffableDataSource , at this point we have the exact type information from the enum cases:
2. How to organise code so we don’t end up with a massive view controller?
Regardless of the architecture you are using, it’s likely that you don’t want to assign the responsibility of managing the list’s content to the view controller. Hence we create a view model that encapsulates all the information that the view controller -> collection view -> cells need to render the screen.
As I mentioned before, the concrete
Hour types are wrapped in a value associated enum. We define a dictionary with
Section as keys and the
Item enum arrays as values. The title represents the location where we request the weather from:
How you propagate the view model to the view controller is up to you, however I show an example where a configure method is called on the view controller to pass on the view model.
3. How to make an item selectable and refresh only those items that have changed?
Selecting a different day should not cause a refresh for the cells of other days. It should refresh hourly breakdown cells, but only for the hours where the weather icon or temperature have changed.
At this point we need to introduce another entity to decouple the business logic (to select a day and show its hourly entries). Everyone uses their preferred architectural pattern, let’s go with a simple
Presenter now as we use VIPER in Wise.
Let’s define two more entities called
HourlyEntry, these represent the data we store (the
Hour we defined before are representing the view’s content — given the simplicity of the domain, these are almost identical). A unique
id property is added, so we can identify which day is selected (and we add this
id to the view related model as well, so on tapping we can associate the day model with the day view data model).
When the user taps on a day, the view tells the presenter to select the day
the presenter then loads the new day’s data (1.) (in the production app this will invoke a network request) and then configures the view with the new data (2.)
You can find the weather demo app’s code here
We are working on a feature at Wise we call Insights. It enables users to have a better understanding of their spendings.
It’s the same concept as the weather app (what a coincidence), a collection view with heterogeneous items in it:
- the months in the first section
- a “legend” in the second
- and the categories you spent your money on in the third.
Interested in solving problems like this? We’re hiring. Check out our open Engineering roles. (Feel free to message one of our tech recruiters on LinkedIn or sign up for job alerts on our careers site if you’re interested in joining us, but can’t find a suitable position).
- To animate changes in the collection view’s data we can use a diffable datasource
- If we have heterogenous (e.g. hourly weather entry / daily weather entry in 1./2. section) items, we can wrap them in a value associate enum
- To minimise code in view controller and reduce its responsibility, encapsulate the data and inject it
- To select an item we added an
idproperty, that acts as a link between the view and data model
- Demo project