Nonescaping and Escaping Closures L4.6.1

Nonescaping Closures

By default, when a closure is passed into a function it is considered nonescaping. A nonescaping closure can only be used within the body of the function and nowhere else. To simplify, a nonescaping closure is trapped inside the body of a function.

func performThisClosure(closure: (Void) -> Void) {  
    closure() // by default, this closure is nonescaping, it's trapped inside this function.
}

The function below printItNow takes a closure parameter called it. The it closure is only used within the body of the function.

func printItNow(it: (Void) -> Void) {  
    it() // the `it` closure never escapes the body of the function
}

If we call the function printItNow, then the closure executes immediately and outputs "print me now!".

printItNow {  
    print("print me now!")
}

Escaping Closures

There are cases like asynchronous control flow when a closure should be able to escape the body of the function to which it is passed. When this happens, we must specify a closure parameter as escaping.

var somethingToDo: (Void) -> Void = {}

func doItLater(it: @escaping (Void) -> Void) {  
    // keep a reference to `it` so we can call it later...
    somethingToDo = it
}

The function doItLater takes a closure parameter also called it. In the body of doItLater, the it closure is saved so that it can be used at a later time. This forces us to specify it as escaping (by using the @escaping syntax) because it can be called after doItLater finishes executing.

var somethingToDo: (Void) -> Void = {}

func doItLater(it: @escaping (Void) -> Void) {  
    // keep a reference to `it` so we can call it later...
    somethingToDo = it
}

// calling `doItLater` only keeps a reference to the closure
doItLater {  
    print("print me later...")
}

// here is where we call the closure (after doItLater has finished) and "print me later..." is output
somethingToDo()  

Why Escaping Closures?

When a closure parameter needs to escape the body of a function, it must explicitly be declared as escaping by using the @escaping syntax. This special marking for closure parameters is primarily a mechanism for improving performance and optimizing memory allocation for Swift.

Using Escaping Closures for Asynchronous Tasks

Say we want to write a function that downloads an image over the network. When the function completes, we want to update the interface to display the newly downloaded image.

It might look something like this.

func downloadAndDisplayImage(imageURL: URL, updateImage: @escaping (UIImage?) -> Void) {

    // create network request
    let task = URLSession.shared.dataTask(with: myImageURL) { (data, response, error) in

        // if no error, then create image and pass it to the completion handler
        if let downloadedImage = UIImage(data: data!), error == nil {
            DispatchQueue.main.async {
                updateImage(downloadedImage)
            }
        } else {
            // otherwise, pass nil to the completion handler
            DispatchQueue.main.async {
                updateImage(nil)
            }
        }
    }

    task.resume()
}

The updateImage parameter is a closure used to update a hypothetical interface with the downloaded image. It is specified as an escaping closure. This is because the updateImage closure needs to outlive the downloadAndDisplayImage function.

downloadAndDisplayImage(imageURL: myImageURL) { downloadedImage in  
    // after network request finishes, update interface with downloadImage
}

When downloadAndDisplayImage is called it kicks off a network request and the function quickly exits. However, the closure we pass as a parameter still needs to be called once the network request finishes which happens at an indeterminate amount of time after downloadAndDisplayImage exits. Therefore, the closure will be used outside of the body of the original function (after the network request finishes) and should be declared as escaping.