🔧

Firebase FirestoreでHashableに準拠する

2023/03/14に公開

まずFirebase Firestoreのオブジェクトについて
Hashableに準拠したいモチベーションは
UICollectionViewDiffableDataSource
を使いたいということです。

typealias DataSourceType = UICollectionViewDiffableDataSource<Int, Message>

モデルはこのような感じです。

struct Message: Codable, Hashable, Equatable {
    @DocumentID var id: String?
    let text: String?

    static func == (lhs: Message, rhs: Message) -> Bool {
        if let lhsId = lhs.id, let rhsId = rhs.id {
            return lhsId == rhsId
        } else {
            return false
        }
    }
}

そうしたら、iOS審査でクラッシュするという件で落ちてしまいました。
エラーは次のとおりです。

Fatal Exception: NSInternalInconsistencyException
Diffable data source detected item identifiers that are equal but have different hash values. Two identifiers which compare as equal must return the same hash value. You must fix this in the Hashable (Swift) or hash property (Objective-C) implementation for the type of these identifiers. Identifiers that are equal but have different hash values: ...

これはEquatableを使ってuniqを行っていました。

extension Array where Element: Equatable {
    var unique: [Element] {
        var uniqueValues: [Element] = []
        forEach { item in
            guard !uniqueValues.contains(item) else { return }
            uniqueValues.append(item)
        }
        return uniqueValues
    }
}

さて、調べたところによると...

https://stackoverflow.com/questions/57309942/diffable-data-source-for-tableview-error-on-ios-13-inconsistent-associations-fo

というわけで、HashableとEquatableが競合しているというか
何というかそんな感じですかね...

struct Message: Codable, Hashable, Equatable {
    @DocumentID var id: String?
    let text: String?
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }

    static func == (lhs: Message, rhs: Message) -> Bool {
        if let lhsId = lhs.id, let rhsId = rhs.id {
            return lhsId == rhsId
        } else {
            return false
        }
    }
}

今回は、このようにhash intoを実装すること対応してみました。

Discussion