Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift protocol conform: Candidate has non-matching type

Tags:

ios

swift

I was trying to define a protocol with some property of type AnyObject, then in the class that conforms to the protocol the property type is SomeClass. However this returned a compiler error. I had to change the type in the class to AnyObject. How can I use the super class in protocol definition and use the sub class as property type?

Thanks!

protocol TestProtocol {
    var prop: [AnyObject] {get}
}

class Test: TestProtocol {
    var prop = [SomeClass]()    //compiler error
    var prop = [AnyObject]()   //this will work
}
like image 827
Bonan Avatar asked Jan 22 '16 01:01

Bonan


3 Answers

The array is an unnecessary complication, so let's remove it and just think about a simple type. This is not legal:

protocol TestProtocol {
    var prop: AnyObject {get}
}

class SomeClass {}

class Test: TestProtocol {
    var prop : SomeClass = SomeClass() // error
}

The problem is that the protocol declaration says quite literally that whoever claims to adopt TestProtocol must have a property prop of type AnyObject — not of some type that merely conforms to AnyObject.

If you find this surprising, you may be confusing instances of types with types themselves. It is true that an instance of SomeClass can be used where an instance of AnyObject is expected. But types themselves do not work this way; you cannot substitute the type SomeClass when the protocol demands the type AnyObject.

To see this more clearly, note that this does compile just fine:

protocol TestProtocol {
    var prop: AnyObject {get}
}

class SomeClass {}

class Test: TestProtocol {
    var prop : AnyObject = SomeClass() // changing the declared _type_
}

That compiles, as you already discovered; but, as you have also said, it is not what you wanted. So how to do what you want?

Well, in Swift, the way you express the notion of specifying a type that conforms to a protocol type is through a generic with a constraint. That is what the answers you've been given do. A typealias in a protocol declaration can be a way of making a generic protocol. And a generic can have a constraint, saying e.g. that the type in question should conform to a protocol or inherit from a class. Thus, this is legal and is the sort of solution you're looking for:

protocol TestProtocol {
    typealias T:AnyObject // generic
    var prop: T {get}
}

class SomeClass {}

class Test: TestProtocol {
    var prop : SomeClass = SomeClass() // :)
}

The line typealias T:AnyObject says that T must be a type that conforms to AnyObject, which is precisely what you are trying to say.

like image 182
matt Avatar answered Sep 21 '22 04:09

matt


A playground example of what you can do:

class SomeClass {

}

class Subclass : SomeClass{

}

protocol TestProtocol {
  typealias T : SomeClass
  var prop: [T] {get}
}

class Test: TestProtocol {
  var prop = [Subclass]()

  func test(){
    prop.append(Subclass())
  }
}

let test = Test()
test.test() 
print(test.prop) // prints "[Subclass]\n"
like image 37
the_critic Avatar answered Sep 23 '22 04:09

the_critic


You can avoid pushing the knowledge of the array element class up to the protocol by using a type alias requirement:

protocol TestProtocol 
{
    typealias ArrayElement: AnyObject
    var prop: [ArrayElement] {get}
}

class Test: TestProtocol 
{
    typealias ArrayElement  = SomeClass
    var prop:[ArrayElement] = []    //No compiler error
}
like image 21
Alain T. Avatar answered Sep 23 '22 04:09

Alain T.