Skip to main content
Noble Desktop Publishing Team/6 min read

Closures

What This Tutorial Covers

Closure Syntax

Anonymous functions with capture semantics.

Trailing Closures

Cleaner syntax when the closure is the last argument.

Capturing Values

Closures capture references from their context.

Build Programming Foundations at Noble Desktop

Noble Desktop's Full-Stack Web Development Certificate teaches programming fundamentals that transfer across mobile, web, and desktop development.

Learn how to simplify your iOS development code with closures in this comprehensive tutorial, including how to define a closure, use closures with parameters and returned values, and utilize closures for callbacks.

Topics Covered in This IOS Development Tutorial:

Defining a Closure, Closures with Parameters & Returned Values, Closures Used for Callbacks

Exercise Overview

In this exercise you’ll learn how to pass around self-contained functions called closures in your code. Closures are basically functions without a full declaration and name—they’re anonymous. You can do many of the same things that you can do with a function, from passing arguments to getting return values. We’ll look at how using closures can shorten and simplify our code.

Getting Started

  1. Launch Xcode if it isn’t already open.

  2. If you completed the previous exercise, Car.xcodeproj should still be open. If you closed it, re-open it now.

  3. We suggest you complete the previous exercises (4A–4B) before doing this one. If you did not do the previous exercises, complete the following:

    • Go to File > Open.
    • Navigate to Desktop > Class Files > yourname-iOS App Dev 1 Class > Car Protocols Done and double–click on Car.xcodeproj.

Closures with Returned Values

We want to calculate the amount of money owed for gas used in the car. Using what you’ve learned so far, you could write a function to return the amount of gas:

func calculateGas(gallons: Int, price: Int) -> Int {
   return gallons * price
}

var amountOwedForGas = calculateGas(5, price: 3)

But there is a shorter way of doing this. You can write an inline closure, rather than having a separate function.

  1. In the Project Navigator, go to ViewController.swift.

  2. Inside viewDidLoad, around line 21, write a closure that will return the amount owed for gas (shown in bold):

    mustang.delegate = self
       mustang.start()
    
       var amountOwedForGas = {
    
       }
    }

    Closures are written as a function type, with parameters and a return type followed by the keyword in as shown below. The in keyword indicates that the definition of the closure’s parameters and return type is done, and that the body of the closure is about to begin.

    {(parameters) -> returnType in
       statements
    }
  3. Within the curly braces { } create a function:

    var amountOwedForGas = {(gallons: Int, price: Int) -> Int in
    
    }

    This is the equivalent of the calculateGas example above, but without having to declare the function name.

  4. Let’s assign the return of our anonymous function back to the amountowedForGas variable. Add the following statement as shown below:

    var amountOwedForGas = {(gallons: Int, price: Int) -> Int in
       return gallons * price
    }
  5. You can now use the variable as a function, and pass values into it. Add the print function as shown below:

    var amountOwedForGas = {(gallons: Int, price: Int) -> Int in
       return gallons * price
    }
    
    print("The amount you owe for gas is \(amountOwedForGas(5,3))")

    Let’s test out our code so far.

  6. At the top right, click the Show the Debug area button show hide debug area if it’s not already showing.

  7. At the top left of Xcode, click the Run button run icon.

    The simulator will take a moment to load. At this point, it will just be a blank white screen.

  8. Go back to the main Xcode window. Take a look at the Debug area to see the last message says: The amount you owe for gas is 15

    Our calculation worked!

Closures Used for Callbacks

There may be times when you want to have a method send back a note about whether something has succeeded or failed—this is known as a callback.

A callback is an asynchronous function that can be passed into another function as an argument, to be executed at a later time. This is usually done to define a behavior to be completed when a certain task is done. Similarly to how you’ve previously used delegates, you can use closures to get callbacks from functions as well.

  1. Let’s create a function in our Car class. In the Project Navigator, select Car.swift.

  2. At the bottom of the Car class, around line 56, add a function declaration for accelerateTo with the parameter for speed:

    func gasUsed(milesDriven: Float) -> Float {
       let gas = milesDriven / mpg!
       return gas
    }
    
    func accelerateTo(speed: Int) {
    
    }
  3. Now add in the callback:

    func accelerateTo(speed: Int, success:() -> (), fail: () -> ()) {
    
    }

    Let’s break this down: The parameter name is the callback name. The success and fail parameters have a return function with an undefined value type.

    NOTE: If a function does not define a return data type, we indicate this using empty parentheses ().

  4. We want the accelerateTo function to inform us if it has succeeded or failed. Write an if-else statement inside the new function:

    func accelerateTo(speed: Int, success:() -> (), fail: () -> ()) {
    
       if speed >= 20 {
          success()
       } else {
          fail()
       }
    
    }

    When you tell the car to accelerate, if its speed is greater than or equal to 20, the success method will be called. If the speed is less than 20, the fail method will be called.

  5. Now let’s add the accelerateTo method to our viewDidLoad method in our viewController. In the Project Navigator, go to ViewController.swift.

  6. As shown in bold below, add:

    print("The amount you owe for gas is \(amountowedForGas(5,3))")
    
       mustang.accelerateTo(speed: 20, 
          success: {() -> () in
          }, fail: {() -> () in
       })
    }
  7. Add print functions to log succeeded or failed messages:

    mustang.accelerateTo(speed: 20, 
       success: {() -> () in
          print("We have succeeded")
       }, fail: {() -> () in
          print("We have failed")
    })

    We are passing in closures to the Car class, which accepts them as functions and creates a callback based on whether the condition succeeded or failed.

  8. Time to test our code! At the top right, click the Show the Debug area button show hide debug area if it’s not already showing.

  9. At the top left of Xcode, click the Stop stop icon button.

  10. Click the Run button run icon.

    The simulator will take a moment to load. At this point, it is still a blank white screen.

  11. Go back to the main Xcode window. Take a look at the Debug area to see the last message says: We have succeeded

    The success method runs because you set the speed value to 20 in the viewDidLoad method.

  12. Let’s see what happens if you change the value. In ViewController.swift around line 27, change 20 to 10:

    mustang.accelerateTo(speed: 10, 
  13. Click the Stop button stop icon.

  14. Click the Run button run icon.

  15. Take a look at the Debug area of Xcode to see the last message now says:

    We have failed

    The fail method runs because we set the speed value to 10 in the viewDidLoad method. Remember that earlier we specified that if the speed is less than 20, the fail method will be called.

  16. Save and close the file. Keep Xcode open, as we’ll use it in the next exercise.