Updated for Xcode 15
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/
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!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.