Reputation: 5673
I'm trying to create generic CollectionView datasource. I have two protocols, first - some abstract cell, the second indicates that conforming class could be presented by some abstract cell and should contain only associated type referencing that cell. Their implementation could look like this:
protocol EntityPresentingCell {
// entity that should be presented in this cell
associatedtype T
static var CellReuseID: String { get }
// takes object and fill UI with data
func populate(with object: T)
}
protocol CellPresentable {
// cell that should present this entity
// I need to constrain it
associatedtype Cell: EntityPresentingCell // where Cell.T == Self
}
class CollectionViewDataSource<T: CellPresentable>: NSObject, UICollectionViewDataSource {
var items: [T]
init(items: [T]) {
self.items = items
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: T.Cell.CellReuseID, for: indexPath)
// compiler error here since, obviously, T.Cell.T not constrained to T itself
(cell as! T.Cell).populate(with: items[indexPath.item])
return cell
}
}
At the point of usage it could look like this:
class SomeEntity: CellPresentable {
typealias Cell = SomeEntityCell
var someValue = "someValue"
}
class SomeEntityCell: EntityPresentingCell {
static var CellReuseID: String = "SomeID"
@IBOutlet weak var label: UILabel!
func populate(with object: SomeEntity) {
label.text = object.someValue
}
}
The problem with this code is that I can not constrain (and thus be sure at compile time that) CellPresentable.Cell.T to be equal to CellPresentable itself (like in example). It is clearly seen with compiler error.
The goal is to make pure compile-time all-sufficient code that could prove that item is presentable by given cell (again, at compile time) and I don't want to force downcasts, or any other run-time checks.
Is it possible? If so, how?
UPD: David Rodrigues answer works, but it means that mismatch (T.Cell.T != T)
will be revealed only when I'm about to create CollectionViewDataSource
. I want it to happen exactly when I define my entity's conformance to EntityPresentingCell
protocol. In other words, compiler should complain when I write something like
class SomeEntity: CellPresentable {
typealias Cell = SomeWrongEntityCell
var someValue = "someValue"
}
but not when I create CollectionViewDataSource
instance. It's entity responsibility to ensure cell type, not the creator of CollectionViewDataSource
.
Upvotes: 0
Views: 170
Reputation: 80781
You can now add the where Cell.T == Self
constraint to the associated type, so you can indeed now just say:
protocol CellPresentable {
associatedtype Cell : EntityPresentingCell where Cell.T == Self
}
Currently, it's not possible to add any further constraints to associated types besides what they are required to conform to.
However, now that SE-0142: Permit where clauses to constrain associated types has been accepted, being able to add where
clauses to associated types will be possible in a future version of Swift.
You should just be able to say:
protocol CellPresentable {
associatedtype Cell : EntityPresentingCell where Cell.T == Self
}
Although until this is implemented, David's solution of adding the generic constraint where T.Cell.T == T
is probably about as good as you're going to get.
Upvotes: 3
Reputation: 842
You can constrain T.Cell.T
to be equal to T
.
class CollectionViewDataSource<T: CellPresentable>: NSObject, UICollectionViewDataSource where T.Cell.T == T
Upvotes: 1