UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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

Paul Hudson    @twostraws   

Updated for Xcode 15

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)

Tip: In that code we’re explicit that runningTotal and next will both be integers, but we can actually leave out the type annotation and Swift will figure it out. Notice that we haven’t had to say our closure returns an integer, again because Swift can figure that out for itself.

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 what 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.

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

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

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI 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 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 Beyond Code

Was this page useful? Let us know!

Average rating: 4.2/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.