WWDC 19
Advances in UI Data Sources
Current approach:
UI***ViewDataSource
protocol with numberOfSections
, numberOfItems
and cellForItemAt
methods
This works well for static data, but when you need to update the list later, you need to either reload all data, which results in bad UX, or calculate the changes manually, which is complicated and error-prone
The UI and data source must always agree and there’s no single source of truth
New approach:
Diffable data sources
performBatchUpdates()
⭢ apply()
Snapshot: saved current state of the UI
Uses identifiers instead of index paths
UICollectionViewDiffableDataSource
, UITableViewDiffableDataSource
, NSCollectionViewDiffableDataSource
Setting up:
dataSource = UICollectionViewDiffableDataSource<…>(collectionView: collectionView) { collectionView, indexPath, mountain -> UICollectionViewCell? in … // basically the same as cellForItemAt }
If we use native objects/structs as identifiers, we get them passed here so we don’t need to look them up in an array by index path
Updating data:
Step 1: Create an empty snapshot
let snapshot = NSDiffableDataSourceSnapshot<Section, Mountain>()
Step 2: Fill it with sections and items
snapshot.appendSections([.main]) snapshot.appendItems(mountains)
Section and item identifiers can be your own hashable types (e.g. an enum for sections)
Step 3: Apply the snapshot
dataSource.apply(snapshot, animatingDifferences: true)
→ it’s safe to call apply()
from a background thread (but don’t mix main & background threads)
Alternatively, you can use a snapshot of the existing state:
let snapshot = dataSource.snapshot() snapshot.deleteItems(…) snapshot.appendItems(…) dataSource.apply(snapshot)
Getting an identifier from an index path in other delegate methods:
let identifier = dataSource.itemIdentifier(for: indexPath)