How to reference a method with the same name and parameters but a different return type in Swift

⋅ 3 min read ⋅ Swift

Table of Contents

Problem

If you have two methods with the same name and parameters, using either of them would cause an ambiguous error.

It is easier to understand this with a real example.

In the following example, both protocol and a class that conforms it has a method with the same name and parameters, but a different return type.

Here is an example. The Greeter protocol requires the method greeting(). Class A has a method with the same name, but different return type, greeting() -> String.

protocol Greeter {
func greet()
}

class A {
func greet() -> String {
print("Hi")
return "Hi"
}
}

Ambiguous use of a method

Trying to make class A conform to Greeter required us to add the greet() method. Trying to reference an instance method greet() -> String within greet() will result in the compile error "Ambiguous use of 'greet()'".

class A {
func greet() -> String {
print("Hi")
return "Hi"
}

// 1
func greet() {
// 2
greet() // Ambiguous use of 'greet()'
}
}

<1> Implement protocol's method.
<2> Trying to reference A's greet() result in ambiguous error.

This is not a surprising error from a compiler. Even us, human, can't know exactly which method the writer is trying to referring to.

Solution

The solution for this problem is to make it less ambiguous by providing more context for the compiler. We achieve this with the help of type inference.

Type inference enables a compiler to deduce the type from the surrounding context. In this article, I provide more context by declaring a variable with an explicit type.

Declare a variable to store a return value

Instead of calling greet() alone, we assign it to a greeting constant of type String. This change makes the compiler know that it should use a greet method that returns a string (the one that belongs to class A).

class A: Greeter {
func greet() {
// 1
let greeting: String = greet()
}

func greet() -> String {
print("Hi")
return "Hi"
}
}

<1> We make it less ambiguous by telling the compiler that we expect a greet method that returns a string.

Declare a variable to store a method signature

Since a function is a first-class citizen in Swift, we can declare a variable to store a method signature that we want instead of a return type.

class A: Greeter {
func greet() {
// 1
let greetingFunction: () -> String = greet
// 2
_ = greetingFunction()
}

func greet() -> String {
print("Hi")
return "Hi"
}
}

<1> We make it less ambiguous by telling the compiler that we expect a greet function with a signature as () -> String.
<2> Call the stored function to print the greeting message.

Extension

These solutions also work with an extension.

extension A: Greeter {
func greet() {
let _: String = greet()
}
}

Any methods

I use a protocol as an example throughout the article, but this problem can happen everywhere.

Here is a class with two instance methods with the same name and parameters, but a diffrent return type.

class B {
func greet() -> String {
print("Hi B")
return "Hi B"
}

func greet() {
print("Hello B")
}
}

Use b.greet() alone would cause the same ambiguous error. The solution is still the same. We need to use a method in a context where a compiler can infer the method signature.

func methodThatExpectedString(_ string: String) {}

let _: String = b.greet()
// Hi B

let _: Void = b.greet()
// Hello B

// 1
methodThatExpectedString(b.greet())
// Hi B

<1> methodThatExpectedString accept a string parameter. A compiler can infer from the parameter type that it needs a greet that returns a string.

You can easily support sarunw.com by checking out this sponsor.

Conclusion

In this article, we learn how to use type inference to make our code less ambiguous for a compiler when referencing methods with the same name and parameters.

I use an example where a class and protocol have the same method signature because it is not obvious that I can have a protocol with a method name that collides with a class that conforms to it. The swift protocol is very powerful but contains hiccups here and there, and I thought this is one of them. Turn out the solution is simple than I thought.


Read more article about Swift or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
How to show multiple alerts on the same view in SwiftUI

If you have ever worked with an app with multiple alerts, please beware that the system can present only the latest or outermost one. Let's see how we can mitigate this.

Next
How to define custom environment values in SwiftUI

It might not be obvious that we can create a custom environment value, but we can do that. The steps to create one are not as straightforward as we usually do, but it isn't hard if you know how to do it.

← Home