ra1028 / DiffableDataSources

💾 A library for backporting UITableView/UICollectionViewDiffableDataSource.

Home Page:https://ra1028.github.io/DiffableDataSources

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

create a data source that vends multiple cell types?

xinsight opened this issue · comments

Checklist

  • Reviewed the README and documents.
  • Searched existing issues for ensure not duplicated.

Expected Behavior

I'd like to use DiffableDataSources with different cell types.

Current Behavior

I'm able to create a single data source and single cell type and apply the changes. I'm obviously missing something because I cannot figure out how to create a data source that vends multiple object types. (I tried creating multiple data sources, but then ended up with the dreaded UICollectionView exceptions about attempt to delete item 2 from section 0 which only contains 2 items before the update.)

Detailed Description (Include Screenshots)

Here is how I'm creating "article" cells. I'm not sure how to create another cell type.

enum Section: Int, CaseIterable {
    case trends = 0
    case articles

    var cellIdentifier: String {
        switch self {
        case .trends:
            return "trendCell"
        case .articles:
            return "articleCell"
        }
    }
}

lazy var dataSource = CollectionViewDiffableDataSource<Section, Article> (collectionView: collectionView) { collectionView, indexPath, _ /*identifier*/ in
    if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Section.articles.cellIdentifier, for: indexPath) as? ArticleCollectionViewCell {
        cell.article = self.articles[indexPath.item]
        return cell
    }

    fatalError()
}

var articles: [Article] = [] {
    didSet {

            let snapshot = DiffableDataSourceSnapshot<Section, Article>()
            snapshot.appendSections([.articles])
            snapshot.appendItems(articles, toSection: .articles)
            dataSource.apply(snapshot, animatingDifferences: true)
        }
    }
}

Environment

  • version: 0.1.0

  • Swift version: 5.0

  • iOS version: 12.2

  • Xcode version: 10.2.1

  • Devices/Simulators: iPhone X

  • CocoaPods/Carthage version: 0.33.0

I think i figured it out. :)

Creating the data source to produce a more generic AnyHashable allows any type of object to be created. So, you just need to cast the AnyHashable to your specific class. For example:

lazy var dataSource = CollectionViewDiffableDataSource<Section, AnyHashable> (collectionView: collectionView) { collectionView, indexPath, item in

    if let article = item as? Article, let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Section.articles.cellIdentifier, for: indexPath) as? ArticleCollectionViewCell {
        cell.article = article
        return cell
    }

    if let trend = item as? Trend, let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Section.trends.cellIdentifier, for: indexPath) as? TrendSummaryCell {
        cell.trend = trend
        return cell
    }

    fatalError()
}

I also found it useful to have a function to create the snapshot, and can then call it when my arrays are updated.

func buildSnapshot() -> DiffableDataSourceSnapshot<Section, AnyHashable> {
    let snapshot = DiffableDataSourceSnapshot<Section, AnyHashable>()
    snapshot.appendSections([.trends, .articles])
    snapshot.appendItems(trends, toSection: .trends)
    snapshot.appendItems(articles, toSection: .articles)
    return snapshot
}

var articles: [Article] = [] {
    didSet {
        let snapshot = buildSnapshot()
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

var trends: [Trend] = [] {
    didSet {
        let snapshot = buildSnapshot()
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

Hi @xinsight .

You can use enum for multiple type data source items.

e.g:

enum Item: Hashable {
    case article(Article)
    case trend(Trend)
}

func viewDidLoad()  {
    super.viewDidLoad()

    let snapshot = DiffableDataSourceSnapshot<Section, Item>()
    snapshot.appendSections([.trends, .articles])
    dataSource.apply(snapshot)
}

func appendArticles(_ articles: [Article]) {
     let snapshot = dataSource.snapshot()
     snapshot.appendItems(articles.map { .article($0) }, toSection: .articles)
     dataSource.apply(snapshot)
}

func appendTrends(_ trends: [Trend]) {
     let snapshot = dataSource.snapshot()
     snapshot.appendItems(trends.map { .trend($0) }, toSection: .trends)
     dataSource.apply(snapshot)
}