Select Page

It is easy to get started with Swift and that is exactly one of the compelling aspects of the language. But as you use the language more often, you gradually come into contact with Swift’s more complex constructs.

In Swift 2, you may have run into the @noescape attribute. Have you ever taken the time to understand what it means? In Swift 3, @noescape has been removed from the language. Why is that? And why does Swift 3 introduce @escaping? Answering these questions is the focus of this article.

What Is the Meaning Of @noescape?

Even though @noescape is deprecated in Swift 3, it is useful to understand what it means. Why is that? The answer is simple. The @noescape attribute is applied by default in Swift 3. Let us start form the beginning.

What Is an Escaping Closure?

You first need to understand what an escaping closure is. The definition is very simple and easy to understand. If a closure is passed as an argument to a function and it is invoked after the function returns, the closure is escaping. It is also said that the closure argument escapes the function body. With this definition in mind, the name of the term escaping closure is well chosen. Right?

If a closure is passed as an argument to a function and it is invoked after the function returns, the closure is escaping.

In Swift 2, you could mark a function parameter with the @noescape attribute, telling the compiler that the closure passed to the function is not allowed to escape the function body. Take a look at the following example. Note that this example is written in Swift 2.

import HealthKit

class HealthKitManager: NSObject {

    private let healthStore = HKHealthStore()

    func requestAuthorization(completion: (Bool, NSError?) -> Void) {
        var shareTypes = Set<HKSampleType>()
        var readTypes = Set<HKSampleType>()

        // Add Workout Type
        shareTypes.insert(HKSampleType.workoutType())
        readTypes.insert(HKSampleType.workoutType())

        // Request Authorization
        healthStore.requestAuthorizationToShareTypes(shareTypes, readTypes: readTypes, completion: completion)
    }

}

You can see that the requestAuthorization(_:) function accepts one parameter, a closure. In Swift 2, closures can by default escape the function body and that is why the above example doesn’t cause the compiler any issues. Note that the completion parameter is passed as an argument to the requestAuthorizationToShareTypes(_:readTypes:completion:) method of HKHealthStore. This method is asynchronous, which means the closure is invoked after requestAuthorization(_:) returns. In other words, the closure we pass to requestAuthorization(_:) is escaping. It escapes the function body of requestAuthorization(_:).

Things become interesting if we add the @noescape attribute to the parameter of requestAuthorization(_:). This is what the example looks like with the @noescape attribute.

import HealthKit

class HealthKitManager: NSObject {

    private let healthStore = HKHealthStore()

    func requestAuthorization(@noescape completion: (Bool, NSError?) -> Void) {
        var shareTypes = Set<HKSampleType>()
        var readTypes = Set<HKSampleType>()

        // Add Workout Type
        shareTypes.insert(HKSampleType.workoutType())
        readTypes.insert(HKSampleType.workoutType())

        // Request Authorization
        healthStore.requestAuthorizationToShareTypes(shareTypes, readTypes: readTypes, completion: completion)
    }

}

We explicitly tell the compiler that the completion parameter should not be allowed to escape the function body. The result is that the compiler throws an error, explaining what is wrong.

The @noescape Attribute in Swift 2
The @noescape Attribute in Swift 2

Swift 3

Let me show you what the above example looks like in Swift 3. This will show you what has changed and what you need to know about escaping closures in Swift 3.

import HealthKit

class HealthKitManager: NSObject {

    private let healthStore = HKHealthStore()

    func requestAuthorization(@noescape completion: (Bool, Error?) -> Void) {
        var shareTypes = Set<HKSampleType>()
        var readTypes = Set<HKSampleType>()

        // Add Workout Type
        shareTypes.insert(HKSampleType.workoutType())
        readTypes.insert(HKSampleType.workoutType())

        // Request Authorization
        healthStore.requestAuthorization(toShare: shareTypes, read: readTypes, completion: completion)
    }
    
}

The compiler immediately informs us that @noescape is the default in Swift 3 and suggests to remove @noescape. In fact, @noescape is deprecated and you should no longer use it in Swift 3.

The @noescape attribute is deprecated in Swift 3.
The @noescape attribute is deprecated in Swift 3.

Because the closure we pass to requestAuthorization(completion:) (note that the method signature is different in Swift 3) is escaping, we need to mark the parameter as escaping. We use a new attribute for this, @escaping. This is a direct result of SE–0103, a Swift evolution proposal that proposes to make non-escaping closures the default. This is a very welcome change if you ask me.

import HealthKit

class HealthKitManager: NSObject {

    private let healthStore = HKHealthStore()

    func requestAuthorization(completion: @escaping (Bool, Error?) -> Void) {
        var shareTypes = Set<HKSampleType>()
        var readTypes = Set<HKSampleType>()

        // Add Workout Type
        shareTypes.insert(HKSampleType.workoutType())
        readTypes.insert(HKSampleType.workoutType())

        // Request Authorization
        healthStore.requestAuthorization(toShare: shareTypes, read: readTypes, completion: completion)
    }
    
}
We need to mark the closure as escaping with the @escaping attribute to satisfy the compiler.
We need to mark the closure as escaping to satisfy the compiler.

You may have noticed that the @escaping attribute precedes the type of the parameter, not the name. This too is new in Swift 3.

What Is the Meaning of @escaping?

This brings us to the meaning of the @escaping attribute. Because closures are by default non-escaping in Swift 3, escaping closures need to be marked as such. And the @escaping attribute lets us do that.

The error of the compiler disappears by marking the closure as escaping, using the @escaping attribute.

Why Is This Important?

There are several benefits to make closures non-escaping by default. The most obvious benefits are performance and the ability for the compiler to optimize your code. If the compiler knows that a closure is non-escaping, it can take care of many of the nitty-gritty details of memory management.

This also means that you can use the self keyword without issues in non-escaping closures because the closure is invoked before the function returns. There is no need to use a weak reference to self in the closure. This is a nice benefit you get for free.

Questions? Leaven them in the comments below or reach out to me on Twitter.

Share This