Swift — UITableViewDiffableDataSource
Introduced with Swift 5, the generic class UITableViewDiffableDataSource provides a brand new API which will facilitate the configuration of a table view.
The generic class UICollectionViewDiffableDataSource has also been released.
class UITableViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType> : NSObject where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable
The ‘UITableViewDiffableDataSource‘ takes two generic types namely ‘SectionIdentifierType‘ and ‘ItemIdentifierType‘ which must conform to the Hashable protocol in order to be uniquely identified.
init(tableView: UITableView, cellProvider: @escaping UITableViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType>.CellProvider)
The generic class takes an initializer with two parameters that is the table view and a cell provider which is an escaping typealiased function.
typealias UITableViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType>.CellProvider = (UITableView, IndexPath, ItemIdentifierType) -> UITableViewCell?
The typealias represents a regular function with three parameters that is a UITableView, an IndexPath and the generic ‘ItemIdentifierType‘ type which is our data model. The function returns a UITableViewCell.
Within the escaping closure, we’ll handle cell dequeuing and configuration operations, just like before in our previous datasource method.
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
The first generic type ‘SectionIdentifierType‘ will represent the sections of the table view while the second generic type ‘ItemIdentifierType’ is our data model. Both types must be hashable.
The use of enums to provide the table view sections come in handy as they already conform to the hashable protocol.
As for our data model, we do need to explicity declare a Hashable protocol conformance.
These new APIs are pretty convenient as we don’t need to work with ‘Int‘ anymore, only with type-safe identifiers.
enum GuitarsSection: CaseIterable {
case gibson
case martin
}struct GuitarModel: Hashable {
let name: String
let year: Int
}let tableView = UITableView()let dataSource = UITableViewDiffableDataSource<GuitarsSection, GuitarModel>(
tableView: tableView,
cellProvider: { tableView, indexPath, guitar in
let cell = tableView.dequeueReusableCell(
withIdentifier: reuseIdentifier,
for: indexPath
)
cell.textLabel?.text = guitar.name
cell.detailTextLabel?.text = String(guitar.year) return cell
}
)tableView.dataSource = dataSource
Once our datasource object is created, we need to use it somewhere.. hence the NSDiffableDataSourceSnapshot class.
class NSDiffableDataSourceSnapshot<SectionIdentifierType, ItemIdentifierType> where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable
Both generic types must match the UITableViewDiffableDataSource ones.
Whenever a table view needs updates, a datasource snapshot must be created — using the latest data — and applied to the datasource object.
An update on the table view will then automatically be performed in the most efficient way.
Calling the populate(list: animated:) will trigger the update.
func populate(
list: Guitars,
animated: Bool = true
) { let snapshot = NSDiffableDataSourceSnapshot<GuitarsSection,
GuitarModel>() snapshot.appendSections(GuitarsSection.allCases) snapshot.appendItems(list.gibsons, toSection: .gibson)
snapshot.appendItems(list.martins, toSection: .martin)
dataSource.apply(snapshot, animatingDifferences: animated)
}
The apply() method enables us to animate the changes if needed.
Conclusion
The UITableViewDiffableDataSource generic class removes a lot of boilerplate code enabling us to create a datasource object in a very declarative way.