2

I'm using a UITableViewDiffableDataSource with my UITableView and it works as expected. However I'm having trouble updating an item in the snapshot. I have a simple sample where;

enum Section {
    case one
}

struct Item: Hashable {
    let identifier: Int
    let text: String

    public func hash(into hasher: inout Hasher) {
        hasher.combine(identifier)
    }

    public static func == (lhs: Item, rhs: Item) -> Bool {
        return lhs.identifier == rhs.identifier
    }
}

with the initial snapshot created using

        var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
        snapshot.appendSections([.one])
        snapshot.appendItems([
            Item(identifier: 1, text: "One"),
            Item(identifier: 2, text: "Two"),
            Item(identifier: 3, text: "Three")
        ])

        dataSource.apply(snapshot)

If I were to attempt updating an item using

        var snapshot = dataSource.snapshot()

        snapshot.reloadItems([
            Item(identifier: 2, text: "Foo")
        ])

        dataSource.apply(snapshot)

nothing happens. My expected result is for the text of item with identifier = 2 to be updated to "Foo". Note I have not included the table view cell provider code here.

I have read similar posts such as How to update a table cell using diffable UITableView and Can UITableViewDiffableDataSource detect an item changed? but the answer isn't clearly obvious.

Chamitha
  • 215
  • 3
  • 11
  • Is the cell provider being called? Try setting a breakpoint and see if it triggers after the reload, and check what the value of the item is there. – EmilioPelaez Jul 13 '20 at 06:50
  • @EmilioPelaez Yes it is and the values for item are the "old" values. – Chamitha Jul 13 '20 at 06:55
  • Looking at my own code, I'm configuring a full snapshot (like in your second block of code) every time something is updated. I call both `appendItems` and `reloadItems` with a list of all the items and it's been working well. I don't think it's the way it's supposed to be, but if you don't find a better answer you might want to try it. – EmilioPelaez Jul 13 '20 at 07:03
  • @EmilioPelaez Interesting, that does work. However it seems rather cumbersome to also re-append all the updated items too before reloading. Thanks for the possible workaround though. – Chamitha Jul 13 '20 at 07:21

1 Answers1

3

You just need to add text property to the Equatable protocol function if you want a custom == :

public static func == (lhs: Item, rhs: Item) -> Bool {
    return lhs.identifier == rhs.identifier && lhs.text == rhs.text
}

After that, data source can figure out the difference and will update item correctly.

Or you can remove it all and leave the default implementation in structure.

There's also another way to update data. Append updated array to a new snapshot and apply it to the data source.

    var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
    snapshot.appendSections([.one])
    snapshot.appendItems([
        Item(identifier: 1, text: "One"),
        Item(identifier: 2, text: "Foo"), // Updated item
        Item(identifier: 3, text: "Three")
    ])

    dataSource.apply(snapshot)

Data source will find difference itself and reload item in the table view.

If you interested in, there're more details here