Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 4 protocol composition conformance [duplicate]

Tags:

swift

swift4

[First of all if someone has a better name for the question, proposal are well accepted. I didn't find a better name for this question so far.]

So, that's the question.

Assuming I have a protocol ProtocolA, a protocol ProtocolB and a protocol ProtocolX defined as following:

protocol ProtocolA {
  func doSomethingA()
}

protocol ProtocolB {
   func doSomethingB()
}

protocol ProtocolX {
   var handler: ProtocolA { get }
}

Then I have a proper implementation in a class of my ProtocolX as following:

class Donald: ProtocolX {
   ...
   var handler: ProtocolA { ... }
   ...
}

Then everything is ok and the protocol requirement is correctly fulfilled.

BUT

If I implement like that:

class Donald: ProtocolX {
   ...
   var handler: ProtocolA & ProtocolB
   ...
}

I have a compile-time issue reporting that my class Donald does not conform to ProtocolX (that, by specification, requires that the var handler must be conform to ProtocolA).

Theoretically the var handler IS conform to ProtocolA (but it's also conform to ProtocolB).

Then my question is: Why the compile-time issue? Swift limitation or conceptual issue on my side?

like image 500
GrizzlyBear Avatar asked Oct 26 '17 13:10

GrizzlyBear


1 Answers

Theres no good reason why this shouldn't work, it just isn't supported by the compiler yet.

Currently, ProtocolX specifies that the handler must be of type ProtocolA, meaning that if you declare your class Donald with anything other than a ProtocolA type for the handler it will not fulfil the protocol's requirements.

You can however specify the handler as type ProtocolA but set it to a property of type ProtocolA & ProtocolB. In which case the property will be cast to ProtocolA and you will need to do an additional cast in order to use ProtocolB's attributes. For example:

typealias ProtocolAB = ProtocolA & ProtocolB

class AB: ProtocolAB {
    func doSomethingA() {}
    func doSomethingB() {}
}

class Donald: ProtocolX {
    var handler: ProtocolA {
        return AB()
    }

    func f() {
        handler.doSomethingA() // is valid
        handler.doSomethingB() // is not valid without casting the handler as `ProtocolB`
    }
}
like image 199
user1636130 Avatar answered Oct 23 '22 23:10

user1636130