Creating a Reusable UICollectionViewDiffableDataSource Implementation Using MVVM
In this article, we’re going to create a generic and reusable implementation of UICollectionViewDiffableDataSource
in MVVM. Hope you’re excited!
Short Background
UICollectionViewDiffableDataSource
is a declarative API for UICollectionView
data source released by Apple in iOS 13.
It sets an alternative to the old UICollectionViewDataSource
— which provides us with delegate methods to determine the number of sections & cells and cell configuration based on our data set, and methods to remove and insert cells into the collection view. This approach isn’t always safe as things can often go wrong on our side; the data may not always be there at the collection view’s index path or we can miscalculate the ranges when inserting or removing cells at an index path.
Apple figured this out and introduced the diffable data source — an API that manages cell creation, insertion, order and deletion for us based on a snapshot — NSDiffableDataSourceSnapshot
which we just provide with sections and items.
In this article, we will look at a simple approach to making a generic and nicely reusable model of UICollectionViewDiffableDataSource
in MVVM.
Note: While we are working with
UICollectionView
in this article, the process is nearly identical forUITableView
usingUITableViewDiffableDataSource
.
In this example, we will build a (very) simple notes app that lets the user compose notes, display them in a collection view and delete them upon tap.
You can download the starter project to follow along :)
In order to be as efficient as possible and create as little number of abstractions as we can, let’s start by defining a protocol named Providable
that our UICollectionViewCell
subclasses will adopt — in this case NoteCell
.
- Note that the
ProvidedItem
associated type needs to conform toHashable
in order to work with diffable data sources.
- You can setProvidable
to be bound toUICollectionViewCell
if you want, which is more compiler-friendly, but I find it to be useful in other use cases as well :)
In our NoteCell
class, we’re going to conform to the protocol and define the associated type as Note
:
Great — now let’s get to the main treat.
In this approach, we’re going to create a superclass to our standard View Model, which will contain all the setup and logic of the data source.
We’re going to begin with creating our CollectionViewModel
class.
- We inherit from
NSObject
to enable our subclass view models to conform toUICollectionViewDelegate
later on.
- I am making use here of theBinding
class for MVVM (sometimes referred to asBox
. You can find the implementation of it here.
As we can see, UICollectionViewDiffableDataSource
requires a Section type and an Item type as its generics. For the item, we will simply pass in the ProvidedItem
from the CellType
. As for the section, I may cover working with mutliple sections in a future article, but for now and for simplicity sake we will define a single section — main
. Let’s define it now as a simple enum.
Now, let’s implement some of the setup logic of the data source.
What we’re doing here is creating our instance of UICollectionViewDiffableDataSource
(that we typealiased as DataSource
) and providing its CellProvider
closure with our implemented function — cellProvider(_:indexPath:item:)
.
It’s time for some actual logic, isn’t it?
We now have everything in place to add our main logic methods — add(_:)
, remove(_:)
and update()
to our CollectionViewModel
superclass.
This is basically where the magic happens.
- In the
update()
method, we are creating a snapshot with our data set. When we calldataSource.apply()
, the data source compares the new snapshot to the current one the updates any changes. SinceNSDiffableDataSourceSnapshot
is astruct
(i.e. Value Type) the comparison process is as lightweight and efficient as it gets. - The
add(_:)
method appends a newItem
object to the data set and updates. - The
remove(_:)
method removes any item from the data set that matches the provided parameter and updates. This functionality is enabled since our item conforms toHashable
, which in turn inherits fromEquatable
.
That is our logic implemented for now, but remember that NSDiffableDataSourceSnapshot
has much more functionality available for you like reordering cells and sections, inserting, deleting and more. See the documentation
Let’s proceed to connect all the pieces together.
We’re going to inherit from CollectionViewModel
in our LibraryViewModel
class with our cell type of NoteCell
, and conform to UICollectionViewDelegate
to implement collectionView(_:didSelectItemAt:
) for deleting items.
That is really all there is to it in our view model, now let’s wrap it up in our LibraryViewController
.
In the above code we first inject the data source from the factorial method from our CollectionViewModel
class.
In line 31 I call the add(_:)
method when receiving a new note from the publisher (as I mentioned in the comment, any communication pattern will do just fine).
And that’s it!
I really do hope you enjoyed and took something from this article :)
You can clone (or download) the full project at this link.
Have a great week and an optimistic remainder of 2020!