How to reload a single cell when using UITableViewDiffableDataSource

In my project I am using a UITableView with a UITableViewDiffableDataSource. I love how easy it is to get up and running with very little boiler plate code and all the free animations. It's a great API!

In my project I have implemented a trailing swipe action AND a context menu action. The action allows the user to mark the message read or unread similar to the native Mail app in iOS.

I had assumed that this could be accomplished using the following code:
Code Block Swift
enum Section: CaseIterable {
    case main
}
struct Item: Hashable {
    var isRead: Bool
    let identifier = UUID()
/* Hashable */
    static func == (lhs: Item, rhs: Item) -> Bool {
        return lhs.identifier == rhs.identifier
    }
    func hash(into hasher: inout Hasher) {
        hasher.combine(identifier)
    }
}
class ViewController: UITableViewController {
var dataSource: UITableViewDiffableDataSource<Section, Item>!
...
func markItemRead(_ item: Item) {
var snapshot = dataSource.snapshot()
var updatedItem = item
        updatedItem.isRead = true
        snapshot.reloadItems(updatedItem)
        self.dataSource.apply(snapshot)
}
}

I assumed the method reloadItems(_:) was intended to replace using reloadRows(at:with:) since that method is not available to you when you are using UITableViewDiffableDataSource.

So far I have run into this error, usually when I am using a struct for my model object with inferred Hashable conformance OR even sometimes with a class, but I can't see to put my finger on it.

Once I am past getting that error, I am noticing that the method doesn't seem to do what I want. The dataSource holds onto the old object with the old value for isRead. So when the cell is reloading it's view, it doesn't change at all, because it receives the old value for the isRead property. That lead me to this workaround:

Code Block Swift
func markItemRead(_ item: Item) {
    let oldSnapshot = dataSource.snapshot()
    let oldItems = oldSnapshot.itemIdentifiers(inSection: .main)
    let updatedItems: [Item] = oldItems.map {
        if $0 == item {
            var updatedItem = $0
            updatedItem.isRead = true
            return updatedItem
        }
        return $0
    }
    var snapshot = NSDiffableDataSourceSnapshot<Section, ConversationsController.Conversation>()
    snapshot.appendSections([.main])
    snapshot.appendItems(updatedItems, toSection: .main)
    dataSource.apply(snapshot)
    DispatchQueue.main.async {
        var snapshot = self.dataSource.snapshot()
        snapshot.reloadItems([item])
        self.dataSource.apply(snapshot)
        self.refreshBadge()
    }
}


The DispatchQueue.main.async was a last ditch effort to get it working and it finally did, but this definitely does not feel right.

If someone has been able to figure it out. I would appreciate some help in understanding what/how reloadItems(_:) is intended to work.

I have a feeling it all comes down to Hashable but I am not sure exactly what. Thanks!
I'm not sure if this will help you in this case, but I noticed that you're creating a new snapshot every time an item is read (apart from the DispatchQueue.main.async code block, which is probably why it's working there).

I created a separate update method in my code and it's updating a single section ok. In this "reload" method, try getting the existing snapshot:

Code Block swift
private func reloadSnapshot(inSection section: Int) {
var snapshot = dataSource.snapshot()
snapshot.reloadSections([section])
dataSource.apply(snapshot, animatingDifferences: true)
}



How to reload a single cell when using UITableViewDiffableDataSource
 
 
Q