Based on this article I have created a reusable data source for UICollectionView as follows :-
final class CollectionViewDataSource<Model>: NSObject, UICollectionViewDataSource {
typealias CellConfigurator = (Model, UICollectionViewCell) -> Void
var models: [Model] = []
private let reuseIdentifier: String
private let cellConfigurator: CellConfigurator
init(reuseIdentifier: String, cellConfigurator: @escaping CellConfigurator) {
self.reuseIdentifier = reuseIdentifier
self.cellConfigurator = cellConfigurator
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return models.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let model = models[indexPath.item]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cellConfigurator(model, cell)
return cell
}
}
I have then extended this class so I can provide 'cell specific' setup based on the type of model
extension CollectionViewDataSource where Model == HomeFeedItem {
static func make(reuseIdentifier: String = "FEED_ARTICLE_CELL") -> CollectionViewDataSource {
return CollectionViewDataSource(reuseIdentifier: reuseIdentifier, cellConfigurator: { item, cell in
(cell as? FeedArticleCell)?.render(with: item)
})
}
}
extension CollectionViewDataSource where Model == HomeFeedAlertItem {
static func make(reuseIdentifier: String = "FEED_ALERT_CELL") -> CollectionViewDataSource {
return CollectionViewDataSource(reuseIdentifier: reuseIdentifier, cellConfigurator: { item, cell in
(cell as? FeedAlertCell)?.render(with: item)
})
}
}
This is working perfect, however, each of these cells has a different design but does in fact accept very similar properties (as do the other cells) - because of this I was thinking of creating a simple FeedItemModel and mapping these properties prior to rendering my feed. This would ensure anywhere I rendered a feed item, I was always dealing with the same properties.
With that in mind I tried to create something like :-
extension CollectionViewDataSource where Model == FeedItemModel {
static func make(reuseIdentifier: String = "FEED_ARTICLE_CELL") -> CollectionViewDataSource {
return CollectionViewDataSource(reuseIdentifier: reuseIdentifier, cellConfigurator: { item, cell in
switch item.type {
case .news: (cell as? FeedArticleCell)?.render(with: item)
case .alert: (cell as? FeedAlertCell)?.render(with: item)
}
})
}
}
This however falls down as the reuseIdentifier field is no longer correct if item.type is .alert.
How can I refactor this pattern to allow me to use different cells types with the same model? Or should I abandon this approach and stick to a different model for each cell type regardless of the input properties being the same?
You can create a protocol such as
Then ensure the
Modeltype conforms toFeedRenderable.You can then refactor your
CollectionViewDataSourcetoNotice the following changes
You can then ensure whatever model is passed in sets its
reuseIdentifierbased on theitem.typepropertyYour extension then becomes