unigraphique.com

Understanding Swift Protocol Extensions and Default Arguments

Written on

Chapter 1: The Common Misunderstanding

Lately, I've observed an increasing number of developers making the error of incorporating default arguments within protocol extensions. This mistake is not limited to novice programmers; even those with considerable experience often find themselves caught in this trap. Thus, this blog post aims to clarify some fundamental aspects of the Swift programming language.

Section 1.1: Identifying the Mistake

What is the specific error I’m referring to? As many of you may already know, Swift prohibits default arguments in protocol method parameters. The following example will not compile:

protocol SomeProtocol {

// Default argument is not allowed in a protocol method

func doSomething(with number: Int = 0)

}

How can we address this limitation? The solution lies in using an extension! We can define a method with the same name in the extension, but without parameters, allowing us to call the original protocol method with the default value:

protocol SomeProtocol {

func doSomething(with number: Int)

}

extension SomeProtocol {

func doSomething() {

doSomething(with: 0)

}

}

This enables us to invoke the method with or without the argument.

Section 1.2: The Temptation of Multiple Default Arguments

However, when a protocol method has several parameters, one might be tempted to provide all default arguments within a single extension method:

protocol SomeProtocol {

func doSomething(with number: Int, and string: String)

}

extension SomeProtocol {

func doSomething(with number: Int = 0, and string: String = "Empty") {

doSomething(with: number, and: string)

}

}

At first glance, this appears to work correctly. For instance:

struct SomeImplementation: SomeProtocol {

func doSomething(with number: Int, and string: String) {

print("The number is (number), and the string is (string)")

}

}

let impl = SomeImplementation()

impl.doSomething(with: 1, and: "Not empty")

impl.doSomething(and: "Not empty")

impl.doSomething(with: 1)

impl.doSomething()

The output will be as follows:

The number is 1, and the string is Not empty

The number is 0, and the string is Not empty

The number is 1, and the string is Empty

The number is 0, and the string is Empty

Nevertheless, the protocol definition allows you to omit the implementation (or forget to add it), which leads to a critical issue:

struct SomeImplementation: SomeProtocol { }

Why is this problematic? Although the method in the extension is declared differently from its definition in the protocol, they share the same name and signature. Without an implementation in SomeImplementation, the method from the extension defaults as the implementation for the protocol method.

Do you sense the danger? What occurs when calling the method on an instance of SomeImplementation? Correct—this will result in a crash due to a stack buffer overflow error, as it leads to infinite recursion:

func doSomething(with number: Int = 0, and string: String = "Empty") {

doSomething(with: number, and: string)

}

Section 1.3: The Correct Approach

How can we prevent this situation? Currently, the only solution is to declare implementations for all variations of the method calls in the extension, excluding the full version that includes all parameters as defined in the protocol:

protocol SomeProtocol {

func doSomething(with number: Int, and string: String)

}

extension SomeProtocol {

func doSomething(with number: Int) {

doSomething(with: number, and: "Empty")

}

func doSomething(with string: String) {

doSomething(with: 0, and: string)

}

func doSomething() {

doSomething(with: 0, and: "Empty")

}

}

All methods in the extension now feature distinct parameter sets, ensuring none invoke themselves in their implementations. While this method may be verbose, it effectively avoids accidental default implementations of the protocol:

// Type 'SomeImplementation' does not conform to protocol 'SomeProtocol'

struct SomeImplementation: SomeProtocol { }

Conclusion

The capability of default arguments in Swift is indeed powerful, yet it comes with caveats when dealing with protocols and their extensions. It’s astonishing how frequently even seasoned developers fall into this trap. This issue may go unnoticed for extended periods, only to emerge unexpectedly. With this knowledge, both novice and experienced developers can better navigate the common pitfalls associated with using default arguments in Swift protocol extensions.

Here are a few additional topics regarding programming in Swift:

  • Dark Side of Swift’s Type Inference
  • Abusing Swift’s Result Type
  • Retriable API Calls with Modern Swift

The video titled "Swift & Fika 2018 – Paul Hudson: How Not to Write Swift" provides further insights into effective Swift programming practices.

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

# The Threat of AI

The potential misuse of generative AI in the upcoming US elections poses serious risks, including misinformation and voter manipulation.

Unlock 15 Google Tricks Every Developer Should Know

Discover 15 useful Google search tricks that can enhance your development skills and streamline your coding process.

A Lesson in Satisfaction from Everyday Employees

Exploring the value of attitude in the workplace through everyday experiences.

Steal This Strategy: Write and Publish Your Book in Six Months

Discover a transformative approach to writing and publishing a book in under six months by shifting your mindset and identity.

Boost Your Luck: Simple Steps to Enhance Your Fortune

Discover effective strategies to increase your luck and opportunities in life through simple actions and social interactions.

Exploring the Dark Side of Crossbreeding Experiments

An exploration into the ethical dilemmas of crossbreeding experiments, highlighting notable hybrids and their implications.

Finding New Purpose Through Digital Technology After Retirement

Exploring how digital tools and video gaming can redefine life after retirement, inspired by a father's journey.

Letting Go: The Journey of Releasing a Relationship for Healing

Discover how letting go of a relationship can pave the way for personal healing and growth amid life's challenges.