iOS Tutorial: Collection View and Diffable Data Source

In this iOS tutorial, you’ll learn how to implement a collection view with UICollectionViewDiffableDataSource and NSDiffableDataSourceSnapshot. By Jordan Osterberg.

4.9 (37) · 3 Reviews

Download materials
Save for later
Share

In iOS 13, Apple introduced a major update to the UICollectionView API: UICollectionViewDiffableDataSource. This new API is more flexible and declarative than the complicated, brittle and error-prone UICollectionViewDataSource API.

In this tutorial, you’ll learn how to:

  • Replace the old UICollectionViewDataSource with the new UICollectionViewDiffableDataSource.
  • Use NSDiffableDataSourceSnapshot.
  • Add sections to the data source.
  • Add supplementary views to the data source.

Further, you’ll see how easy it is to animate changes with this new type of data source. I hope you’re excited to get started!

Getting Started

Start by downloading the project materials using the Download Materials button at the top or bottom of this tutorial.

The project, fittingly called RayTube, allows you to browse a collection of RayWenderlich video courses, search for a specific one, and tap it to view more details about it.

First up, open the starter project. Build and run.

UICollectionView with RayWenderlich courses as collection view cells.

You’ll see a list of RayWenderlich videos. Tap a video to see its details. Also, try searching for a specific video by title using the search bar.

At the moment, the filtered videos aren’t animated when you perform a search query.

The search results appearing without animation as the user searches

Obviously, this isn’t ideal. This UI looks like it stutters, which may not be the animation you want. You need to that smooth animation! While adding a diffable data source, you will automagically solve this issue as well.

What is UICollectionViewDiffableDataSource?

Before iOS 13, you’d configure a UICollectionView‘s data source by adopting UICollectionViewDataSource. This protocol tells the collection view what cell to display, how many cells to display, which section to display the cells in, and so on.

Note: If you haven’t used UICollectionView before, check out UICollectionView Tutorial: Getting Started to understand how it works.

The new UICollectionViewDiffableDataSource abstracts a significant amount of UICollectionViewDataSource‘s logic. This leaves less room for client code errors when handling collection view’s data source.

Rather than telling the data source how many items to display, you tell it what sections and items to display.

The diffable part of UICollectionViewDiffableDataSource means that whenever you update the items you’re displaying, the collection view will automatically calculate the difference between the updated collection and the one previously shown. This will in turn cause the collection view to animate the changes, such as updates, insertions and deletions.

Benefits of UICollectionViewDiffableDataSource

Here are three benefits of implementing UICollectionViewDiffableDataSource:

Assertion error because the collection view update is invalid

  1. Automatic data change animations: Whenever you add, update or delete data, you can get the data change animation automatically.
  2. Automatic data synchronization: To utilize collection view’s standard animation without UICollectionViewDiffableDataSource, you’d have to manually manage and synchronize data changes between the collection view and the data source. If you have a misalignment in one of the synchronization operations, you’d see an error like this:

    Assertion error because the collection view update is invalid

  3. Reduced code: Overall, you can write less code and benefit from the collection view’s data change animations and data synchronization.

Smart, right!? So, how do you make use of the new UICollectionViewDiffableDataSource? More on this next.

Creating a Diffable Data Source

UICollectionViewDiffableDataSource has two generic types: Section type and item type. If you’ve used collection views before, you should be familiar with the concept of sections and items.

To create your section type, add the following code below videoList in VideosViewController.swift:

enum Section {
  case main
}

Now that you’ve created the Section enum, it’s time to create the diffable data source.

To keep things concise, create a type alias for the data source. This mitigates the need to write UICollectionViewDiffableDataSource value types when you need to configure the data source as well as every time you need to reference the same data type.

Below the section type, write the following code to declare a DataSource type alias:

typealias DataSource = UICollectionViewDiffableDataSource<Section, Video>

Awesome! Build and run.

An Xcode error saying Video doesn't conform to Hashable

Swift bird saying "Uh oh... What is Hashable?"

As it turns out, your video data type needs to conform to Hashable.

Implementing Hashable

Hashable allows the diffable data source to perform updates when videos are added, removed or updated. Conformance to the protocol is needed in order to know whether or not two elements are equal to each other.

Open Video.swift. Make Video adopt Hashable:

class Video: Hashable {

Next, you need to implement the protocol methods. Add the following code below init(title:thumbnail:lessonCount:link:):

// 1
func hash(into hasher: inout Hasher) {
  // 2
  hasher.combine(id)
}

// 3
static func == (lhs: Video, rhs: Video) -> Bool {
  lhs.id == rhs.id
}

Here’s what you did:

  1. Implemented hash(into:), which hashes the given components.
  2. Added the id of Video to the hash. For videos, you only need the ID to know whether two videos are equal.
  3. Implemented the Equatable protocol’s == function, because all Hashable objects must also be Equatable.

Your project should now be able to build again without any errors.

Configuring The Diffable Data Source

Open VideosViewController.swift. Now that Video conforms to Hashable, you can finish creating the diffable data source.

Below viewDidLoad(), add the following code:

func makeDataSource() -> DataSource {
  // 1
  let dataSource = DataSource(
    collectionView: collectionView,
    cellProvider: { (collectionView, indexPath, video) ->
      UICollectionViewCell? in
      // 2
      let cell = collectionView.dequeueReusableCell(
        withReuseIdentifier: "VideoCollectionViewCell",
        for: indexPath) as? VideoCollectionViewCell
      cell?.video = video
      return cell
  })
  return dataSource
}

Here:

  1. You create a dataSource, passing in collectionView and a cellProvider callback.
  2. Inside the cellProvider callback, you return a VideoCollectionViewCell. The code you write in this function is the same as you’re used to seeing in UICollectionViewDataSource‘s collectionView(_:cellForItemAt:).

Now that you’ve implemented makeDataSource() you can delete the data source methods under `// MARK: - UICollectionViewDataSource`. Specifically, delete the following two methods:

  • collectionView(_: numberOfItemsInSection:)
  • collectionView(_:cellForItemAt:)

You can delete these methods because the diffable data source automatically handles these functionalities for you.

Next, it’s time to actually use the makeDataSource() you worked so hard on!

In VideosViewController, add the following property to the top:

private lazy var dataSource = makeDataSource()

This creates the data source for the collection view. You must mark it lazy because Swift requires VidoesViewController to complete initialization before you can call makeDataSource().
Finally, inside collectionView(_:didSelectItemAt:) replace:

let video = videoList[indexPath.row]

…with:

guard let video = dataSource.itemIdentifier(for: indexPath) else {
  return
}

This ensures the app retrieves videos directly from the dataSource. This is important because UICollectionViewDiffableDataSource might do work in the background that makes videoList inconsistent with the currently displayed data.

Build and run.

The RayTube app with no videos displayed

And…nothingness.

Before the collection view can display any cells, you need to tell it what data you’d like to display. This is where snapshots come in!