What Is @escaping in Swift

In Swift, a closure marked with @escaping means the closure can outlive the scope of the block of code it is passed into.

In a sense, @escaping tells the closure to “stay up even after the surrounding function is gone”.

But why is this useful?

Let’s say you are performing an (asynchronous) network request such that:

  • Function f() fetches information from a web page.
  • After completion, a closure function g() runs inside f().

The only way to make this possible is to pass the function g() as an escaping closure into the function f().

This tells the Swift compiler after calling f() to keep g() alive to wait for the network request to complete and the response to arrive.

In this guide, we are going to take a look at non-escaping closures and escaping closures in detail.

Also, you are going to see a demonstrative example to support understanding.

Closures in Swift

In Swift, closures are non-escaping by default.

If you pass a non-escaping closure into a function, it is called right away.

The life of the non-escaping closure ends when the function call finishes.

As a result, there will be no trace of that closure.

This is the default behavior.

Let’s see a simple example of a non-escaping closure and its behavior.

  • Let’s write a function add that takes:
    • Integers a and b
    • A closure called action
  • Right after summing the numbers up, the function add calls the action.
func add(a: Int, b: Int, _ action: (Int) -> Void) {
    var sum = a + b
    action(sum)
}

Let’s call this function with integers 10 and 20 and a closure that prints the result into the console:

add(a: 10, b: 20, { res in
    print(res)
})

Output:

30

As you can see, the result of adding 10 and 20 gets printed to the console right away.

However, this default behavior of a closure can be an issue when dealing with asynchronous code.

In an asynchronous function, you might not want to execute the closure right away. Instead, you want to wait for the asynchronous task to complete before calling a function.

This is where escaping comes in handy.

@escaping: Escaping Closures in Swift

Escaping makes it possible for the closure to outlive the function surrounding it.

This means the closure can stay around to catch a network request-response that arrives later.

To make a closure escaping, use the @escaping keyword modifier in front of the closure type.

Let’s see an example of how using a non-escaping closure fails in the context of an async function, and how to fix it using an escaping closure.

Example

Say you are using an app to message your friend and you ask them “How are you?”.

As you know, it always takes a while to get a response.

For the sake of demonstration, let’s say it takes 2 seconds for your friend to reply back.

Let’s simulate this conversation in Swift code.

To mimic this behavior, you need a function that takes a response handler closure as a parameter.

This response handler is simply responsible for taking care of handling your friend’s response that arrives 2 seconds later.

Here is how it looks in code:

func howAreYou(_ responseHandler: (String) -> Void) {
    print("Hey, how are you?") // you ask how are you
  
    // Simulating how it takes 2 seconds for your friend to answer:
    DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
        responseHandler("Hi, I'm doing really good.")
    })
  
    print("Responding takes a while...")
}

howAreYou({ friendResponse in
    print(friendResponse) // print the response that arrives later
})

But this code causes a compiler error.

This is because you are trying to execute the responseHandler function 2 seconds after the howAreYou function has executed.

This is a problem because 2 seconds after calling the howAreYou, there is no trace of that function or the responseHandler closure.

Thus the responseHandler never has a chance to handle your friend’s response. It gets destroyed before the response even arrives.

The Swift compiler is clever. It knows this is about to happen. Thus it shows you an error and does not run the code.

To make it work, you should make the responseHandler closure outlive the context of the howAreYou function to see the response arrive.

To do this, you need to turn it into an escaping closure:

func howAreYou(_ responseHandler: @escaping (String) -> Void) {
    print("Hey, how are you?") // you ask how are you
  
    // It takes 2 seconds for your friend to answer:
    DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
        responseHandler("Hi, I'm doing really good.")
    })
  
    print("Responding takes a while...")
}

howAreYou({ friendResponse in
    print(friendResponse) // print the response that arrives later
})

This way you tell the program that the responseHandler is an asynchronous closure and you want it to stay alive even after howAreYou call completes.

Now the compiler error is gone and the code works as intended.

It is as simple as that!

Now your responseHandler closure is able to show your friend’s response as it can out-live the howAreYou function’s scope!

But this example is quite arbitrary.

The next logical question is when is an escaping closure actually useful.

Let’s take a look.

When Are Escaping Closures Actually Useful

Escaping closures are useful whenever you want the closure to be able to outlive the function’s scope from where you are calling it.

A great example is when dealing with network requests.

When fetching data over a server, it takes a while for a network request to complete, and thus, the response to arrive.

To perform actions on the response, you need to wait for it to arrive.

To make a closure wait for the data, it has to be able to outlive the function’s scope where it is called.

To do this, you need to use an escaping closure.

To perform actions on the response, you need to

Conclusion

Today you learned what is @escaping in Swift.

In short, @escaping is a keyword modifier that turns a closure into an escaping closure.

An escaping closure is useful in asynchronous tasks when the closure has to wait for the function to complete.

When you use an escaping closure, the closure outlives the function in which it is called. In other words, when the function completes, the closure stays up and keeps waiting for the response.

Thanks for reading. Happy coding!

Further Reading

50 Swift Interview Questions