简体   繁体   中英

Constrain protocol's associated type itself

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 .

Swift 4 Update

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
}

Swift 3

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.

您可以约束T.Cell.T等于T

class CollectionViewDataSource<T: CellPresentable>: NSObject, UICollectionViewDataSource where T.Cell.T == T

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM