NSDiffableDataSourceSnapshot doesn't call Hashable methods if ItemIdentifierType is class

I have a collection view with UICollectionViewDiffableDataSource, which has class PHAssetVisualAssetsPickerItem as it’s ItemIdentifierType. When I create instance of NSDiffableDataSourceSnapshot with the same items that dataSource already has, and then apply it, I see that collection view blinks even tho nothing changed. It actually blinks no matter what new snapshot is.

My investigation led to a fact that methods == (Equatable) and hasher(into:) (Hashable) are not called on items if items are classes. If I change them to structs, methods are called, dataSource understands what to do, nothing blinks.

https://www.icloud.com/iclouddrive/0vnB3auwp0ShEvPQmpVOAy6fg#ApplySnapshotIndexPaths

Here is the code where you can simply change HashableType to either class or struct and see that collectionView update looks different when you tap button.
Could you show the complete definition of the class ?
Sure! Here's the class that is used as ItemIdentifierType for UICollectionViewDiffableDataSource:
Code Block swift
class HashableType: Hashable {
    let string: String
    init(string: String) {
        self.string = string
    }
    func hash(into hasher: inout Hasher) {
        hasher.combine(string)
    }
    static func == (lhs: HashableType, rhs: HashableType) -> Bool {
        return lhs.string == rhs.string
    }
}

I do not see a good reason for it not to work.

How did you define the dataSource ?
May look at this tutorial if needed:
https :// www.raywenderlich. com/8241072-ios-tutorial-collection-view-and-diffable-data-source
Seems the same. Overall the implementation linked in my post is pretty simple and default, I haven't found where it could go wrong.
Same here! 🙋‍♂️

However, the collection view doesn't actually blink when updated in my case. Not sure why. Anyway, I only discovered it when trying to figure out why does a collection view creates way more cells then needed and makes most of them invisible. Turns out that each collection view reload creates a new set of cells because it thinks that data actually changed.

Same for me. Both methods are never called when the model is a class but it's mandatory to implement them.

There is a work around, in iOS 15 at least. You can wrap your class in a struct like this ->


struct ItemWrapper: Hashable {
    let item: Item

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

    static func == (lhs: ItemWrapper, rhs: ItemWrapper) -> Bool {
        lhs.item == rhs.item
    }
}

I just ran into this issue. If I try to use a class instead of a struct as a diffable data source object, the hashable methods are not called causing issues. I haven't found anything documented about this limitation. It would be nice to get an official feedback about this. As a temporary fix, logancautrell solution works perfectly, thank you for sharing that.

Here's the stack overflow explaining the problem and the answer: https://stackoverflow.com/questions/67324651/why-do-diffable-datasources-treat-class-and-struct-types-differently

Need to inherit from NSObject and override isEqual(_ object: Any?) -> Bool and var hash: Int

NSDiffableDataSourceSnapshot doesn't call Hashable methods if ItemIdentifierType is class
 
 
Q