Protocol default implementations and class inheritance

Hi,
At the moment I have the following code:

@objc protocol Protocol: AnyObject {
    @objc optional func protocolFunction()
}

class Class: Protocol {
    func classFunction() {
        (self as? Protocol)?.protocolFunction?()
    }
}

class SubclassA: Class {
    func protocolFunction() {
        print("subclass a function")
    }
}

class SubclassB: Class {
    func protocolFunction() {
        print("subclass b function")
    }
}

Basically, in Class, protocolFunction() is called from inside classFunction(), but Class's implementation of protocolFunction() is not important.
When an instance of a Class subclass calls classFunction(), if it has implemented protocolFunction(), then its own implementation of it is called (in this case a SubclassA instance prints "subclass a function"). Not every subclass needs this functionality.

What I am having trouble with is doing the same thing but without having to use objc protocols.

I have tried to the following:

protocol Protocol: AnyObject {
    func protocolFunction()
}

extension Protocol {
    func protocolFunction() {
        print("default function")
    }
}

class Class: Protocol {
    func classFunction() {
        protocolFunction()
    }
}

class Subclass: Class {
    func protocolFunction() {
        print("subclass function")
    }
}

However, when I call classFunction() on a Subclass instance it uses the default implementation of protocolFunction() rather than the implementation in Subclass (in this case it prints "default function" rather than the desired "subclass function").

Could anyone please let me know how I can have it so that I can use the inherited classFunction() with Subclass but have it call the subclass's implementation of protocolFunction() instead?

Thanks in advanced!

This happens because you are not overriding protocolFunction in Subclass. You are defining your custom implementation of protocolFunction for Subclass, while Class.classFunction continues to use the default implementation. So

Subclass().classFunction()  // "default function"
// Calls Class.classFunction, and thus Class.protocolFunction === Protocol.protocolFunction
// (the default implementation of protocolFunction)

Subclass().protocolFunction() // "subclass function"
// Calls Subclass.protocolFunction

If you want classFunction to call Subclass.protocolFunction, you can either override it:

class Subclass: Class {
    func protocolFunction() {
        print("subclass function")
    }
    override classFunction() {
        self.protocolFunction() // Calls Subclass.protocolFunction ("subclass function")
    }
}

or define a custom implementation of protocolFunction in Class and then override it in Subclass:

class Class: Protocol {
    func classFunction() {
        protocolFunction()
    }
    func protocolFunction() {
        print("class function")
    }
}

class Subclass: Class {
    override func protocolFunction() {
        print("subclass function")
    }
}

Subclass().classFunction() 
// Calls Class.classFunction, which calls the overridden Subclass.protocolFunction ("subclass function")

In your example with objc protocols, protocolFunction is called if there is an implementation. Considering Class doesn't have any implementation available, the subclass implementations are called.

I am not entirely sure though that I haven't messed up with stating who calls who – @jrose can you help out if needed?

Thanks, for your reply.

If you want classFunction to call Subclass.protocolFunction, you can either override it

I understand that I can override classFunction in Subclass but since the code would be identical in each subclass I was hoping to not have to duplicate so much.

Or define a custom implementation of protocolFunction in Class and then override it in Subclass

Also, unfortunately this second alternative behaves the same way as my code in the original post. (i.e. Subclass().classFunction() still performs Class's implementation of protocolFunction rather than the overridden one in Subclass).

In your example with objc protocols, protocolFunction is called if there is an implementation. Considering Class doesn’t have any implementation available, the subclass implementations are called.

With the objc protocol, even when Class has its own implementation of protocolFunction, calling Subclass().classFunction() will still print the desired "subclass function".

Thanks!

Unluckily this is a dangerous language limitation, of which there is an ongoing discussion on how to solve it.

My personal workaround to this that I used in the past is:

protocol Protocol: AnyObject {
    func protocolFunction()
}

extension Protocol {
    func protocolFunction() {
        _default_protocolFunction()
    }
    func _default_protocolFunction() {
        print("default function")
    }
}

class Class: Protocol {
    func classFunction() {
        protocolFunction()
    }
    func protocolFunction() {
        _default_protocolFunction()
    }
}

class Subclass: Class {
    override func protocolFunction() {
        print("subclass function")
    }
}

Ugly, I know, but to dynamically resolve protocolFunction then Class must implement it (currently).

1 Like

Thanks, your code works for me!
The thread you linked is very interesting. Hopefully this will be resolved soon. :slight_smile:

It must work. At least for Swift 4.0 and 4.1. I confirmed just in case before proposing. If you are referring to this, that is:

protocol Protocol: AnyObject {
    func protocolFunction()
}

extension Protocol {
    func protocolFunction() {
        print("default function")
    }
}
class Class: Protocol {
    func classFunction() {
        protocolFunction()
    }
    func protocolFunction() {
        print("class function")
    }
}

class Subclass: Class {
    override func protocolFunction() {
        print("subclass function")
    }
}
Subclass().classFunction() // "subclass function"

Of course, because, just like in the above example, you would be overriding protocolFunction. Overriding equally applies to both native and objc protocols.

We consider this a compiler bug (or more precisely, a limitation). Fixing this is tracked in [SR-103] Protocol Extension: function's implementation cannot be overridden by a subclass · Issue #42725 · apple/swift · GitHub.

3 Likes