Why UICollectionViewDiffableDataSource compares items by reference

I have these items
Code Block swift
class Month: Hashable {
let name: String
var isSelected: Bool
}


I'm displaying these months in a collectionview and when tapping on one I set it's isSelected property to true and the cell should change the label's colour based on isSelected.

This is how I create a new snapshot:

Code Block swift
func makeSnapshot() -> NSDiffableDataSourceSnapshot<Section, Month> {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Month>()
        snapshot.appendSections([Section.main])
        snapshot.appendItems(months, toSection: .main)
        return snapshot
    }


When the user taps on a cell, this happens:
Code Block swift
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let month = dataSource.itemIdentifier(for: indexPath)
    months.forEach({ $0.isSelected = false })
    month?.isSelected = true
dataSource.apply(makeSnapshot())
}


The UI doesn't change, unless I scroll up and down (dequeue the cell again) or I call the reloadItems function.
I explicitly tell the system that two months are not the same if one is selected and the other is not (and their name is the same), I expected that the UICollectionViewDiffableDataSource will use the hash and == functions to check which cells needs to be reloaded, however this clearly is not the case.
Although, when I replace the instance itself, it will reload the cell (same happens if I mutate structs instead of classes).

The hash/== functions:
Code Block swift
extension Month {
func hash(into hasher: inout Hasher) {
    hasher.combine(name)
    hasher.combine(isSelected)
}
static func == (lhs: DemoViewController.Month, rhs: DemoViewController.Month) -> Bool {
    lhs.name == rhs.name && lhs.isSelected == rhs.isSelected
        }
}



This makes me think that the system compares the memory addresses and not the hash/equality of the items. Can you please confirm if this is the case.
Since I provide all the information needed to work out the difference between a new and an old snapshot I expected that the UICollectionViewDiffableDataSource is smarter and encapsulates this logic and I don't have to write the additions/deletions/reorders/replacements logic every single time I use this API.

Thanks a lot,
Bence

Which is even more weird is that it actually calls in to this block with the right data, I and the label colour is set to red, but the UI doesn't update 🤷‍♂️ could you explain please?

Code Block swift
dataSource = UICollectionViewDiffableDataSource<Section, Month>(collectionView: collectionView) { (collectionView: UICollectionView, indexPath: IndexPath, item: Month) -> UICollectionViewCell? in
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "monthcell", for: indexPath) as! MonthCollectionViewCell
            cell.label.text = item.name
            cell.label.textColor = item.isSelected ? .systemRed : .systemBlue
            return cell
}


You didn't declare your class Equatable. You should try to add this protocol.

Code Block
class Month: Hashable, Equatable


Otherwise, AFAIU, you use the standard == to compare instances, hence the references.
Hashable inherits from Equatable, so no need
Good point.

But look at this, where it explains how protocol are or are not overload.

https://stackoverflow.com/questions/42283715/overload-for-custom-class-is-not-always-called

So it seems, in your case, the == operator used is not the one you overloaded.
Unless you also implement isEqual() …
It is really a bit tricky.
That's not the case here as I neither subclass NSObject nor overload the optional == operator.
proof:

Code Block swift
class Month: Hashable {
let name: String
    internal init(name: String) {
        self.name = name
    }
    static func == (lhs: Month, rhs: Month) -> Bool {
        print("Month overloaded")
        return false
    }
    func hash(into hasher: inout Hasher) {
        hasher.combine(name)
    }
}
let a: AnyHashable = Month(name: "ab")
let b: AnyHashable = Month(name: "ab")
a == b // prints: `Month overloaded`


nor overload the optional == operator. 

Yes, so it applies the == for instances (compares references).

What I said is that even when you overload, that may still be the case.
So I don't understand your last point.
Why UICollectionViewDiffableDataSource compares items by reference
 
 
Q