Notice: Undefined index: understanding-swift in /var/sites/hackingwithswift.com/site/src/Controller/NewBooksReadController.php on line 89
Returning closures from functions - a free tutorial

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

Returning closures from functions

Paul Hudson    @twostraws   

Functions can return strings, integers, arrays, and more, and because Swift lets us use closures anywhere we can also return closures from functions. But why would you want to do this – is it actually a reasonable thing to want to do?

The most common situation is effectively this: I need a function to call, but I don’t know what that function is. I know how to find out that function – I know who to ask to find the function – but I don’t know myself.

For example, I want to generate lots of random numbers, but I don’t know what algorithm to use. All I know is that if I call makeRandomGenerator() I’ll get back a function I can call that will generate a new random number every time I call it.

This behavior means we don’t need to know what the actual random number generator does, or how it was created. Behind the scenes we might get sent the same closure each time or perhaps a different closure; we don’t care. Without this, we’d need to type the precise name of the function everywhere we wanted to use it.

That leads to a second benefit: we can change our mind whenever we want, just by changing the function that creates the random number generator. All the places that call the function won’t change, because they still call makeRandomGenerator(). And all the ways they use the returned random number generator won’t change, because it’s just a closure that somehow returns a random number every time it’s called.

So, if you want to change the generator to a “fair” algorithm – where each number is generated an equal number of times, in a random order – then you just change the returned closure in makeRandomGenerator(). Or if you want to change it to have a Gaussian distribution, which means you get a bell curve where numbers in the middle of your range are returned more commonly than numbers towards the end of your range, then again you just change the returned closure – everywhere you use it remains absolutely unchanged.

I want to give you a little example of how this looks, because Swift actually comes with a built-in random number generator. It looks like this:

print(Int.random(in: 1...10))

That will print a number from 1 through 10.

If we wanted to write a function that returned one random number between 1 and 10, we would write this:

func getRandomNumber()-> Int {
    Int.random(in: 1...10)
}

That will return a random integer every time it’s called, so we can use it like this:

print(getRandomNumber())

Now let’s go a step further: we’re going to make a function that returns a closure that, when called, will generate a random number from 1 through 10:

func makeRandomGenerator() -> () -> Int {
    let function = { Int.random(in: 1...10) }
    return function
}

Notice that our return type is now () -> Int, which means “a closure that takes no parameters and returns an integer.” Then, inside the function, we create a closure that wraps Int.random(in: 1...10) and send back that closure.

We can now use makeRandomGenerator() like this:

let generator = makeRandomGenerator()
let random1 = generator()
print(random1)

Again, the flexibility here is that all our code can call makeRandomGenerator() to be sent back some sort of code that can generate a random number. It doesn’t have to care what it does; we only care that it generates a new number every time it’s called.

If you’re finding closure syntax hard to remember, you’ll be pleased to know there’s a site just for you: http://goshdarnclosuresyntax.com/

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.