Swift protocol extension method dispatch

Protocol extension is one of the coolest features introduced in swift 2.0. They enable new ways to share implementation details between different types, wether they are structs, enums or classes.

Alexandros Salazar has a very nice write up about one tricky cause of issues that could face swift developers in the future. (please read his great article for more details)

In this article, he expects the following question to be asked:

“Why does the method that I wrote overriding protocol extension X never get called?”

As part of the linked article, he tries to explain the nitty gritty details about how this question is important, and how to reason about method dispatching when using protocol extensions.

The answer to the question can be summarised as following:

IF the inferred type of a variable is the protocol:
— AND the method is defined in the original protocol — — THEN the runtime type’s implementation is called, irrespective of whether there is a default implementation in the extension.
— AND the method is not defined in the original protocol, — — THEN the default implementation is called.
ELSE IF the inferred type of the variable is the type — THEN the type’s implementation is called.

If you are like me and you prefer reading decisions from a flow chart, the above reasoning can summarised in the following flow chart:


Lets get consider this example:

protocol TheProtocol {
func method1()
}

I then create Struct1 that conforms to this protocol

struct Struct1: TheProtocol {
func method1() {
print(“Called method1 from struct 1”)
}

func method2() {
print(“Called method2 from struct 1”)
}
}

Struct1 implements method1 that is required in TheProtocol and also defines method2, which is not defined in TheProtocol.

Lastly lets define a protocol extension that also defines method2.

extension TheProtocol {
 func method1() {
print(“Called method1 from TheProtocol”)
}
  func method2() {
print(“Called method2 from TheProtocol”)
}
}

First, lets call method1 and method2 with a variable that has the inferred type of Struct1.

let s1 = Struct1()
s1.method1()
s1.method2()

Since the inferred type of s1 is Struct1, as for flow diagram, the above will execute method1 and method2 from Struct1:

Called method1 from struct 1
Called method2 from struct 1

For the next experiment, we will annotate the variable type with TheProtocol type:

let s1: TheProtocol = Struct1()
s1.method1()
s1.method2()

Again, as per the flow diagram, since method1 is a requirement of TheProtocol the dynamic version of the method will be called. This means s1.method1 will execute method1 from Struct1.

On the other hand since method2 is not a requirement of TheProtocol, and since s1 static type is TheProtocol, then s1.method1 will execute TheProtocol’s version of method1. The following will be the output.

Called method1 from struct 1
Called method2 from TheProtocol

As a final take away, consider keeping this flow diagram in handy.

Happy swifting~~