user509981
user509981

Reputation:

Protocol requirement cannot be satisfied

I would like to create a protocol for view on watchOS, iOS and TvOS, so I can get their subviews and superviews in a generic way.

At first I tried this :

protocol ViewProtocol: Hashable {
    var superview: Self? { get }
    var subviews: [Self] { get }
}

And then I extend the UIView class like this :

extension UIView: ViewProtocol {}

But I get this error from the compiler:

<unknown>:0: error: protocol 'ViewProtocol' requirement 'superview' cannot be satisfied by a non-final class ('UIView') because it uses 'Self' in a non-parameter, non-result type position

I'm not sur to understand the problem (I think it's related to the compiler not being able to use Self in a non final class), so I tried the following :

The protocol would look like this :

protocol ViewProtocol: Hashable {
    func getSuperview() -> ViewProtocol?
    func getSubviews() -> [ViewProtocol]
}

But now I get this error at the protocol declaration :

Protocol 'ViewProtocol' can only be used as a generic constraint because it has Self or associated type requirements

So I tried this :

protocol ViewProtocol: Hashable {
    func getSuperview<T: ViewProtocol>() -> T?
    func getSubviews<T: ViewProtocol>() -> [T]
}

And the implementation look like this:

extension UIView: ViewProtocol {
    func getSuperview<T>() -> T? where T : ViewProtocol {
        return self.superview as! T?
    }

    func getSubviews<T>() -> [T] where T : ViewProtocol {
        return self.subviews as! [T]
    }
}

But now when I try to use the method on generic type ViewProtocol I get this error : Generic parameter 'T' could not be inferred

Can someone help me ? I would like to fundamentally understand what's going on here and why it is so difficult to make that work ?

Upvotes: 1

Views: 1283

Answers (1)

Alexander
Alexander

Reputation: 63272

You can't have a Self requirement in non-final classes, and here's an example to illustrate why that wouldn't make sense:

protocol Copyable {
    var copyOfSelf: Self { get }
}

final class Car {
    let name: String
    init(name: String) { self.name = name }
}

extension Car: Copyable {
    var copyOfSelf: Car { return Car(name: self.name) }
}

class SportsCar: Car {
    // Inherited:
    // var copyOfSelf: Car { return Car(name: self.name) }
    // Notice that it still returns `Car`, not `SportsCar`,
    // Breaking the conformance to `Copyable`
}

Upvotes: 1

Related Questions