Closures are first-class citizens in Swift. This means they can be assigned to variables and passed as arguments to functions. Now that we’ve looked at assigning them to variables, let’s see how we can pass them to functions.
A function that accepts a closure as an argument is known as a higher-order function. This pattern is very common in Swift, and we will take a closer look at some built-in higher-order functions in a later exercise.
For now though, the science division is so excited about the transformation closure that they assigned us another task! They would like us to create a function that takes a closure that describes a Teenage Mutant Ninja Turtle. We’re not quite sure what they’re cooking over there but it sounds pretty wild!
Let’s start by defining a closure that takes a String
as input and prints the turtle’s ability to the console:
let turtleAbility: (String) -> Void = { turtle in switch turtle { case "Leonardo": print("Jujitsu") case "Raphael": print("Super strength") case "Donatello": print("Intelligence") case "Michelangelo": print("Eating pizza") default: print("Unknown TNMT") } }
The closure definition should look familiar from the previous exercise. We name the closure turtleAbility
which takes a String
as an input and returns Void
. Within the braces, we name the String
parameter turtle
. In the body of the closure, we switch on turtle
and print its ability.
Next, lets define a function that takes a String
and closure as a parameter:
func description(for turtle: String, descriptionClosure: (String) -> Void) { descriptionClosure(turtle) }
We have a function named description(for:descriptionClosure)
that takes a String
as an input, named turtle
and a closure named descriptionClosure
. The closure type matches that of the turtleAbility
we defined earlier. See where this is going? Just like with functions, if the type matches, we can pass it as an argument. Here’s what the calling code looks like:
description(for: "Michelangelo", descriptionClosure: turtleAbility) // prints “Eating pizza”
What’s cool about this is we can define an entirely new closure with the same type and pass it to the description
function to get different functionality. Consider the following closure which print’s the turtle’s color:
let turtleColor: (String) -> Void = { turtle in switch turtle { case "Leonardo": print("Blue") case "Raphael": print("Red") case "Donatello": print("Purple") case "Michelangelo": print("Orange") default: print("Unknown TNMT") } }
And when we call the description
function with the turtleColor
closure:
description(for: "Donatello", descriptionClosure: turtleColor) // prints “Purple”
Awesome. Now we can pass closures to functions like it’s our own super ability!
Instructions
To start, create a function named handValue
that takes an array of integers named cards
and a closure named scoringRules
of type (Int) -> Int
. The function should return an integer which will be the value of the hand.
In the body of the handValue
function, calculate the value of the hand with the scoring rules closure and return the value. Start by adding a variable named totalValue
initialized to 0. Then apply the closure to each card by looping through the cards array. Return totalValue
.
Define a closure named allCards
that matches the scoringRules
type in our handValue
function that counts all cards. Call the handValue
function with the allCards
closure. What do you expect the output to be?
Define a closure named onlyEvens
that matches the scoringRules
type in our handValue
function. The closure should only count cards if they are even. Use the isMultiple(of:)
function to determine if the card is even. Call the handValue
function with the closure. What do you expect the output to be?