Swift Protocol Extensions overriding Swift Protocol Extensions overriding xcode xcode

Swift Protocol Extensions overriding


The short answer is that protocol extensions don't do class polymorphism. This makes a certain sense, because a protocol can be adopted by a struct or enum, and because we wouldn't want the mere adoption of a protocol to introduce dynamic dispatch where it isn't necessary.

Thus, in getColor(), the color instance variable (which may be more accurately written as self.color) doesn't mean what you think it does, because you are thinking class-polymorphically and the protocol is not. So this works:

let colorB = B().color // is "Red color" - OK

...because you are asking a class to resolve color, but this doesn't do what you expect:

let b = B().getColor() // is "Default color" BUT I want it to be "Red color"

...because the getColor method is defined entirely in a protocol extension. You can fix the problem by redefining getColor in B:

class B: A, RedColor {    func getColor() -> String {        return self.color    }}

Now the class's getColor is called, and it has a polymorphic idea of what self is.


There are two very different issues at play here: The dynamic behavior of protocols and the resolution of protocol "default" implementations.

  1. On the dynamic front, we can illustrate the problem with a simple example:

    protocol Color { }extension Color {    var color: String { return "Default color" }}class BlueBerry: Color {    var color: String { return "Blue color" }}let berry = BlueBerry()print("\(berry.color)")                 // prints "Blue color", as expectedlet colorfulThing: Color = BlueBerry()print("\(colorfulThing.color)")         // prints "Default color"!

    As you point out in your answer, you can get the dynamic behavior if you define color as part of the original Color protocol (i.e. thereby instructing the compiler to reasonably expect the conforming classes to implement this method and only use the protocol's implementation if none is found):

    protocol Color {    var color: String { get }}...let colorfulThing: Color = BlueBerry()print("\(colorfulThing.color)")         // now prints "Blue color", as expected
  2. Now, in your answer, you question why this falls apart a bit when B is a subclass of A.

    I think it helps to remember that the method implementations in protocol extensions are "default" implementations, i.e. implementations to be used if the conforming class doesn't implement it, itself. The source of the confusion in your case comes from the fact that B conforms to RedColor which has a default implementation for color, but B is also a subclass of A which conforms to Color, which has a different default implementation of color.

    So, we might quibble about Swift's handling of this situation (personally I'd rather see a warning about this inherently ambiguous situation), but the root of the problem, in my mind, is that there are two different hierarchies (the OOP object hierarchy of subclasses and the POP protocol hierarchy of protocol inheritance) and this results in two competing "default" implementations.

I know this is an old question, so you've probably long since moved on to other things, which is fine. But if you're still struggling regarding the right way to refactor this code, share a little about what this class hierarchy and what this protocol inheritance actually represent and we might be able to offer more concrete counsel. This is one of those cases where abstract examples just further confuse the issue. Let's see what the types/protocols really are. (If you've got working code, http://codereview.stackexchange.com might be the better venue.)


I managed to get it working by defining color on Color and switching the implementation list for B. Not much good if B must be an A though.

protocol Color {    var color : String { get }}protocol RedColor: Color {}extension Color {    var color : String {        get {return "Default color"}    }}extension RedColor {    var color : String {        get {return "Red color"}    }}protocol PrintColor {    func getColor() -> String}extension PrintColor where Self: Color {    func getColor() -> String {        return color    }}class A : Color, PrintColor {}class B : RedColor, PrintColor {}let a = A().getColor() // "Default color"let b = B().getColor() // "Red color"