Chapter 9: Closures

Swift Programming from Scratch

The book is updated for Swift 3 and the Swift Sandbox is integrated, making the exercises interactive. Read more about the book here.

  1. First Steps
  2. Conditionals
  3. Types
  4. Loops
  5. Strings
  6. Arrays
  7. Functions
  8. Recursion
  9. Closures
  10. Tuples & Enums
  11. Dictionaries

?You can buy Training app  +  the PDF and ePub versions of the book!

    cover final small     +     cover final small

Chapter 9: Closures

 

achievement9@2x

 

Closures are self contained chunks of code that can be passed around and used in your code. Closures can capture and store references to any constants or variables from the context in which they are defined. This is know as closing over those variables, hence the name closures. Closures are use intensively in the Cocoa frameworks – which are used to develop iOS or Mac applications.

Functions are a special kind of closures. There are three kinds of closures:

  • global functions – they have a name and cannot capture any values
  • nested functions – they have a name and can capture values from their enclosing functions
  • closure expressions – they don’t have a name and can capture values from their context

The thing to keep in mind for the moment is that you already have an intuition about closures. They are almost the same as functions but don’t necessarily have a name.

// a closure that has no parameters and return a String
var hello: () -> (String) = {
    return "Hello!"
}

hello() // Hello!

// a closure that take one Int and return an Int
var double: (Int) -> (Int) = { x in
    return 2 * x
}

double(2) // 4

// you can pass closures in your code, for example to other variables
var alsoDouble = double

alsoDouble(3) // 6

Remember the array sort method? There is similar one named sort(by:) that takes a closure as an parameter:

var numbers = [1, 4, 2, 5, 8, 3]

numbers.sort(by: <) // this will sort the array in ascending order
numbers.sort(by: >) // this will sort the array in descending order

The < and > operators are defined as functions, which can be referenced as closures. Here is an example for calling sort that uses a closure:

var numbers = [1, 4, 2, 5, 8, 3]

numbers.sort(by: { x, y in
    return x < y
})

print(numbers)
// [1, 2, 3, 4, 5, 8]

Declaring a closure

The general syntax for declaring closures is:

{ (parameters) -> return type in
statements
}

If the closure does not return any value you can omit the arrow (->) and the return type. This also applies to the case where the type of the closure can be infered.

{ (parameters) in
statements
}

Closures can use inout parameters, but cannot assign default values to any parameter. Also closure parameters cannot have external names.

Let’s look at some examples:

var noParameterAndNoReturnValue: () -> () = {
    print("Hello!")
}

var noParameterAndReturnValue: () -> (Int) = {
    return 1000
}

var oneParameterAndReturnValue: (Int) -> (Int) = { x in
    return x % 10
}

var multipleParametersAndReturnValue: (String, String) -> (String) = 
    { (first, second) -> String in
    return first + " " + second
}

The examples from above don’t declare the type of each parameter, if you do so you don’t need to state the return type of the closure because it can be inferred.

var noParameterAndNoReturnValue = {
    print("Hello!")
}

var noParameterAndReturnValue = { () -> Int in
    return 1000
}

var oneParameterAndReturnValue = { (x: Int) -> Int in
    return x % 10
}

var multipleParametersAndReturnValue = 
    { (first: String, second: String) -> String in
    return first + " " + second
}

Shorthand Parameter Names

Swift provides shorthand parameter names for closures. You can refer to the parameters as $0, $1, $2 and so on. To use shorthand parameter names ignore the first part of the declaration.

numbers.sort({ return $0 < $1 })

var double: (Int) -> (Int) = {
    return $0 * 2
}

var sum: (Int, Int) -> (Int) = {
    return $1 + $2
}

Capturing Values

In the beginning of the chapter I mentioned that closures can capture values. Let’s see what that means:

var number = 0

var addOne = {
    number += 1
}

var printNumber = {
    print(number)
}

printNumber() // 0
addOne() // number is 1
printNumber() // 1
addOne() // number is 2
addOne() // number is 3
addOne() // number is 4
printNumber() // 4

So a closure can remember the reference of a variable or constant from its context and use it when it’s called. In the example above the number variable is in the global context so it would have been destroyed only when the program would stop executing. Let’s look at another example, in which a closure captures a variable that is not in the global context:

func makeIterator(from start: Int, step: Int) -> () -> Int {
    var i = start
    return {
        let currentValue = i
        i += step
        return currentValue
    }
}

var iterator = makeIterator(from: 1, step: 1)

iterator() // 1
iterator() // 2
iterator() // 3

var anotherIterator = makeIterator(from: 1, step: 3)

anotherIterator() // 1
anotherIterator() // 4
anotherIterator() // 7
anotherIterator() // 10

Trailing Closure Syntax

If the last parameter of a function is a closure, you can write it after the function call.

numbers.sort { $0 < $1 }

func sum(from: Int, to: Int, f: (Int) -> (Int)) -> Int {
    var sum = 0
    for i in from...to {
        sum += f(i)
    }
    return sum
}

sum(from: 1, to: 10) {
    $0 
} // the sum of the first 10 numbers 

sum(from: 1, to: 10) {
    $0 * $0
} // the sum of the first 10 squares

Closures are reference types

Closures are reference types. This means that when you assign a closure to more than one variable they will refer to the same closure. This is different from value type which make a copy when you assign them to another variable or constant.

// a closure that take one Int and return an Int
var double: (Int) -> (Int) = { x in
    return 2 * x
}

double(2) // 4

// you can pass closures in your code, for example to other variables
var alsoDouble = double

alsoDouble(3) // 6

Implicit Return Values

Closures that have only one statement will return the result of that statement. To do that simply omit the returnkeyword.

array.sort { $0 < $1 }

Higher order functions

A higher order function is a function that does at least one of the following:

  • takes a function as input
  • outputs a function

Swift has three important higher order functions implemented for arrays: map, filter and reduce.

Map

Map transforms an array using a function.

[ x1, x2, ... , xn].map(f) -> [f(x1), f(x2), ... , f(xn)]

Let’s take as an example the problem of converting an array of numbers to an array of strings.

[1, 2, 3] -> ["1", "2", "3"]

One way of solving this problem would be to create an empty array of strings, iterate over the original array transforming each element and adding it to the new one.

var numbers = [1, 2, 3]

var strings: [String] = []

for number in numbers {
    strings.append("\(number)")
}

The other way of solving this problem is by using map:

var numbers = [1, 2, 3]

var strings = numbers.map { "\($0)" }

{ "\($0)" } is the closure we provided to solve this problem. It takes one parameter and converts it into a string using string interpolation.

The closure that we need to give to map take one parameter and will be called once for each of the elements from the array.

Filter

Filter selects the elements of an array which satisfy a certain condition.

For example let’s remove all the odd numbers from an array:

var numbers = [1, 2, 3, 4, 5, 6, 7, 8]

var evenNumbers = numbers.filter { $0 % 2 == 0 }
// evenNumbers = [2, 4, 6, 8]

Or remove all the even numbers:

var oddNumbers = numbers.filter { $0 % 2 == 1 }
// oddNumbers = [1, 3, 5, 7]

The closure that we need to give to map takes one parameter and will be called once for each of the elements from the array. It should return a Bool value, if it’s true the element will be copied into the new array otherwise no.

Reduce

Reduce combines all the values from an array into a single value.

For example we can reduce an array of numbers to their sum.

var numbers = [1, 2, 3, 4, 5]

var sum = numbers.reduce(0) { $0 + $1 } // 15

Reduce take two parameters, an initial value and a closure that will be used to combine the elements of the array. The closure provided to reduce takes two parameters, the first one is the partial result and the second one will be an element from the array. The closure will be called for each element once. In the sum example we started the sum from 0 and the closure added the partial sum with each element.

Here is another cool way in which we can use the fact that Swift operators are implemented as functions:

var numbers = [1, 2, 3, 4, 5]

var sum = numbers.reduce(0, +) // 15

9.1 K Times

Write a function named applyKTimes that takes an integer K and a closure and calls the closure K times. The closure will not take any parameters and will not have a return value.

Function Definition

func applyKTimes(_ K: Int, _ closure: () -> ())

[collapse]
Example 1

Function call:

applyKTimes(3) { 
    print("We Heart Swift")
}

Output:

We Heart Swift
We Heart Swift
We Heart Swift

[collapse]
Example 2

Function call:

applyKTimes(2) { 
    print("Tic")
    print("Tac")
}

Output:

Tic
Tac
Tic
Tac

[collapse]
Hint

Remember that you can call a closure just like a regular function.

[collapse]
Solution

func applyKTimes(_ K: Int, _ closure: () -> ()) {
    for _ in 1...K {
        closure()
    }
}

[collapse]

9.2 Div3

Use filter to create an array called multiples that contains all the multiples of 3 from numbers and then print it.

Example 1

Input:

var numbers = [1, 2, 3, 4, 6, 8, 9, 3, 12, 11]

Expected values:

multiples = [3, 6, 9, 3, 12]

[collapse]
Example 2

Input:

var numbers = [3, 3, 27, 3, 99]

Expected values:

multiples = [3, 3, 27, 3, 99]

[collapse]
Example 3

Input:

var numbers = [2, 4, 8, 16, 32, 128, 1]

Expected values:

multiples = []

[collapse]
Hint

Think of the condition the numbers have to satisfy so that they’re divisible by3.

[collapse]
Solution

let numbers = [1, 2, 3, 4, 6, 8, 9, 3, 12, 11]

let multiples = numbers.filter { $0 % 3 == 0 }

print(multiples)

[collapse]

9.3 Max

Find the largest number from numbers and then print it. Use reduce to solve this exercise.

This content is restricted to buyers of Online Exercise Platform.

Example

Input:

var numbers = [4, 7, 1, 9, 6, 5, 6, 9]

Output:

9

[collapse]
Hint 1

What would be the initial value for our reduce function?

[collapse]
Hint 2

How can you “combine” 2 numbers to get the maximum between them?

[collapse]
Solution

let numbers = [4, 7, 1, 9, 6, 5, 6, 9]

let max = numbers.reduce(numbers[0]) {
    if $0 > $1 {
        return $0
    } else {
        return $1
    }
}

print(max) // 9

[collapse]

9.4 Join

Join all the strings from strings into one using reduce. Add spaces in between strings. Print your result.

This content is restricted to buyers of Online Exercise Platform.

Example 1

Input:

var strings = ["We", "Heart", "Swift"]

Output:

"We Heart Swift"

[collapse]
Example 2

Input:

var strings = ["lonely"]

Output:

"lonely"

[collapse]
Hint 1

What would be the initial value for our reduce function?

[collapse]
Hint 2

How can you combine the strings so that they have spaces between them?

[collapse]
Solution

let strings = ["We", "Heart", "Swift"]

let string = strings.reduce("") {
    if $0 == "" {
        return $1
    } else {
        return $0 + " " + $1
    }
}

print(string)

[collapse]

9.5 Sorting

Sort numbers in ascending order by the number of divisors. If two numbers have the same number of divisors the order in which they appear in the sorted array does not matter.

This content is restricted to buyers of Online Exercise Platform.

Example

Input:

var numbers = [1, 2, 3, 4, 5, 6]

Expected values:

numbers = [1, 2, 3, 5, 4, 6]
// 1 has one divisor
// 2, 3 and 5 have 2
// 4 has 3 divisors
// 6 has 4 divisors

// [1, 5, 2, 3, 4, 6] would also have been a valid solution 

[collapse]
Hint

You’ll have to pass a closure that tells you how the numbers should be compared.

[collapse]
Solution

var numbers = [1, 2, 3, 4, 5, 6]

numbers.sort(by: { x, y in
    func countDivisors(_ number: Int) -> Int {
        var count = 0
        for i in 1...number {
            if number % i == 0 {
                count += 1
            }
        }
        return count
    }
    return countDivisors(x) < countDivisors(y)
})

[collapse]

9.6 Chains

Find the sum of the squares of all the odd numbers from numbers and then print it. Use map, filter and reduce to solve this problem.

This content is restricted to buyers of Online Exercise Platform.

Example 1

Input:

var numbers = [1, 2, 3, 4, 5, 6]

Output:

25 // 1 + 9 + 25 -> 25

[collapse]
Example 2

Input:

var numbers = [2, 4, 6]

Output:

0 // none of the numbers are odd

[collapse]
Hint

The order in which you apply the map, filter and reduce operations is important.

[collapse]
Solution

var numbers = [1, 2, 3, 4, 5, 6]

let sum = numbers.filter {
        $0 % 2 == 1 //select all the odd numbers
    }.map {
        $0 * $0 // square them
    }.reduce(0, +) // get their sum

print(sum)

[collapse]

9.7 For each

Implement a function forEach(array: [Int], _ closure: Int -> ()) that takes an array of integers and a closure and runs the closure for each element of the array.

This content is restricted to buyers of Online Exercise Platform.

Example Usage

var array = [1,2,3,4]
forEach(array) {
    print($0 + 1)
}
// This will be printed:
// 2
// 3
// 4
// 5

[collapse]
Function Definition

func forEach(_ array: [Int], _ closure: (Int) -> ())

[collapse]
Example 1

Function input:

forEach([1, 2, 3, 4]) {
    print($0 + 1)
}

Output:

2
3
4
5

[collapse]
Example 2

Function input:

forEach([1, 2, 3, 4]) {
    print($0 * $0)
}

Output:

1
4
9
16

[collapse]
Solution

func forEach(_ array: [Int], _ closure: (Int) -> ()) {
    for number in array {
        closure(number)
    }
}

[collapse]

9.8 Combine arrays

Implement a function combineArrays that takes 2 arrays and a closure that combines 2 Ints into a single Int. The function combines the two arrays into a single array using the provided closure.
Assume that the 2 arrays have equal length.

This content is restricted to buyers of Online Exercise Platform.

Function Definition

func combineArrays(_ array1: [Int], 
           _ array2: [Int], 
           _ closure: (Int,Int) -> Int) -> [Int]

[collapse]
Example 1

Function input:

var array1 = [1,2,3,4]
var array2 = [5,5,5,3]
combineArrays(array1,array2) {
    $0 * $1
}

Function output:

[5,10,15,12]

[collapse]
Example 2

Function input:

var array1 = [5,14,77,12]
var array2 = [1,5,3,13]
combineArrays(array1,array2) {
    return max($0,$1)
}

Function output:

[5,14,77,13]

[collapse]
Hint

You’ll have to iterate both arrays at the same time using an index.

[collapse]
Solution

func combineArrays(_ array1: [Int], 
                   _ array2: [Int], 
                   _ closure: (Int,Int) -> Int) -> [Int] {
    var result: [Int] = []
    for i in 0..<array1.count {
        result.append(closure(array1[i],array2[i]))
    }
    return result
}

[collapse]

 

Swift Programming from Scratch

Read more about the book here.

  1. First Steps
  2. Conditionals
  3. Types
  4. Loops
  5. Strings
  6. Arrays
  7. Functions
  8. Recursion
  9. Closures
  10. Tuples & Enums
  11. Dictionaries

?You can buy Training app  +  the PDF and ePub versions of the book!

    cover final small     +     cover final small

 

Feel free to ask any questions in the comments bellow. :)

1 Star2 Stars3 Stars4 Stars5 Stars (6 votes, average: 5.00 out of 5)
Loading...

  10 comments for “Chapter 9: Closures

  1. March 21, 2015 at 11:54 pm

    Fun!

    Here’s my solution to 9.5:

    “`var numbers = [1, 2, 3, 4, 5, 6]

    numbers.sort({ x, y in

    func factors(n: Int) -> [Int] {
    return filter(1…n) { n % $0 == 0 }
    }

    return factors(x).count < factors(y).count

    })“`

  2. Josh
    September 3, 2015 at 8:12 pm

    var sum: (Int, Int) -> (Int) = {
    return $1 + $2
    }

    in xcode 7 beta 6 seeing this error:
    Tuple types ‘(Int, Int’ and ‘(_,_,_)’ have a different number of elements (2 vs 3)

    • September 3, 2015 at 8:15 pm

      { return $0 + $1 } wont give you that error. $0 is the first unnamed parameter. read the part title “Shorthand Parameter Names” from this chapter

  3. steve
    December 8, 2015 at 6:22 pm

    To find the largest Int in an array try this instead.

    let result = numbers.reduce(0){ max($0, $1) }

    • December 9, 2015 at 8:30 pm

      max is already a closure that takes two ints -> let result = numbers.reduce(0, combine: max)

  4. July 7, 2016 at 3:27 pm

    9.7 I used reduce

    func forEach(array: [Int], withClosure closure: Int -> ()){
    array.reduce(array[0], combine: {
    closure($1)
    return $1
    })
    }

  5. Mohamed
    August 25, 2016 at 1:11 pm

    Ever since learning ObjC & Swift, I had problem with blocks and closures.

    Not anymore. This was pure gold!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe
We send about one email per week with our latest tutorials and updates
Never display this again :)