Hallo muren en plafonds, leo hier. Today we will think about the architectural decision of using closures and protocols for your types.

We’re diving head-first into the riveting world of design decisions. More specifically, we’ll discuss and compare two primary approaches to injecting behavior into your types: Closures and Protocols.

Lately, I’m more to the office so I can get in touch with my team and that has been an amazing experience so far. Of course, as a developer, we can make our jobs in our own house, but I feel that at least in the beginning is good to try to create some real connection with your new team. Sure, when you get more independence and already met everyone you can easily work full home office/hybrid. 

Another thing very important when you are new to a job is to ask a lot of questions. IS EXPECTED YOU TO DO SO, no matter your seniority level asking questions is the most important thing that you can do for yourself and the team when you join a new team. I’m writing this because is my current situation where I’m fairly new to the team and I don’t know about all the flows/products/acronyms/etc.

I’m also very glad that this Monday we had our biggest users peak in one day ever, we got an outstanding amount of two thousand users Sunday and Monday. That is all because we got featured in his amazing SwiftUI newsletter. Thanks for that!

In other news, I’m now looking to buy a house here in the Netherlands. This will be a wild ride and I’m excited to see where we will end up living.

So grab a coffee (or a cup of tea if you’re that way inclined), and let’s get started! But first…

 

Painting of The Day

The painting I chose for today is called Portrait of a Lady, which is a 1775 art piece by Angelica Kauffman.

Angelica Kauffman RA, whose full name is Maria Anna Angelika Kauffmann, was born on October 30, 1741, and passed away on November 5, 1807. Originating from Switzerland, Kauffman was a talented painter of the Neoclassical era. Although she’s most commonly recognized for her contribution to history painting, Kauffman also excelled in creating portraits, landscapes, and decorative artwork. Notably, in 1768, she became one of the first two women to establish the Royal Academy in London.

This painting is a technical marvel, renowned for its precision in tone, detail, and proportion. It portrays an unidentified woman leaning on a neoclassical column, with a statue of Minerva, the Roman goddess of wisdom, by her side – a symbol that Kauffman associated with female empowerment. The woman, seen with a book and writing tool, may represent contemporary female intellectuals like Catherine Macaulay or Elizabeth Montagu. Her demeanor radiates self-assurance, empowerment, and elegance, akin to a Roman goddess.

I chose this painting because of the apparent simplicity, however, when we dig into the intricate details of the painting we can truly understand why some decisions were made. This is what this article is about, a decision between closures or protocols that seems simple but hides a lot below the surface.

 

The Problem: Closures and Protocols. What to choose?

You need to inject a behavior in your class, you are thinking about using protocols but it might as well have been a closure. What to choose?

I think everyone that has a minimum time developing for iOS already asked this type of question. And that is a completely natural and valid question. Also, the example that we will discuss today is constrained to your need to inject ONE function in another object, otherwise, there will not be even anything to compare, right?

Starting out with dependency injection we have several ways to inject behaviors. We can use the initializers, we can use frameworks, we can use language capabilities, etc. The fact that we have so many alternatives doesn’t make life easy for the ones who are starting this journey.

Before we kick things off, let’s shed some light on these two programming concepts for anyone not entirely up to speed.

Protocols, in the Swift realm, define a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. Protocols are very much like interfaces in other languages.

On the other hand, Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to lambdas in other programming languages.

Now that we’ve set the stage, let’s take a deep dive and examine how these two players perform in the arena of iOS development.

The goal of this article is just to highlight architectural differences in choosing closures over protocols, or vice-versa. Don’t expect a guide on what closures are or a deep protocol article. This is not your place.

 

Closures: A Love Affair

Closures are a hot favorite among Swift developers and for good reason. Closures in Swift are first-class citizens, which generally means that typically they can be passed as an argument, returned from a function, and assigned to a variable.

A named closure example would be:

var myAgeClosure: (Int) -> String = { age in
    return "My age is \(age)"
}

print(myAgeClosure(2))

This article will discuss the case when a type has a closure as one of its properties, like the example below:

struct Animal {
    let makeSound: () -> ()
}

So we needed to add some behavior to the Animal type, but we will only know what behavior is when we actually instantiate the Animal struct.

Before we continue to protocols and the comparison let’s enumerate some pros and cons of closures in our beloved Swift language.

 

Architectural Pros and Cons of Closures in Swift

Closures offer several notable advantages, they are but not limited to:

  1. Flexibility and Conciseness: Closures are perfect for short, contained blocks of code. They’re inline and don’t require defining an entirely separate entity like a protocol or a class, making your code concise and easily readable.
  2. Capturing and Storing References: This is where closures shine. They effortlessly capture and store references to any constants and variables from the context in which they were defined (known as closing over variables, hence the name). This functionality isn’t possible using protocols.

However, closures aren’t without their quirks.

Closures Cons:

  1. Possible Memory Leaks: Because closures can capture and store references, they can unintentionally create strong reference cycles leading to memory leaks if you’re not careful. Always remember to use [weak self] to prevent these issues.
  2. Complexity: While closures are beautiful for small tasks, using them for larger pieces of functionality can make your code difficult to read and understand.

Just looking at both lists you can already imagine where closures shine… Whenever you have a really simple task that you need reference semantics and it won’t grow in size and complexity, you have a good case for using a closure.

 

Protocols: The Sturdy Staple

Protocols, the interface-like entities in Swift, also come with their own set of perks, some are:

  1. Clear Structure and Readability: With protocols, what you see is what you get. They offer a clear structure and enhance the readability of your code, making it easier to understand and maintain.
  2. Enhanced Testability: Protocols shine when it comes to unit testing. Mocking dependencies by creating fake objects conforming to the same protocol is a breeze.
  3. Reusability: You can define a protocol once and use it in many places. This level of reusability allows for better consistency across your code.

 

There are also a lot of other advantages to using protocols.

But it’s not all rainbows and unicorns in Protocol-land.

Protocols Cons:

  1. Verbosity: Protocols, being separate entities, tend to make your code more verbose. This can become problematic, especially when dealing with simple tasks.
  2. Lack of State: Protocols can’t maintain a state, which makes them less suitable for tasks where retaining context is essential. You can’t capture the surrounds with a protocol unless you use a closure inside your protocol but this is not what we will compare here.

 

As we are injecting behaviors into our objects we need to really think about what we need in terms of maintainability and conciseness. Closures are not flexible as protocols but depend on the situation that is exactly what you need. Let’s explore some key head-to-head comparisons in the next section.

 

Making The Call: Closures or Protocols?

In the battle of closures vs protocols, who takes the crown? Well, that’s a question only you can answer, and it depends on the context of your task.

Try to answer some questions: How simple is the behavior that I’m injecting? Do I need it to have a stateful context or not? Do I need it to capture the surrounding objects? What will be the name of the protocol that is not harmful to the whole app namespace?

The namespace discussion is something that I usually don’t see people talking about but is a very important one. Naming is one of the most difficult things in computer science and when you declare a new protocol it becomes a type for the compiler and no other types can have that name. Check the code below:

struct Animal {
    let makeSound: () -> ()
}

protocol Animal {} // you can't do this!

When choosing the naming for your protocols always try to follow Swift language API recommendations

  • Protocols that describe what something is should read as nouns (e.g. Collection).

  • Protocols that describe a capability should be named using the suffixes able, ible, or ing (e.g. Equatable, ProgressReporting).

 

Another thing to keep in mind is that when you create a protocol that your class will use, you are adding a new dependency to that class. Imagine that you want to inject a behavior that receives an object, for example a car. So you have a Park that needs to park a car but you want to inject that behavior. Check the code below how the approach could be done with closures and with protocols: 

 

struct Car {}

// Closure based
struct Park {
    let park: (Car) -> ()
}


// Protocol Based
protocol Parkable {
    func park(car: Car)
}

struct Park2 {
    let park: Parkable
}

But Leo, besides everything we already discussed, are there more differences? Yes, we need to look at the architectural side of this.

Let’s draw each one of the solutions below and you will clearly see the brutal difference between them:

how to chose between closures and protocols in Swift

 

See? Now with the protocol approach, we have one more layer of indirection. And this could be good, or bad, depending on what you want. Usually adding indirections make your code more reusable as we stated in the protocol section, however, too much indirection decreases your code maintainability and transforms your life into a hell of abstractions.

While some people really advocate for: If you have just one function you should use a closure instead a protocol. I’m not that sure about this statement. I think you should think about everything we discussed today and do your own decision.

For short, simple tasks without the need for retained state, closures might be your best bet. They’re concise, flexible, and great at capturing and storing references.

However, protocols rise to the occasion for larger tasks that require readability, testability, and reusability.

And remember, it’s not always an “either-or” situation. Swift is a versatile language, and often the best solution involves a combination of both closures and protocols. So keep an open mind, understand the pros and cons of each, and make your decision based on the specific needs of your project.

 

Summary – Closures and Protocols Discussion 

As with most architectural decisions, the closures vs protocols debate isn’t black and white. Both have their merits and shortcomings, and the key is understanding when to use which. Hopefully, our little exploration today helped you get a clearer perspective on these two crucial Swift features.

In the end, remember the wise words of Tony Hoare, “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies.”

Fellow Apple Developers, that’s all.

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 say hello on Twitter. I’m available on LinkedIn or send me an e-mail through the contact page.

You can likewise sponsor this blog so I can get my blog free of ad networks.

Thanks for the reading and… That’s all folks.

Image credit: Featured Painting