Understanding closure syntax in Swift

Understanding closure syntax in Swift
Photo by Dan Burton / Unsplash

If you're just getting started with the Swift programming language, you may have read or heard the word closure getting thrown around a lot.

If you've spent some time working with iterators like map, you've already been using them. e.g.

// define an array
let someArray = [7,3,2,6,4,8,3,9]

// add one to each element
someArray.map({$0 + 1})

// print out a concatenated string
print(someArray.map({"\($0) cakes"}))

// perform other operations
let otherArray = someArray.map({ item in
    (7*item + 4) * 3
})

This concise writing style can often cloud what is going on behind the scenes, so in this short post, we'll work through an example to motivate how we got to this abbreviated style.

Let's take a simple example of a function written in Swift, that takes as input two integers numberA and numberB, and a callback operation.

func calculator(numberA: Int, numberB: Int, operation: (Int, Int) -> Int) -> Int {
    return operation(numberA, numberB)
}

When writing functions that take another function (or callback) as an argument, rather than writing the expected argument as an explicit named function

func addOperation(nA: Int, nB: Int) -> Int {
    return nA + nB
}

and calling it like

calculator(numberA: 8, numberB: 3, operation: addOperation)

we can pass it as an anonymous function (or closure) instead

calculator(numberA: 8, numberB: 3, operation: { (nA: Int, nB: Int) -> Int in
    return nA + nB
})

We can now iterate on this first pass, and simplify in a number of ways.

  1. We can remove the unnecessary return statement
calculator(numberA: 8, numberB: 3, operation: { (nA: Int, nB: Int) -> Int in
    nA + nB
})
  1. Drop the closure argument label
calculator(numberA: 8, numberB: 3) { (nA: Int, nB: Int) -> Int in
    nA + nB
}
  1. Allow swift to infer the types
calculator(numberA: 8, numberB: 3) { (nA, nB) -> Int in
    nA + nB
}
calculator(numberA: 8, numberB: 3) { (nA, nB) in
    nA + nB
}
calculator(numberA: 8, numberB: 3) { (nA, nB) in nA + nB }
  1. Finally we can use anonymous arguments to rewrite
calculator(numberA: 8, numberB: 3) { (nA, nB) in nA + nB }

more concisely as

calculator(numberA: 8, numberB: 3) { $0 + $1 }

From this point, we can see how this can easily be extended to other operations

calculator(numberA: 6, numberB: 5) { $0 - $1 }
calculator(numberA: 9, numberB: 23) { $0 * $1 }
calculator(numberA: 15, numberB: 4) { $0 / $1 }
Mark Sindoni Houghton

Mark Sindoni Houghton

SoonCall founder & app developer
Trento, Italy