見出し画像

UICollectionViewDiffableDataSourceで異なるデータを表示する

 この記事はQiitaで開催中のiOS #2 Advent Calendar 2020の19日目の記事です。

 少し前にリリースされたnoteのiOSアプリ4.3.0でみつけるタブという機能がリリースされました。

 この機能には記事ユーザーなど型の異なるデータが一つのUICollectionViewに表示されています。レイアウトにはUICollectionViewCompositionalLayoutを利用し、データの表現にはUICollectionViewDiffableDataSourceを利用しています。UICollectionViewDiffableDataSourceを利用するにはHashableに適合したデータを利用する必要があるのですが、既存のモデルを後からHashableに適合するとうまく動作しないことがありました。また、異なるセクションで同一のデータを表示させようとすると一意性が崩れてしまいクラッシュすることがありました。この記事ではこれらの問題をどう対処したかを書いていきます。

値の表現

UICollectionViewDiffableDataSourceにはHashableに適合した値を利用する必要があります。下記のようなイメージです。

struct Item: Hashable {
    let name: String
}

ただ、これだと異なる型を表現できないのでnoteのみつけるタブでは下記のようにenumを利用するようにしました。

enum Item: Hashable {
    case note(Note)
    case user(User)
}

enumのAssociated ValuesがHashableに対応していない場合

 例えば上記でいうUserというクラスがHashableに適合していないクラスだったとします。UserはIDや名前以外にも様々なプロパティを持っているので安直にextension User: Hashable {}などとするとビルドが通らなくなることがあります。今回のケースだと一意な値のハッシュ値がわかればいいので下記のようにIDのみのハッシュで逃すことが可能です。

extension User: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

値が一意にならない場合の対処

 複数の同じアイテムを表示したい場合には一意性が崩れてしまいアプリがクラッシュしてしまいます。今回のみつけるタブでいえば、異なるセクションで同じ.note(Note)が表示されてしまうという可能性があります。そういった場合にはそれぞれにSectionを持たせることでセクション跨ぎの重複を回避することが可能です。

enum Item: Hashable {
    case note(Note, Section)
    case user(User, Section)
}

まとめ

 iOS 13から利用可能になったUICollectionViewDiffableDataSourceは簡単に差分更新が可能で便利な機能ですが、データの重複が発生してしまうとクラッシュするなどこれまでとは異なる点で気をつける必要が出てきました。特に今回のようにデータの重複があり得るような場合には注意が必要です。新しい便利な機能の利便性を享受しつつ、これまでには無かったハマりポイントには気をつけて開発をしていきましょう!

この記事が気に入ったらサポートをしてみませんか?