Using Protocol Default and Conforming Function in Swift

Protocol Default and Conforming Function in Swift main article image

Hallo mijn metgezellen, Leo hier. The topic today is using Protocol Default and Conforming Function in Swift to be able at the same time use the default protocol function and the conforming function.

Today we will explore a very niche case about Protocols. It’s not something new that Protocol is one of the most important things in iOS development. They are everywhere. And your ability to master them is decisive in building a modular and testable code.

The problem today appeared weeks ago when a teammate said: “Hey anyone knows if I can conform to a protocol and also use the default function implementation of it?”. At the moment I answered: “Not that I’m aware of”…

I couldn’t be more wrong, there’s not only one way, but also at least three ways to achieve that behavior, and we will explore each one of them today!

Let’s code! But first…

 

Painting of The Day

Today’s art piece is an 1857 painting called A Dream of the Past: Sir Isumbras at the Ford by Sir John Everett Millais. He lived from 8 June 1829 to 13 August 1896 and was an English painter. Also, he was an illustrator who was one of the founders of the Pre-Raphaelite Brotherhood.

The painting is depicting a medieval knight helping two young peasant children over a swollen river. The children are carrying heavy burdens of wood for winter fuel. Though the title refers to the medieval poem Sir Isumbras, the painting does not illustrate a scene from the original text. However, Millais’s friend, the writer Tom Taylor, wrote verse in a pastiche of the original poem, describing the event depicted. This was included in the original exhibition catalog.

I chose this painting because as the knight helped the children go through the river, I hope this article helps you go through your protocol problems.

 

The problem – Protocol Default and Conforming Function in Swift

You want to conform to a Protocol but also want the default behavior of the function in the protocol.

Copy/paste the code below into your playgrounds:

protocol Action {
    func getMessage() -> String
}

struct MyAction: Action {
    func getMessage() -> String { "Bring me Ala!" }
}

func doTheAction(action: MyAction) {
    print(action.getMessage())
}

doTheAction(action: MyAction())

As you can imagine this will print in the console: Bring me Ala. What happens if we add a default implementation for the doIt() function?

protocol Action {
    func getMessage() -> String
}

extension Action {
    func getMessage() -> String { "I said... Bring me Mik!" }
}

struct MyAction: Action {
    //func getMessage() -> String { "Bring me Ala!" }
}

func doTheAction(action: MyAction) {
    print(action.getMessage())
}

doTheAction(action: MyAction())

What happened now is that the getMessage is custom implementation is optional. This means that even if the MyAction struct doesn’t declare, we can still call it. In that case, Swift would show the default implementation of the function that returns: I said… Bring me Mik!

Now comes the problem, what if I want to use the return of the default implementation of the protocol and also have my custom implementation? Let’s see how can we do that.

 

First solution: Using self Cast

You can access the function default implementation as a self cast. But to do that work you need to erase the protocol requirement and only implement it in the protocol extension. Check below:

protocol Action {
}

extension Action {
    func getMessage() -> String { "I said... Bring me Mik!" }
}

struct MyAction: Action {
    func getMessage() -> String { "Bring me Ala!" }
    
    func defaultGetMessage() -> String { (self as Action).getMessage() }
}

func doTheAction(action: MyAction) {
    print(action.getMessage())
    print(defaultGetMessage())
}

doTheAction(action: MyAction())

Now you can call getMessage and defaultGetMessage returning two different values.

 

Second solution: Using Dummy Structs

You can create a dummy implementation of the protocol. This way you don’t have the downside of having to delete the protocol requirement. Check below how you can do it:

protocol Action {
func getMessage() -> String
}

extension Action {
func getMessage() -> String { "I said... Bring me Mik!" }
}

struct MyAction: Action {
func getMessage() -> String { "Bring me Ala!" }

func dummyDoIt() -> String {
struct Dummy : Action {} // create a Dummy struct inside the function
return Dummy().getMessage()
}
}

func doTheAction(action: MyAction) {
print(action.getMessage())
print(action.dummyDoIt())
}

doTheAction(action: MyAction())

Swift is a wonderful language. It enables us to do a lot of things. One of them is to create members inside other members. You can create a function inside a function, you can create a new `struct` inside functions, etc.

This example is creating a dummy struct implementation of the `Action` protocol so we can use it as the default implementation. This way you don’t need to change anything in the protocol. The downside is the verbosity of this solution, looks like a lot to do just to get the default implementation.

Very hacky isn’t it?

 

Create a Default Caller

And the last way that I found to use the default implementation within the conformed type, is to create in the extension a function that exclusively calls the default implementation. This technique also requires erasing the protocol requirement and leaving just the default implementation.

protocol Action {
}

extension Action {
func getMessage() -> String { "I said... Bring me Mik!" }
func defaultGetMessage() -> String { getMessage() }
}

struct MyAction: Action {
func getMessage() -> String { "Bring me Ala!" }

func callDefaultGetMessage() -> String {
return defaultGetMessage()
}
}

func doTheAction(action: MyAction) {
print(action.getMessage())
print(action.callDefaultGetMessage())
}

doTheAction(action: MyAction())

As you can see this also demands a little modification to the protocol. But yes, is also an alternative for the conformance problem.

 

Extra Protocol Default and Conforming Function in Swift: Default caller + protocol requirement

As pointed out by Jayson Rhynas (thanks for the suggestion!), you can also use the default caller and maintain the protocol requirements for the function:

protocol Action {
    var name: String { get }
    func getMessage() -> String
}

extension Action {
    func defaultGetMessage() -> String {
        "Name: \(name)"
    }
    
    func getMessage() -> String {
        defaultGetMessage()
    }
}

struct MyAction: Action {
    let name = "action"
    
    func getMessage() -> String {
        "Special name: \(name)"
    }
}

struct MyDefaultAction: Action {
    let name = "default action"
}

func doTheAction(action: Action) {
    print(action.getMessage())
    print(action.defaultGetMessage())
}

doTheAction(action: MyAction())
print("--")
doTheAction(action: MyDefaultAction())

Resulting in:

final image demonstrating the end result of the article Protocol Default and Conforming Function in Swift

And we are done!

 

Summary – Protocol Default and Conforming Function in Swift

Today we talked about how can you use the function protocol default implementation and also have your own implementation. I’m sure there should be other ways to do that, and I would love to hear from you guys.

That’s all my people, I hope you liked reading this article as much as I enjoyed writing it. If you want to support this blog you can Buy Me a Coffee or leave a comment saying hello. You can also sponsor posts and I’m open to freelance writing! You can reach me on LinkedIn or Twitter and send me an e-mail through the contact page.

Thanks for reading and… That’s all folks.

Credits:

title image

Share this post:

Related posts

Sponsor