Skip to content

Latest commit

 

History

History
302 lines (198 loc) · 7.81 KB

File metadata and controls

302 lines (198 loc) · 7.81 KB

Closures and Callbacks

Agenda

  • Intro to course
  • Learning Objectives
  • Closures
  • Trailing Closures
  • Capturing values
  • Escaping and Non escaping

Learning objectives

  1. Identify use cases of closures
  2. Know the difference between escaping and non escaping closures
  3. Implement closures as completion handlers
  4. Use closures to pass data between View Controllers

Initial Exercise

  1. Brainstorm everything you know about closures.

What is a closure?

Self contained blocks of functionality that can be passed around and used in your code.

Basically blocks of code that we can assign to a variable, pass around in our code, then called so they can execute their code.
let greeting = {
    print("Welcome back!")
}

greeting()
We define a closure and assign it to a constant. The body of the closure is between curly brackets.

Then it gets called by calling the name of the constant with ().

let greeting:(String) -> () = { name in
    print("Welcome back, \(name)!")
}

greeting("Tahoe 🐶")
Same as previous example. The difference is that now we have one parameter of type String. We can use the parameter inside the closure by assigning a local variable "name" to the first parameter of the closure.

The keyword "in" separated the closure parameters and the body.

We can give parameters any name we want to use or leave them blank to use shorthand values.

When we call the closure, it will need a value for such parameter.

let greeting:(String) -> () = {
    print("Welcome back, \($0)!")
}

greeting("Tahoe 🐶")

Review practice

Trailing closures

We can send closures as parameters to functions. If the closure happens to be the last parameter sent, we can use trailing closure syntax

func washHands(action:()->()) {
    print("Get some soap 🧼")
    action()
    print("Hands are clean. 🙌🏼")
}

Can be called:

washHands() {
    print("Singing for 20 seconds 🎤")
}
The closure is written outside of the function call parentheses, this is known as trailing closure. Its purpose is readability (specially for long closures), so it is recommended to use when a function accepts a closure as a final argument.

Capturing values

Closures have a distinct feature that makes them stand out: they can capture values from their surrounding context.

From Apple's documentation:

A closure can capture constants and variables from the surrounding context in which it is defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.

Variables, functions and closures have a scope. This scope determines if we can access any of them. Simply if a variable, function or closure isn't in a scope, we can access it.
let location = "San Francisco"
let explore = {
    print("Exploring \(location)")
}

explore()
The closure assigned to "explore" closes over the local variable "location" since it is available in the scope that the closure is defined in. We can access the constant without declaring it locally inside the closure.

Here's another example of closures closing over variables.

func addScore(_ points: Int) -> Int
{
    let score = 42
    let calculate = {
        return score + points
    }
    return calculate()
}

let value = addScore(11)
print(value)

Trailing Closure Practice

Self Study

<iframe src="https://youtube.com/embed/tRK0sGymo24" data-autoplay width="700" height="500"></iframe> <iframe src="https://youtube.com/embed/6nN2YFQQY4A" data-autoplay width="700" height="500"></iframe>

Escaping and non escaping closures

Closures come in two different variants - escaping and non-escaping.

When a closure is escaping it means that the closure will outlive, or leave the scope that you’ve passed it to.

Non-escaping closures are called within the function it was passed into.

An example of an escaping closure is a completion handler. It’s executed in the future, after a lengthy task completes, so it outlives the function it was created in.

In an escaping closure, the lifecycle looks like this:

  1. Passing the closure to a function as argument
  2. Function performs a task
  3. Return compiler back
  4. The closure runs asynchronously
This means that the closure outlives the function (it is called after the function has returned).

By default, all closures are non escaping. This helps with memory management.

escaping

In a non escaping closure, the lifecycle looks like this:

  1. Passing the closure to a function as argument
  2. Function performs a task
  3. Running the closure
  4. Return compiler back
A non-escaping closure does not outlive the function from where it was called.

nonescaping

Completion handler practice

Completion Handlers

Download this working example and look at the code to find out how closures are being used to pass data between view controllers

Identify:

  • Q: What does the app do?
  • Q: Where are closures being used?

Closures Practice using HOF

Additional Resources

  1. Closing over - diagram and article
  2. Completion Handler
  3. Completion Handlers - article
  4. Capture lists - article
  5. Capturing with closures - article
  6. Completion handlers - article
  7. All on closures - article with examples