92

I'm wondering if it's possible to achieve such a thing.
I have a Playground like this:

protocol Foo {
    func testPrint()
}

extension Foo {
    func testPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        // Calling self or super go call default implementation
        self.testPrint()
        print("Call from struct")
    }
}


let sth = Bar()
sth.testPrint()

I can provide a default implementation in extension but what if Bar needs everything that is in default implementation plus additional things?
It's somehow similar to calling super. methods in classes to fulfill requirement of implementing every property etc. but I see no possibility to achieve the same with structs.

cojoj
  • 6,405
  • 4
  • 30
  • 52
  • I would use `Foo.testPrint(self)()` - the problem is that it fails due to a segmentation fault (tested on both 7.0 GM and 7.1 beta) – Antonio Sep 16 '15 at 09:37
  • 1
    That's a strange construction you've presented – cojoj Sep 16 '15 at 09:42
  • 4
    Every instance method is a static curried method taking an instance as its first parameter – Antonio Sep 16 '15 at 09:43
  • However I tried removing the extension, and it throws the same segmentation error. Probably that is not supposed to work with protocols – Antonio Sep 16 '15 at 09:44
  • Hmmm, shame that I've to repeat myself in code while this could be easily fixed by using default implementation... – cojoj Sep 16 '15 at 09:49
  • The selected and top voted answer here, as pointed out by the top voted comment on it, will **drastically changes the semantics** of what is going on. Unless you understand **exactly** what is happening and this really is your intention, it will almost certainly lead to insidious, unexpected, and potentially nasty bugs. [Another answer here](https://stackoverflow.com/a/52876778/2547229) is a great solution to the problem. It provides a default protocol function implementation and also allows non-default implementations to call it. Perhaps the OP could consider changing the selected answer? – Benjohn Jul 26 '23 at 07:40

6 Answers6

108

I don't know if you are still looking for an answer to this, but the way to do it is to remove the function from the protocol definition, cast your object to Foo and then call the method on it:

protocol Foo { 
    // func testPrint() <- comment this out or remove it
}

extension Foo {
    func testPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        print("Call from struct")
        (self as Foo).testPrint() // <- cast to Foo and you'll get the  default
                                  //    function defined in the extension
    }
}

Bar().testPrint()

// Output:    "Call from struct"
//            "Protocol extension call"

For some reason it only works if the function isn't declared as part of the protocol, but is defined in an extension to the protocol. Go figure. But it does work.

Aaron Rasmussen
  • 13,082
  • 3
  • 42
  • 43
  • 1
    Oh god! I can't believe they force you to take the func out of the protocol! good answer, thank you! – SkyWalker May 30 '16 at 08:24
  • 6
    This looks like a bug for me, this should not be possible to get different method implementation by just casting the same instance. – MANIAK_dobrii Aug 18 '16 at 23:07
  • @MANIAK_dobrii Why should it not be possible? – Aaron Rasmussen Aug 19 '16 at 04:56
  • @RomanSausarnes because casting does not modify casted object (not sure about objc interoperability, see Apple's Swift book in "Downcasting"). In your example casting `self` to `Foo` made `self` effectively change it's type, because for the same type instance you got two different method implementations. – MANIAK_dobrii Aug 19 '16 at 09:29
  • 1
    @MANIAK_dobrii I disagree that casting `self` to `Foo` made `self` change its type. It is similar to casting an object from a subclass to a superclass and then calling one of the superclass' methods. The actual type of the object remains the same, but a subclass has access to the methods of its superclass through the `as` operator, even if the method was overriden. An object that conforms to a protocol has access to the protocol's extension method implementations in the same way, even if the method was overriden. – Aaron Rasmussen Aug 19 '16 at 18:11
  • 2
    @RomanSausarnes I'm not sure why do you disagree. You can have access to the super's implementation from **inside** of the subclass - that's ok. If you downcast subclass instance to superclass and call overriden method you still get subclass implementation. But, you can call `(self as Foo).testPrint()` outside of `Bar` implementation, right? So you can have access to the superclass (in that case default protocol extension imp) implementation from the **outside** of the subclass implementation, that's what I find wrong. In that case `Bar` demonstrates `Foo`'s behavior, that's wrong. – MANIAK_dobrii Aug 19 '16 at 20:00
  • 22
    This actually drastically changes the semantics of the protocol+extension. If you leave the declaration out of the protocol, you will get _static_ dispatch when calling the function on a type that conforms to the protocol - this is why you can cast and get the implementation from the extension. If you add the declaration to the protocol, your call to the function will be _dynamically_ dispatched. – Thorsten Karrer Aug 24 '16 at 14:03
  • 2
    This works though only if `Foo` protocol doesn't inherit from any other protocol. – iyuna Feb 20 '17 at 14:43
  • I'm having troubles making this work in Swift 4. When casting to the protocol type without removing the method from the protocol (`self as Foo`), things break at runtime. When casting to the protocol type while keeping the method in the protocol definition (`func testPrint()`), the class' version of the protocol does not even get called. So, not sure if things still work as described in this answer, but I might do things the wrong way. – Romain Sep 01 '17 at 16:25
  • 8
    Doesn't this lead to an infinite loop? – stan liu Feb 23 '18 at 06:46
  • "Go figure" .. Exactly – andrewz Apr 09 '18 at 18:37
  • This cast (which is a fine way to declare intent IMO) will NOT work in case your protocol has associated types. In this case you are forced to repeat the default implementation in the conforming type (struct). – David James Jul 27 '18 at 06:59
  • yet another endless loop – Anton Tropashko Jun 04 '19 at 15:17
  • @MANIAK_dobrii You have to understand how type checking works. See [Swift Protocol Compile Time Check](https://mfaani.com/posts/swift-protocol-compile-time-check/). The post is on compile time check, but it's the same principle here... – mfaani Oct 28 '22 at 17:02
  • @mfaani the reason the approach from the answer works is because in Swift protocol members use static dispatch. What is described in the article is about the scope and the differences in interfaces (imagine Person protocol would have an extension property age, which getter would get called, Person extension or Adults?). It's been 6 years, and in 2016 Swift was still way closer to Objective-C, where every method is dynamically dispatched, so the expectations of Swift method dispatch in 2016 were aligned accordingly. I'd still never rely on a behavior like that, very confusing and error-prone – MANIAK_dobrii Oct 28 '22 at 19:37
  • For an example of when this answer's **static dispatch** semantics break, suppose I am given a `let foo: Foo = Bar()`. And I `foo.testPrint()`. Only the `Foo` implementation is called. The `Bar` implementation will not be called. In anything beyond a toy example this is almost certainly not the intention as the implementation's behaviour is entirely sliced away and only the default implementation is left. [An alternative answer here](https://stackoverflow.com/a/52876778/2547229) provides **dynamic dispatch** semantics. – Benjohn Jul 26 '23 at 07:55
10

Well, you could create a nested type conforming to the protocol, instantiate it, and call the method on that one (it does not matter that you cannot access your type's data as the implementation inside the protocol extension cannot reference it anyway). But it's not a solution I'd call elegant.

struct Bar: Foo {
    func testPrint() {
        // Calling default implementation
        struct Dummy : Foo {}
        let dummy = Dummy()
        dummy.testPrint()
        print("Call from struct")
    }
}
Thorsten Karrer
  • 1,345
  • 9
  • 19
  • 1
    It looks like it's the only possibility right now (confirmed by Apple)... I'll file a feature radar for this one as it may be useful – cojoj Sep 16 '15 at 14:54
7

what do you think about such way of fixing this ?

protocol Foo {
    func testPrint()
}

extension Foo {
    func testPrint() {
        defaultTestPrint()
    }

    func defaultTestPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        // Calling self or super go call default implementation
        defaultTestPrint()
        print("Call from struct")
    }
}


let sth = Bar()
sth.testPrint()
Amin Madani
  • 129
  • 1
  • 5
  • Is this an answer? – Anh Pham Oct 18 '18 at 15:13
  • @AnhPham thats it, just another method to perform default functionality – Amin Madani Oct 18 '18 at 16:22
  • cleaver and easy solution – Stephan Januar May 04 '19 at 22:15
  • The most expressive and flexible answer. – DawnSong May 19 '20 at 13:27
  • It's unfortunate this (or an equivalent) is not the selected and top voted answer. It is **almost certainly** the answer nearly everyone needs. The top answer, as pointed out by a comment there, radically changes the semantics to static dispatch; is very unlikely to be what people want; and will likely lead to insidious bugs, and perhaps worse, botched work arounds. – Benjohn Jul 26 '23 at 07:31
  • This is a good answer @AminMadani It is instructive of problematic information signals in S.O. that your very good answer has >10x fewer votes than the semantically dubious selected answer. The reputation ratio of the authors also very much boosts the wrong view. – Benjohn Jul 26 '23 at 07:47
4

Thanks for the post! If you put the function definition in the protocol then when the object is casted as the protocol it only sees the object's version of the function and since you are calling it inside itself you get the new address of Apple ...

I did try a version like this:

import UIKit
protocol MyProc
{
}

protocol MyFuncProc
{
    func myFunc()
}

extension MyProc
{
    func myFunc()
    {
        print("Extension Version")
    }
}

struct MyStruct: MyProc, MyFuncProc
{
    func myFunc()
    {
        print("Structure Version")
        (self as MyProc).myFunc()
    }
}

(MyStruct() as MyFuncProc).myFunc()

This gives an output of:

Structure Version
Extension Version
Jim Malak
  • 179
  • 1
  • 2
3

In case your protocol has associatedType or Self requirements, then the cast will not work. To work around this, create a "shadow" default implementation that both the regular default implementation and the conforming type can call.

protocol Foo { 
    associatedType Bar
}

extension Foo {
    func testPrint() {
        defaultTestPrint()
    }
}

fileprivate extension Foo { // keep this as private as possible
    func defaultTestPrint() {
        // default implementation
    }
}

struct Bar: Foo {
    func testPrint() {
        // specialized implementation
        defaultTestPrint()
    }
}
David James
  • 2,430
  • 1
  • 26
  • 35
3

I have come up with a solution for this.

Issue

When you have a default implementation in an extension, when you implement the protocol to another class/struct, you lose this default implementation if you implement the method. This is by design, this is how protocols work

Solution

  • Create a Default Implementation of your protocol and make it a property of your protocol.
  • Then when you implement this protocol in a class, provide your default implementation with a getter
  • Call default implementation when you need to.

Example


protocol Foo {
    var defaultImplementation: DefaultImpl? { get }
    func testPrint()
}

extension Foo {
    // Add default implementation
    var defaultImplementation: DefaultImpl? {
        get {
            return nil
        }
    }
}

struct DefaultImpl: Foo {
    func testPrint() {
        print("Foo")
    }
}


extension Foo {
    
    func testPrint() {
        defaultImplementation?.testPrint()
    }
}

struct Bar: Foo {
    
    var defaultImplementation: DefaultImpl? {
        get { return DefaultImpl() }
    }
    func testPrint() {
        if someCondition {
            defaultImplementation?.testPrint() // Prints "Foo"
        }
    }
}

struct Baz: Foo {
    func testPrint() {
        print("Baz")
    }
}


let bar = Bar()
bar.testPrint() // prints "Foo"

let baz = Baz()
baz.testPrint() // prints "Baz"


Drawbacks

You lose the required implementation error in the struct/class where you implement this protocol.

Andi Beqiri
  • 131
  • 1
  • 4
  • 1
    Please make sure to explain why your code works. This helps others who visit your answer learn from it. – AlexH Nov 23 '20 at 23:31