Notice: Undefined index: understanding-swift in /var/sites/hackingwithswift.com/site/src/Controller/NewBooksReadController.php on line 89
When would you use closures with return values as parameters to a function? - a free tutorial

NEW: Subscribe to Hacking with Swift+ and accelerate your learning! >>

When would you use closures with return values as parameters to a function?

Paul Hudson    @twostraws   

Swift’s closures can return values as well as take parameters, and you can use those closures in functions. Even better, those functions can also return values, but it’s easy for your brain to get a bit fuzzy here because there’s a lot of syntax.

To demonstrate a common usage for this kind of closures, we’re going to implement a simple reducer method. This is a common behavior that is designed to summarize arrays – to take a lot of numbers, or strings, or whatever, and boil them down to a single value.

In our simplified example, our reducer will accept two parameters: an array of numbers, and a closure that can reduce that array down to a single value. For example, it might accept an array of numbers and add them together, then return the final total. To do that, the closure will accept two parameters: one to track the current value (everything that been reduced so far), and the current value that needs to be added to the reduced value. The closure will also return a value, which is the new reduced value, and the whole function will return the fully reduced value – the total of all numbers, for example.

For example, if we wanted to reduce the array [10, 20, 30], it would work something like this:

  • It would create a variable called current with a value set to the first item in its array. This is our starting value.
  • It would then start a loop over the items in the array that got passed in, using the range 1... so that it counts from index 1 to the end.
  • It would then call the closure with 10 (the current value) and 20 (the second value in the array).
  • The closure might be reducing the array using addition, so it would add 10 to 20 and return the sum, 30.
  • Our function would then call the closure with 30 (the new current value) and 30 (the third item in the array).
  • The closure would add 30 to 30 and return the sum, which is 60.
  • Our function would then send back 60 as its return value.

In code it looks like this:

func reduce(_ values: [Int], using closure: (Int, Int) -> Int) -> Int {
    // start with a total equal to the first value    
    var current = values[0]

    // loop over all the values in the array, counting from index 1 onwards
    for value in values[1...] {
        // call our closure with the current value and the array element, assigning its result to our current value
        current = closure(current, value)
    }

    // send back the final current value    
    return current
}

With that code in place, we can now write this so add up an array of numbers:

let numbers = [10, 20, 30]

let sum = reduce(numbers) { (runningTotal: Int, next: Int) in
    runningTotal + next
}

print(sum)

The great thing here is that reduce() doesn’t care what its closure does – it only cares that it will accept two integers and return one integer. So, we could multiply all the items in our array like this:

let multiplied = reduce(numbers) { (runningTotal: Int, next: Int) in
    runningTotal * next
}

Although this was just an example to demonstrate why closures with return values make useful functional parameters, I want to mention three more things.

First, our reduce() function uses values[0] for its initial value, but what happens if we call reduce() with an empty array? We get a crash – that’s happens. Clearly that isn’t a good thing, so you wouldn’t want to use this code outside of a learning sandbox.

Second, I mentioned previously that Swift’s operators are actually functions in their own right. For example, + is a function that accepts two numbers (e.g. 5 and 10) and returns another number (15).

So, + takes two numbers and returns a number. And our reduce() function expects a closure that takes two numbers and returns a number. That’s the same thing! The + function fulfills the same contract as reduce() wants – it takes the same parameters and returns the same value.

As a result, we can actually write this:

let sum = reduce(numbers, using: +)

Yes, really. Neat, huh?

Third, this reduce() function is so important that a variant actually comes with Swift as standard. The concept is the same, but it’s more advanced in several ways:

  • You can specify your own starting value.
  • It will work on arrays of any type, including strings.
  • It handles errors gracefully.

Even better, it won’t crash when used on an empty array!

This took quite a bit of explaining, but I hope it’s given you a practical example of why closures that return values can be useful as parameters. As you progress in your skills you’ll learn many more examples – it’s surprisingly common.

Hacking with Swift is sponsored by Instabug

SPONSORED Are you tired of wasting time debugging your Swift app? Instabug’s SDK is here to help you minimize debugging time by providing you with complete device details, network logs, and reproduction steps with every bug report. All data is attached automatically, and it only takes a line of code to setup. Start your free trial now and get 3 months off exclusively for the Hacking with Swift Community.

Start your free trial!

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Link copied to your pasteboard.