Day 07
Day 07 êŽë š
Closures, part two
Yesterday you learned the basics of closures, but today things get a little trickier. But thatâs OK: Benjamin Franklin once said, âenergy and persistence conquer all thingsâ â you can do this!
Sometimes closure syntax can be a bit hard on your eyes. If you find it a bit overwhelming â if youâre staring at some code and arenât 100% sure of what it means â just go back one video and watch it again.
Today you have seven one-minute videos to watch, and youâll learn about how closures accept parameters and return values. Once youâve completed each video you can go over any optional reading if you need it, then take a short test to help make sure youâve understood what was taught.
To make things a little less intimidating, Iâve split up todayâs topics into two sections: the least you need to know to continue, and more advanced topics that will really develop your learning. Keep in mind that Iâve already said closures are hard, so I hope you can appreciate that more advanced closures are doubly hard â donât feel bad if you try the more advanced topics and think youâve had enough and want to move on!
The least you need to know
1. Using closures as parameters when they accept parameters
1. Using closures as parameters when they accept parameters
This is where closures can start to be read a bit like line noise: a closure you pass into a function can also accept its own parameters.
Weâve been using () -> Void
to mean âaccepts no parameters and returns nothingâ, but you can go ahead and fill the ()
with the types of any parameters that your closure should accept.
To demonstrate this, we can write a travel()
function that accepts a closure as its only parameter, and that closure in turn accepts a string:
func travel(action: (String) -> Void) {
print("I'm getting ready to go.")
action("London")
print("I arrived!")
}
Now when we call travel()
using trailing closure syntax, our closure code is required to accept a string:
travel { (place: String) in
print("I'm going to \(place) in my car")
}
1. Using closures as parameters when they accept parameters - Additional
2. Using closures as parameters when they return values
2. Using closures as parameters when they return values
Weâve been using () -> Void
to mean âaccepts no parameters and returns nothingâ, but you can replace that Void
with any type of data to force the closure to return a value.
To demonstrate this, we can write a travel()
function that accepts a closure as its only parameter, and that closure in turn accepts a string and returns a string:
func travel(action: (String) -> String) {
print("I'm getting ready to go.")
let description = action("London")
print(description)
print("I arrived!")
}
Now when we call travel()
using trailing closure syntax, our closure code is required to accept a string and return a string:
travel { (place: String) -> String in
return "I'm going to \(place) in my car"
}
2. Using closures as parameters when they return values - Additional
3. Shorthand parameter names
3. Shorthand parameter names
We just made a travel()
function. It accepts one parameter, which is a closure that itself accepts one parameter and returns a string. That closure is then run between two calls to print()
.
Hereâs that in code:
func travel(action: (String) -> String) {
print("I'm getting ready to go.")
let description = action("London")
print(description)
print("I arrived!")
}
We can call travel()
using something like this:
travel { (place: String) -> String in
return "I'm going to \(place) in my car"
}
However, Swift knows the parameter to that closure must be a string, so we can remove it:
travel { place -> String in
return "I'm going to \(place) in my car"
}
It also knows the closure must return a string, so we can remove that:
travel { place in
return "I'm going to \(place) in my car"
}
As the closure only has one line of code that must be the one that returns the value, so Swift lets us remove the return
keyword too:
travel { place in
"I'm going to \(place) in my car"
}
Swift has a shorthand syntax that lets you go even shorter. Rather than writing place in
we can let Swift provide automatic names for the closureâs parameters. These are named with a dollar sign, then a number counting from 0.
travel {
"I'm going to \($0) in my car"
}
3. Shorthand parameter names - Additional
If you make it through those, youâre most of the way to understanding how closures are used in Swift and why they are important. However, if you want to see more advanced usages of closures youâre welcome to press on!
Advanced closures
4. Closures with multiple parameters
4. Closures with multiple parameters
Just to make sure everything is clear, weâre going to write another closure example using two parameters.
This time our travel()
function will require a closure that specifies where someone is traveling to, and the speed they are going. This means we need to use (String, Int) -> String
for the parameterâs type:
func travel(action: (String, Int) -> String) {
print("I'm getting ready to go.")
let description = action("London", 60)
print(description)
print("I arrived!")
}
Weâre going to call that using a trailing closure and shorthand closure parameter names. Because this accepts two parameters, weâll be getting both $0
and $1
:
travel {
"I'm going to \($0) at \($1) miles per hour."
}
Some people prefer not to use shorthand parameter names like $0
because it can be confusing, and thatâs OK â do whatever works best for you.
4. Closures with multiple parameters - Additional
5. Returning closures from functions
5. Returning closures from functions
In the same way that you can pass a closure to a function, you can get closures returned from a function too.
The syntax for this is a bit confusing a first, because it uses ->
twice: once to specify your functionâs return value, and a second time to specify your closureâs return value.
To try this out, weâre going to write a travel()
function that accepts no parameters, but returns a closure. The closure that gets returned must be called with a string, and will return nothing.
Hereâs how that looks in Swift:
func travel() -> (String) -> Void {
return {
print("I'm going to \($0)")
}
}
We can now call travel()
to get back that closure, then call it as a function:
let result = travel()
result("London")
Itâs technically allowable â although really not recommended! â to call the return value from travel()
directly:
let result2 = travel()("London")
5. Returning closures from functions - Additional
6. Capturing values
6. Capturing values
If you use any external values inside your closure, Swift captures them â stores them alongside the closure, so they can be modified even if they donât exist any more.
Right now we have a travel()
function that returns a closure, and the returned closure accepts a string as its only parameter and returns nothing:
func travel() -> (String) -> Void {
return {
print("I'm going to \($0)")
}
}
We can call travel()
to get back the closure, then call that closure freely:
let result = travel()
result("London")
Closure capturing happens if we create values in travel()
that get used inside the closure. For example, we might want to track how often the returned closure is called:
func travel() -> (String) -> Void {
var counter = 1
return {
print("\(counter). I'm going to \($0)")
counter += 1
}
}
Even though that counter
variable was created inside travel()
, it gets captured by the closure so it will still remain alive for that closure.
So, if we call result("London")
multiple times, the counter will go up and up:
result("London")
result("London")
result("London")
6. Capturing values - Additional
- Optional: Why do Swiftâs closures capture values?
- Test: Capturing values
7. Closures summary
7. Closures summary
Youâve made it to the end of the sixth part of this series, so letâs summarize:
- You can assign closures to variables, then call them later on.
- Closures can accept parameters and return values, like regular functions.
- You can pass closures into functions as parameters, and those closures can have parameters of their own and a return value.
- If the last parameter to your function is a closure, you can use trailing closure syntax.
- Swift automatically provides shorthand parameter names like
$0
and$1
, but not everyone uses them. - If you use external values inside your closures, they will be captured so the closure can refer to them later.
7. Closures summary - Additional
- Test: Closures
Remember, stay accountable: tell the world that youâve just learned all about closures in Swift, and discuss them with others who are learning too.