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)
}