You probably have already used closures by now without noticing 😯
Closures are a powerful way of writing code that performs and looks better.
They are also present in many programming languages so knowing how they work will help you recognize them when working in other type of projects.
- Describe how closures work and how to use them
- Declaring and calling closures
- Apply the closure syntax and shorthand argument names
After 5 days, it finally arrives to its destination.
My friend can now open the package and reveal their gift.
I'm not there to physically give them the present, but the contents remain in the package until it gets opened and ready to use.
This is the same concept as closures. Let's see how.
Apple's definition:
"Closures are self-contained blocks of functionality that can be passed around and used in your code."
In essence, a closure is a block of code that you can assign to a variable or constant. Then pass it around in your code and execute its content later somewhere else
The actual content of the gift - block of code, something we are going to use later
The wrapped package - the block of code now assigned to a variable
The package gets passed around by the delivery truck - code being passed around
the package being opened and used - Executing the block of code.
var brunch = {
print("Coffee and bagels")
}
Q: What is the type of the closure?
We can add it in the declaration.
var brunch: () -> () = {
print("Coffee and bagels")
}
brunch()
let brunchOption:(String) -> () = { option in
print(option)
}
brunchOption("Mimosas and croissants")
Q: What is the type of the closure?
We can use the value passed inside the statements of the closure by placing a parameter name `option` followed by the `in` keyword.The in
keyword separates the parameter name with the body of the closure.
When calling the closure, since it accepts a String, we pass the string in that moment.
let brunchOptionLocation:(String) -> (String) = { option in
let message = option + " @ Castro St."
return message
}
let result = brunchOptionLocation("Pancakes and smoothies")
print(result)
Q: What is the meaning of (String) -> (String)
?
Q: How are we using option
?
Q: How do we return and show the output?
func getBrunch(optionClosure:()->()) {
print("Going for brunch.")
}
getBrunch(optionClosure: {
print("Anything edible")
})
Q: What is the output?
Q: Why is the closure statement is not executed?
Closures are very similar to functions. In fact, functions are a special type of closures. They have a name and are declared with the keyword func
whereas closures are nameless.
Take the following function and turn it into a closure. Note each step you make in the transformation. Include how you would call it.
func add(number1: Int, number2: Int) -> Int {
return number1 + number2
}
func cook() -> (String) -> Void {
return {
print("I'm going to cook \($0)")
}
}
let result = cook()
result("pizza")
func cook() -> (String) -> Void {
var counter = 1
return {
print("\(counter). I'm going to cook \($0)")
counter += 1
}
}
let result = cook()
result("pizza")
result("pasta")
result("cake")
Swift’s closure expressions have a clean, clear style, with optimizations including:
- Inferring parameters
- Implicit returns from single-expression closures
- Shorthand argument names
We'll use the sort method as an example.
sorted(by:)
sorts an array of values of a known type, based on the output of a sorting closure that we provide.
Once it completes the sorting process, the sorted(by:)
method returns a new array of the same type and size as the old one, with its elements in the correct sorted order.
The original array is not modified.
let names = ["Andrea", "Chris", "Marie", "Beth", "Tom"]
The sorted(by:)
method accepts a closure that takes two arguments of the same type as the array’s contents, and returns a Bool
value to say whether the first value should appear before or after the second value once the values are sorted. The sorting closure needs to return true if the first value should appear before the second value, and false otherwise.
First approach, using a function and passing it as a parameter.
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)
For characters in strings, “greater than” means “appears later in the alphabet than”. This means that the letter "B" is “greater than” the letter "A".
Second approach, using a closure expression syntax.
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
Since the body of the closure is short, we can write in in one line.
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
Also, because the sorting closure is passed as an argument to a method, Swift can infer the types of its parameters and the type of the value it returns.
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0
, $1
, $2
, and so on.
reversedNames = names.sorted(by: { $0 > $1 } )
Complete these challenges on closures.
- Complete challenges
- Read the content listed below if you need more clarity on closures.
- Go over this playground if you feel like wanting more practice with closures.