iOS 13 Diffable Data Sources Tutorial

Goodbye reloadData() and performBatchUpdates()

Declarative programming was wholeheartedly endorsed by Apple during its WWDC 2019 event.

Be it through the introduction of a new UI framework, SwiftUI, or by upgrading the existing UIKit frameworks, Apple showcased the power of declarative programming in boosting iOS and macOS development.

Among the newer enhancements in UIKit, compositional layouts and diffable data sources not only looked promising but also gave a strong indication that the UIKit framework isn’t going away anytime soon.

Our Goal

  • Understanding the need for diffable data sources.

  • Knowing how diffable data source and snapshots work.

  • Accessing diffable data sources in index path APIs.

In this article, we’ll be implementing diffable data sources using a UITableViewin an iOS application.

But, before we delve into the intricacies of diffable data sources, let’s for once just look at the traditional ways of setting up the table and collection views.

Traditional Approach

The traditional approach for plugging data sources requires conforming to the UITableViewDataSource protocol and implementing the methods numberOfItemsInSectionnumberOfSectionscellForItemAt.

This works all fine for simple TableViews and CollectionViews until we need to start updating the rows. For updating, both the approaches, reloadData() and performBatchUpdates(), had their own sets of issues.

While reloadData() would ruin our chances of showing nice animations, performBatchUpdates() would easily lead to errors if not handled carefully.

Errors like the one shown below were pretty common with performBatchUpdates():

Terminating app due to uncaught exception ‘NSInternalInconsistencyException’,
reason: ‘Invalid update: invalid number of items in section 0.
The number of items contained in an existing section after the update 
must be equal to the number of items contained in that section before.

Gladly, Apple brought in diffable data sources to tackle such errors on our behalf.

Diffable Data Sources — An Error-Free World

Diffable data sources mark a shift towards a declarative paradigm by handling mechanisms like synchronization, updating the changes on its own, thereby making things less error-prone and with the new state-driven approach.

By using the diffing tool, diffable data sources take care of updating TableView and CollectionView rows between the states (current and new).

The class NSDiffableDataSourceSnapshot represents the state of a UI. To update a TableView or CollectionView, we just need to apply(snapshot) on the UITableViewDiffableDataSource and it takes care of updates and animation for you.

The apply method can be executed from the background thread as well. Doing so, the diffing that takes O(n) time, takes place in the background after which the changes are updated to the main thread. This speeds up the updating process.

Apple recommends sticking with either the background or main thread when calling the apply method.

Setting Up the Diffable Data Source

For animation and updates to work, we first need to set up our UITableViewDiffableDataSource on the UITableView.

The following code creates a data source and sets it on the UITableView instance:

enum Section : CaseIterable {
case one
case two
class ViewController: UIViewController {
private let tableView = UITableView(frame: .zero, style: .insetGrouped)
private let cellReuseIdentifier = "cellId"
private lazy var dataSource = makeDataSource()
override func viewDidLoad() {
tableView.register(UITableViewCell.self,forCellReuseIdentifier: cellReuseIdentifier)
view.addSubview(tableView) //additional autolayout setup in the source code
tableView.dataSource = dataSource
updateDataSource(animated: true)
func makeDataSource() -> UITableViewDiffableDataSource<Section, Movies> {
let reuseIdentifier = cellReuseIdentifier
return UITableViewDiffableDataSource(
tableView: tableView,
cellProvider: { tableView, indexPath, movie in
let cell = tableView.dequeueReusableCell(
withIdentifier: reuseIdentifier,
for: indexPath)
cell.textLabel?.text =
return cell

Snapshots don’t rely on index paths for updating items. Instead, it relies on type-safe unique identifiers to identify its sections and items uniquely.

To generate these unique identifiers, the section and items must conform to the Hashable protocol.

While Sections in the above code are enums that conform to Hashable implicitly, the Movies struct needs to implement the Hashable protocol as shown in the below code:

struct Movies: Hashable {
let identifier: UUID = UUID()
let name: String
func hash(into hasher: inout Hasher) {
return hasher.combine(identifier)
static func == (lhs: Movies, rhs: Movies) -> Bool {
return lhs.identifier == rhs.identifier

Populating Diffable Data Source

To update or populate the data source, we simply add the necessary sections and its items on a snapshot instance and apply it. The following code shows how that’s done.

func updateDataSource(animated: Bool) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Movies>()
snapshot.appendItems([Movies(name: "Inception")], toSection: .one)
snapshot.appendItems([Movies(name: "War")], toSection: .one)
snapshot.appendItems([Movies(name: "Departed")], toSection: .one)
snapshot.appendItems([Movies(name: "Departed")], toSection: .two)
dataSource.apply(snapshot, animatingDifferences: animated)

Accessing Data

To update or remove data from the TableView, we need to get a hold of the current snapshot which is done by invoking dataSource.snapshot().

To access items that have index path based APIs in diffable data sources, we need to translate the index path to the item identifier in the following way:

dataSource.itemIdentifier(for: indexPath)

The following code snippet removes elements when selected from the UITableView:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let movie = dataSource.itemIdentifier(for: indexPath) {
var currentSnapshot = dataSource.snapshot()

In return, we get the following outcome of our application:

Note that the “Departed” item row, though present twice, is identified uniquely because of the Hashable protocol.

In another scenario, where the item is non-hashable, the diffable data source wouldn’t be able to identify items with the same content from each other and would end up overwriting the previous item.

So, in the above case with a non-hashable item (let’s say, string), “Departed” would be displayed just once.


We’ve seen how the diffable data source introduces a whole new way of building data sources for our CollectionView and TableViews.

It uses item identifiers instead of index paths and possesses the ability to invoke the apply() function from the background as well.

So, it’s time to say no to reloadData() and performBatchUpdates(). The full source code is available in the GitHub repository.

That’s it for this one. I hope you enjoyed reading it.