This definitely feels like a bug to me. The DiffableDataSource is passing along the stale version of the model object to the cellProvider (from the previous snapshot rather than the new one, which you can see by reloading again and you'll get the new object). I've written a backport of diffable data sources that uses the built in API on iOS 13+ and a custom implementation otherwise, and the bug doesn't exist in my custom implementation.
Diffable data sources were advertised to support using custom model objects rather than just identifiers, so to be told that we shouldn't be using it this way is a bit ridiculous.
From WWDC 2019:
For our mountain type, we'll look at our Mountains Controller, which is again, our model layer. And here, we see that we've declared mountain as a Swift struct. And we declared that struct type as hashable so that we can use it with DiffableDataSource natively rather than explicitly have to pass an identifier. And the important requirement there is just that each mountain be uniquely identifiable by its hash value. So we achieve this by giving each mountain an automatically generated unique identifier. Playground code to reproduce the second reload behaviour:
struct Model: Hashable {
var id: UUID
var name: String
init(id: UUID = UUID(), name: String) {
self.id = id
self.name = name
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: Model, rhs: Model) -> Bool {
lhs.id == rhs.id
}
}
let tableView = UITableView(frame: CGRect(x: 0, y: 0, width: 300, height: 500), style: .grouped)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
let dataSource = UITableViewDiffableDataSource<Int, Model>(tableView: tableView) { tableView, indexPath, model in
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = model.name
return cell
}
var model = Model(name: "A Model")
var snapshot = NSDiffableDataSourceSnapshot<Int, Model>()
snapshot.appendSections([0])
snapshot.appendItems([model])
dataSource.apply(snapshot, animatingDifferences: false)
_ = tableView // Shows "A Model"
model.name = "Another Name"
var secondSnapshot = NSDiffableDataSourceSnapshot<Int, Model>()
secondSnapshot.appendSections([0])
secondSnapshot.appendItems([model])
secondSnapshot.reloadItems([model])
dataSource.apply(secondSnapshot, animatingDifferences: true)
_ = tableView // Shows "A Model"
var thirdSnapshot = NSDiffableDataSourceSnapshot<Int, Model>()
thirdSnapshot.appendSections([0])
thirdSnapshot.appendItems([model])
thirdSnapshot.reloadItems([model])
dataSource.apply(thirdSnapshot, animatingDifferences: true)
_ = tableView // Shows "Another Name"