Greetings, traveler!
Sometimes, when writing code, we can encounter ambiguous behavior. Can you guess which method will be executed by default?
func action(_ view: UIView) {}
func action(_ views: UIView...) {}
let view = UIView()
action(view)
Let’s check out another example.
func action() {}
func action(string: String = "") {}
action()
And what about this one?
class Object: ExpressibleByStringLiteral {
private let string: String
required init(stringLiteral value: StringLiteralType) {
string = value
}
}
func action(_ value: String) {}
func action(_ value: Object) {}
action("")
Finally, let’s take a look at this example.
func action(_ number: Int) {}
func action(_ number: some Numeric) {}
action(5)
There is a hidden principle in Swift, which Jordan Rose, a former Swift team member, declared:
The general principle is that the most specific overload should win, and a function that always takes 0 parameters is “more specific” than a function that sometimes takes 0 parameters.
So, let’s check out the correct answers.
// 1
func action(_ view: UIView) {} ✅
func action(_ views: UIView...) {} ❌
// 2
func action() {} ✅
func action(string: String = "") {} ❌
// 3
func action(_ value: String) {} ✅
func action(_ value: Object) {} ❌
// 4
func action(_ number: Int) {} ✅
func action(_ number: some Numeric) {} ❌
But what if we want to change this behavior? We can do so with the underscore @_disfavoredOverload
attribute.
Note
There is a warning in Swift documentation about underscore attributes:
Usage of these attributes outside of the Swift monorepo is STRONGLY DISCOURAGED.
The semantics of these attributes are subject to change and most likely need to go through the Swift evolution process before being stabilized.
All the stable attributes are described here.
Example
We can mark our method with this attribute to indicate that calling it will be less preferable. Let’s consider an example without this attribute once again.
func action(_ number: Int) {
print("Something else")
}
func action(_ number: some Numeric) {
print(number)
}
action(5) // Something else
And now, let’s add this attribute to the first method.
@_disfavoredOverload
func action(_ number: Int) {
print("Something else")
}
func action(_ number: some Numeric) {
print(number)
}
action(5) // 5
Conclusion
While this attribute was introduced a few years ago, it must still pass the Swift evolution process. So, we should count it as unstable.
If you enjoyed this article, please feel free to follow me on my social media: