How To: Escaping Closures In Swift With @escaping

Written by Reinder de Vries on January 27 2019 in App Development

How To: Escaping Closures In Swift With @escaping

In Swift, closures are non-escaping by default. What are escaping closures, anyway? And what do you use @escaping for?

This article discusses closures, escaping vs. non-escaping, capture lists and retain cycles, and what that means for practical iOS development.

What we’ll get into:

  • When to use an escaping and non-escaping closure, and the @escaping keyword
  • How escaping and non-escaping closures affect the way you code
  • What the inner mechanics are of closures, capturing and memory management

Enjoy!

  1. Quick Primer: What’s A Closure?
  2. What Does “Escaping” Mean?
  3. The Risks Of Wrongly Escaping Closures
  4. Non-Escaping Closures vs. Capture Lists
  5. What’s Changed In Swift 3?
  6. Further Reading

Quick Primer: What’s A Closure?

Closures are awesome! You probably already know this, but a closure is a block of code you can pass around and use elsewhere in your code.

It’s like a function you can assign to a variable. You can move the variable around in your code, and call the code of the closure at a later point.

let closure = { (name:String) -> String in
    return "Hello, \(name)!"
}

let message = closure("Bob")
print(message)

In the example above the constant closure contains a closure. This closure takes one parameter called name of type String. The closure itself then returs a string – a greeting of sorts.

When the closure is called in the above example, with closure("Bob"), its value is assigned to constant message and printed out.

A common use-case for a closure is a so-called completion handler.

Say you’re downloading an image, from the web, that you want to display in a view controller. Using Alamofire, a popular networking and web request library for Swift, your code could look something like this:

Alamofire.request(imageURL).responseData {
    response in

    if let data = response.result.value {
        self.imageView?.image = UIImage(data: data)
    }
}  

As you can see a web request is made, which is chained to a call to function responseData(queue:completionHandler:). Only one argument is provided to the function – a closure.

Inside the closure the response is unwrapped and its data is used to assign the image data to a property imageView.

This closure is a completion handler. It’s executed when the request to download the image has finished. In other words: you can use a closure to determine what happens when a particular action is completed (hence “completion handler”).

Closures are extremely powerful. In the example above, the closure is written in the same place as the request to the web image, and in the same file as the imageView property (and outlet).

The code that actually downloads the image and processes it is somewhere else in the project, in an entirely different place. Thanks to closures we can pass functionality around, keep concerns separated, and keep our code compact, yet modularized.

Become a professional  iOS developer

Get started with iOS 12 and Swift 4

Sign up for our iOS development course Zero to App Store to learn iOS development with Swift 4, and start with your professional iOS career.

What Does “Escaping” Mean?

Now, take a look at the signature of the function responseData:

public func responseData(
    queue: DispatchQueue? = nil,
    completionHandler: @escaping (DataResponse<Data>) -> Void)
    -> Self
{
    ...

Right there you can see the type of the parameter completionHandler, a closure that provides one parameter and returns Void.

The closure type also contains a special keyword: @escaping. This indicates that the closure is an escaping closure – but what’s that?

An escaping closure outlives the function it was passed to.

In this sense there are two kinds of closures:

  • An escaping closure is a closure that’s called after the function it was passed to returns. In other words, it outlives the function it was passed to.
  • A non-escaping closure is a closure that’s called within the function it was passed into, i.e. before it returns.

A good example of an escaping closure is a completion handler. It’s executed in the future, when a lengthy task completes, so it outlives the function it was created in. Another example is asynchronous programming: a closure that’s executed asynchronously always escapes its original context.

The Risks Of Wrongly Escaping Closures

Closures can be challenging for beginner programmers, and if you go crazy with too much closures it’s very easy to make a big mess of your code. Moreover, with a closure you can very easily create a retain loop.

Let’s look at an example. In this example I’m asking you to go to the shop to buy 5 apples. When the apples are bought I want to send them with airmail to my aunt. It’s a weird world…

We’re very civilized citizens so we keep to a strict separation of concerns – you only know how to buy apples, and I only know how to send them.

Fortunately you let me hand you a block of commands when I tell you to go shopping. This is a closure, a set of commands to be executed when you finished buying the apples. Thanks to the closure I can tell you what to do with the apples, without requiring you to have this knowledge beforehand.

Inside the closure I reference the airmail property. It’s an object only I know and hold on to, because I know how to send packages.

The by-effect of me referencing airmail inside the closure is that the closure now also keeps a strong reference to it. This is known as closing over that property – the closure strongly captures a reference to the airmail property.

Unfortunately, this introduces a problem in our weird world. The time comes that you’ve executed the closure and we’re all ready to go home and call it a day. We try to clean up all the references we’ve made to objects, such as the airmail property.

Specifically, I try to remove the airmail property from my memory because I have no need for it anymore. Additionally, the closure, that also had a reference to airmail tries to do the same. It has sent the apples and now it doesn’t need the airmail instance anymore.

When I try to remove airmail I see that the closure still keeps a reference to it, so I won’t remove it – perhaps the closure still needs the reference. Likewise, the closure sees that I’m still holding on to the airmail instance and won’t remove it.

This is known as a retain cycle. It’s a memory leak, because we know have an instance occupying space in memory when it shouldn’t.

Non-Escaping Closures vs. Capture Lists

Fortunately, there’s a solution. When I write my closure I provide it with a capture list. This explicitly tells the closure to weakly hold on to the airmail instance, like this:

{ [weak airmail] (apples) -> Void in
    // Send apples
}

The closure now holds a weak reference to airmail. This will break the retain cycle, because I’m the only one holding on to the instance. When the time comes to remove references from memory, I can safely remove airmail. Logically, this is based on the assumption that the closure will finish before I’m removing airmail (and myself) from memory.

But… how is this relevant for escaping closures? Easy!

A non-escaping closure can’t create a retain cycle, because all variables it references from the context it was created in will be removed after the closure completes execution. There’s no risk of the closure holding onto references because the closure doesn’t outlive the context it was declared in.

OK, let’s take a step back. Let’s say we have a non-escaping closure that references a particular object. We provide the closure as an argument to a function. The function then calls the closure inside its function body, before the function returns.

The closure will make a strong reference to any objects it captures, but those strong references will be released once the closure finishes executing. The closure finishes executing before the function it was called in returns, thus it doesn’t outlive that function. Therefore, it can’t create a retain loop.

What’s Changed In Swift 3?

In Swift 3 closures are made non-escaping by default. If you want to declare an escaping closure, for instance as a completion handler, you’ll have to mark it with @escaping.

If the closure, passed as an argument to the function you defined _without_@escaping, is executed after your function returns, you’ll get a compile-time error. This is because the closure escaped without being marked as such.

This benefits the programmer in a few ways:

  • It’s safer! Because closures are non-escaping by default it’s harder to unknowingly create a retain cycle. Previously you could forget to create a weak capture list in an escaping closure. Now that you actively have to indicate that a closure can escape, you’re less likely to also forget to make a capture list. Additionally, because the compiler can detect errors like these, it can improve your coding experience by giving you a warning before you build and run your app.
  • It’s more productive! You remove boilerplate code for closures that were non-escaping anyway by making closures non-escaping by default. Moreover, it’s more work to write an escaping closure anyway so adding another @escaping keyword doesn’t add much, whereas it reduces work for closures that already were non-escaping.
  • It’s faster! Technically, the compiler can optimize non-escaping closures better because they don’t strongly reference objects. The compiler doesn’t need to store these objects in a way that allows it to access them later.

Become a professional  iOS developer

Get started with iOS 12 and Swift 4

Sign up for our iOS development course Zero to App Store to learn iOS development with Swift 4, and start with your professional iOS career.

Further Reading

In Swift, closures are non-escaping by default. This means that the closure can’t outlive the function it was passed to as a parameter. This means less risk of creating a retain cycle as a programmer and an increase in productivity, code safety and compiler optimization.

If you do need to declare a function that accepts a closure that’s called after that function returns – an escaping closure – you need to mark it with the keyword @escaping.

In your day-to-day as an app developer you might not notice much from this change. You may run into less memory issues and write less capture lists. If you’re a coder that writes frameworks or abstractions, you’ll probably need the @escaping keyword a lot more.

Want to learn more? Check out these resources:

Reinder de Vries

Reinder de Vries

Reinder de Vries is a professional iOS developer. He teaches app developers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed a few dozen apps for iOS, worked for global brands and lead development at several startups. When he’s not coding, he enjoys strong espresso and traveling.