Search code examples
iosswiftprotocolsswift-protocolsprotocol-oriented

Swift Protocol Oriented Extensions


Trying to get my head around Protocol oriented programming in Swift and how the extensions work and what level of extensibility it can provide.

Have the following code snippet which I ran through Playgrounds

protocol ProtocolA {
    func doSomethingA()
}

protocol ProtocolB {
    func doSomethingB()
}

protocol ProtocolC {
    func doSomethingC()
}

extension ProtocolA {
    func doSomethingA() {
        print("Extension - doSomethingA")
    }
}

extension ProtocolA where Self: ProtocolB {
    func doSomethingA() {
        print("Extension - doSomethingA Self: ProtocolB")
    }
}

extension ProtocolA where Self: ProtocolC {
    func doSomethingA() {
        print("Extension - doSomethingA Self: ProtocolC")
    }
}

extension ProtocolA where Self: ProtocolB, Self: ProtocolC {
    func doSomethingA() {
        print("Extension - doSomethingA Self: ProtocolB, ProtocolC")
    }
}

extension ProtocolB {
    func doSomethingB() {
        print("Extension - doSomethingB")
    }
}

extension ProtocolC {
    func doSomethingC() {
        print("Extension - doSomethingC")
    }
}

class Implementation: ProtocolA, ProtocolB, ProtocolC {
}

let obj = Implementation()

obj.doSomethingA()

What I get printed is:

Extension - doSomethingA Self: ProtocolB, ProtocolC

Is there anyway that I can guarantee all the extensions to run.

Ideally I'd like to get the following output.

Extension - doSomethingA
Extension - doSomethingA Self: ProtocolB
Extension - doSomethingA Self: ProtocolC
Extension - doSomethingA Self: ProtocolB, ProtocolC

I do understand that Swift, will choose the strongest match in terms of it's types, in fact if I don't provide an implementation where ProtocolA matches both ProtocolB and ProtocolC, I would get a compile time error. Is there anyway that I can go around this?

Thanks.


Solution

  • The point of extensions is to provide a default implementation for protocol methods.

    In your example the compiler will resolve to call the doSomethingA() method of the extension that best fits your concrete object, but that's it.

    As you can see in your point of call:

    obj.doSomethingA()

    You are performing one method invocation. This will be resolved and dispatched to one method call, not four.

    This is similar to when you override a method via subclassing and call a method of the base class, only the derived method is called and not the base class one. In that case, if you want you can call super.method() from the subclass.

    In your case, if you want multiple methods getting called, you can give them separate names and implementations via extensions, then just create a class that implements all the protocols like so:

    class A: ProtocolA, ProtocolB, ProtocolC {
    
       func all() {
          doSomethingA()
          doSomethingB()
          doSomethingC()
       }
    }
    

    The advantage of extensions here is that your class A doesn't have to provide implementations for the three methods.