Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type does not conform to protocol

I'm still having trouble understanding some subtleties of generics in Swift. I define the following types:

protocol SomeProtocol {
    func setValue(value: Int)
}

class ProtocolLabel : UILabel, SomeProtocol {
    func setValue(value: Int) {

    }
}

class ProtocolImageView : UIImageView, SomeProtocol {
    func setValue(value: Int) {
    }
}

viewForValue(2) Now I defined the following function. I expect T to be a UIView that conforms to protocol SomeProtocol.

func viewForValue<T where T: SomeProtocol, T: UIView>(param: Int) -> UIView {
    var someView: T
    if param > 0 {
        someView = ProtocolLabel() as T
    } else {
        someView = ProtocolImageView() as T
    }
    someView.setValue(2)
    someView.frame = CGRectZero
    return someView
}

However, I'm getting the following compile error when I execute the code:

viewForValue(2) // <-- Type 'UIView' does not conform to protocol 'SomeProtocol'

It seems that in the where clause I can't specify a class that does not implement the protocol. Why is that?

Thanks in advance.

like image 811
Kamchatka Avatar asked Sep 15 '14 10:09

Kamchatka


People also ask

Can a protocol conform to a protocol?

The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.

Is protocol a type in Swift?

Protocols as TypesProtocol is a type. You can use it in many places like: As a parameter type or return type in a function, method, or initializer. As the type of a constant, variable, or property.


1 Answers

viewForValue is supposed to return a class which inherits from UIView and implements SomeProtocol. You have defined 2 classes having no direct relationship - they just inherit from UIView and implement SomeProtocol.

When the function has to determine the return type, the direct concrete type both classes inherit from is UIView, so that is what viewForValue returns.

In order to fix the problem, you have to create a direct and concrete relationship between the 2 classes, by creating a 3rd class inheriting from UIView and implementing SomeProtocol:

protocol SomeProtocol {
    func setValue(value: Int)
}

class SomeClass: UIView, SomeProtocol {
    func setValue(value: Int) {

    }
}

class SomeSubclass : SomeClass {
}

class SomeOtherSubclass : SomeClass {
}

func viewForValue<T where T: SomeProtocol, T: SomeClass>(param: Int) -> T {
    var someView: T
    if param > 0 {
        someView = SomeSubclass() as T
    } else {
        someView = SomeOtherSubclass() as T
    }
    someView.setValue(2)
    someView.frame = CGRectZero
    return someView
}

viewForValue(2)

Addendum: reading the OP comment below, the purpose is to dynamically instantiate existing UIKit classes inheriting from UIView. So the proposed solution doesn't apply.

I think that extending UIView by implementing SomeProtocol should work:

protocol SomeProtocol {
    func setValue(value: Int)
}

extension UIView : SomeProtocol {
    func setValue(value: Int) {
    }
}

func viewForValue<T where T: SomeProtocol, T: UIView>(param: Int) -> UIView {
    var someView: T
    if param > 0 {
        someView = UILabel() as T
    } else {
        someView = UIImageView() as T
    }
    someView.setValue(2)
    someView.frame = CGRectZero
    return someView
}

but it looks like there's a compiler bug. This code in a playground shows a message stating that:

Communication with the playground service was interrupted unexpectedly. The playground service "com.apple.dt.Xcode.Playground" may have generated a crash log.

whereas in an iOS application compilation fails due to a segmentation fault 11.

like image 133
Antonio Avatar answered Sep 28 '22 21:09

Antonio