Writing Functions and Closures in Swift
I’ve covered a lot up to this point in the book: variables, constants, dictionaries, arrays, looping constructs, control structures, and the like. You’ve used both the REPL command-line interface and now Xcode’s playgrounds feature to type in code samples and explore the language.
Up to this point, however, you have been limited to mostly experimentation: typing a line or three here and there and observing the results. Now it’s time to get more organized with your code. In this chapter, you’ll learn how to tidy up your Swift code into nice clean reusable components known as functions.
Let’s start this chapter with a fresh new playground file. If you haven’t already done so, launch Xcode and create a new playground by choosing File > New > Playground, and name it Chapter 4.playground. You’ll explore this chapter’s concepts with contrived examples in similar fashion to earlier chapters.
The Function
Think back to your school years again. This time, remember high school algebra. You were paying attention, weren’t you? In that class your teacher introduced the concept of the function. In essence, a function in arithmetic parlance is a mathematical formula that takes one or more inputs, performs a calculation, and provides a result, or output.
Mathematical functions have a specific notation. For example, to convert a Fahrenheit temperature value to the equivalent Celsius value, you would express that function in this way:
The important parts of the function are:
- Name: In this case the function’s name is f.
- Input, or independent variable: Contains the value that will be used in the function. Here it’s x.
- Expression: Everything to the right of the equals sign.
- Result: Considered to be the value of f(x) on the left side of the equals sign.
Functions are written in mathematical notation but can be described in natural language. In English, the sample function could be described as:
- A function whose independent variable is x and whose result is the difference of the independent variable and 32, with the result being multiplied by 5, with the result being divided by 9.
The expression is succinct and tidy. The beauty of functions is that they can be used over and over again to perform work, and all they need to do is be called with a parameter. So how does this relate to Swift? Obviously I wouldn’t be talking about functions if they didn’t exist in the Swift language. And as you’ll see, they can perform not just mathematical calculations but a whole lot more.
Coding the Function in Swift
Swift’s notation for establishing the existence of a function is a little different than the mathematical one you just saw. In general, the syntax for declaring a Swift function is:
func funcName(paramName : type, ...) -> returnType
Take a look at an example to help clarify the syntax. Figure 4.1 shows the code in the Chapter 4.playground file, along with the function defined on lines 7 through 13. This is the function discussed earlier, but now in a notation that the Swift compiler can understand.
FIGURE 4.1 Temperature conversion as a Swift function
Start by typing in the following code.
func fahrenheitToCelsius(fahrenheitValue : Double) -> Double { var result : Double result = (((fahrenheitValue - 32) * 5) / 9) return result }
As you can see on line 7, there is some new syntax to learn. The func keyword is Swift’s way to declare a function. That is followed by the function name (fahrenheitToCelsius), and the independent variable’s name, or parameter name, in parentheses. Notice that the fahrenheitValue parameter’s type is explicitly declared as Double.
Following the parameter are the two characters ->, which denote that this function is returning a value of a type (in this case, a Double type), followed by the open curly brace, which indicates the start of the function.
On line 8, you declare a variable of type Double named result. This will hold the value that will be given back to anyone who calls the function. Notice that it is the same type as the function’s return type declared after the -> on line 7.
The actual mathematical function appears on line 10, with the result of the expression assigned to result, the local variable declared in line 8. Finally on line 12, the result is returned to the caller using the return keyword. Anytime you wish to exit a function and return to the calling party, you use return along with the value being returned.
The Results sidebar doesn’t show anything in the area where the function was typed. That’s because a function by itself doesn’t do anything. It has the potential to perform some useful work, but it must be called by a caller. That’s what you’ll do next.
Exercising the Function
Now it’s time to call on the function you just created. Type in the following two lines of code, and pay attention to the Results sidebar in Figure 4.2.
var outdoorTemperatureInFahrenheit = 88.2 var outdoorTemperatureInCelsius = fahrenheitToCelsius(outdoorTemperatureInFahrenheit)
FIGURE 4.2 The result of calling the newly created function
On line 15, you’ve declared a new variable, outdoorTemperatureInFahrenheit, and set its value to 88.2 (remember, Swift infers the type in this case as a Double). That value is then passed to the function on line 16, where a new variable, outdoorTemperatureInCelsius, is declared, and its value is captured as the result of the function.
The Results sidebar shows that 31.222222 (repeating decimal) is the result of the function, and indeed, 31.2 degrees Celsius is equivalent to 88.2 degrees Fahrenheit. Neat, isn’t it? You now have a temperature conversion tool right at your fingertips.
Now, here’s a little exercise for you to do on your own: Write the inverse method, celsiusToFahrenheit, using the following formula for that conversion:
Go ahead and code it up yourself, but resist the urge to peek ahead. Don’t look until you’ve written the function, and then check your work against the following code and in Figure 4.3.
func celsiusToFahrenheit(celsiusValue : Double) -> Double { var result : Double result = (((celsiusValue * 9) / 5) + 32) return result } outdoorTemperatureInFahrenheit = celsiusToFahrenheit(outdoorTemperatureInCelsius)
FIGURE 4.3 Declaring the inverse function, celsiusToFahrenheit
The inverse function on lines 18 through 24 simply implements the Celsius to Fahrenheit formula and returns the result. Passing in the Celsius value of 31.22222 on line 26, you can see that the result is the original Fahrenheit value, 88.2.
You’ve just created two functions that do something useful: temperature conversions. Feel free to experiment with other values to see how they change between the two related functions.
More Than Just Numbers
The notion of a function in Swift is more than just the mathematical concept I have discussed. In a broad sense, Swift functions are more flexible and robust in that they can accept more than just one parameter, and even accept types other than numeric ones.
Consider creating a function that takes more than one parameter and returns something other than a Double (Figure 4.4).
FIGURE 4.4 A multi-parameter function
func buildASentenceUsingSubject(subject : String, verb : String, noun : String) -> String { return subject + " " + verb + " " + noun + "!" } buildASentenceUsingSubject("Swift", verb: "is", noun: "cool") buildASentenceUsingSubject("I", verb: "love", noun: "languages")
After typing in lines 28 through 33, examine your work. On line 28, you declared a new function, buildASentence, with not one but three parameters: subject, verb, and noun, all of which are String types. The function also returns a String type as well. On line 29, the concatenation of those three parameters, interspersed with spaces to make the sentence readable, is what is returned.
To demonstrate the utility of the function, it is called twice on lines 32 and 33, resulting in the sentences in the Results sidebar.
If you are familiar with the C language and how parameters are passed to functions, the notation on lines 32 and 33 may appear confusing at first. Swift enforces the notion of named parameters on all but the first parameter of a function. The names that were declared in the function on line 28 (verb and noun) are specified on this line right alongside the actual string values.
Swift enforces the notion of named parameters, which is a legacy of Objective-C. Named parameters bring clarity to your source code by documenting exactly what is being passed. From the code, you can clearly see that the verb and noun are the second and third parameters, respectively.
Feel free to replace the parameters with values of your own liking and view the results interactively.
Parameters Ad Nauseam
Imagine you’re writing the next big banking app for the Mac, and you want to create a way to add some arbitrary number of account balances. Something so mundane can be done a number of ways, but you want to write a Swift function to do the addition. The problem is you don’t know how many accounts will need to be summed at any given time.
Enter Swift’s variable parameter passing notation. It provides you with a way to tell Swift, “I don’t know how many parameters I’ll need to pass to this function, so accept as many as I will give.” Type in the following code, which is shown in action on lines 35 through 48 in Figure 4.5.
FIGURE 4.5 Variable parameter passing in a function
// Parameters Ad Nauseam func addMyAccountBalances(balances : Double...) -> Double { var result : Double = 0 for balance in balances { result += balance } return result } addMyAccountBalances(77.87) addMyAccountBalances(10.52, 11.30, 100.60) addMyAccountBalances(345.12, 1000.80, 233.10, 104.80, 99.90)
This function’s parameter, known as a variadic parameter, can represent an unknown number of parameters.
On line 36, your balances parameter is declared as a Double followed by the ellipsis (...) and returns a Double. The presence of the ellipsis is the clue: It tells Swift to expect one or more parameters of type Double when this function is called.
The function is called three times on lines 46 through 48, each with a different number of bank balances. The totals for each appear in the Results sidebar.
You might be tempted to add additional variadic parameters in a function. Figure 4.6 shows an attempt to extend addMyAccountBalances with a second variadic parameter, but it results in a Swift error.
FIGURE 4.6 Adding additional variable parameters
This is a no-no, and Swift will quickly shut you down with an error. Only one parameter of a function may contain the ellipsis to indicate a variadic parameter. All other parameters must refer to a single quantity.
Since we’re on the theme of bank accounts, add two more functions: one that will find the largest balance in a given list of balances, and another that will find the smallest balance. Type the following code, which is shown on lines 50 through 75 in Figure 4.7.
FIGURE 4.7 Functions to find the largest and smallest balance
func findLargestBalance(balances : Double...) -> Double { var result : Double = -Double.infinity for balance in balances { if balance > result { result = balance } } return result } func findSmallestBalance(balances : Double...) -> Double { var result : Double = Double.infinity for balance in balances { if balance < result { result = balance } } return result } findLargestBalance(345.12, 1000.80, 233.10, 104.80, 99.90) findSmallestBalance(345.12, 1000.80, 233.10, 104.80, 99.90)
Both functions iterate through the parameter list to find the largest and smallest balance. Unless you have an account with plus or minus infinity of your favorite currency, these functions will work well. On lines 74 and 75, both functions are tested with the same balances used earlier, and the Results sidebar confirms their correctness.
Functions Fly First Class
One of the powerful features of Swift functions is that they are first-class objects. Sounds pretty fancy, doesn’t it? What that really means is that you can handle a function just like any other value. You can assign a function to a constant, pass a function as a parameter to another function, and even return a function from a function!
To illustrate this idea, consider the act of depositing a check into your bank account, as well as withdrawing an amount. Every Monday, an amount is deposited, and every Friday, another amount is withdrawn. Instead of tying the day directly to the function name of the deposit or withdrawal, use a constant to point to the function for the appropriate day. The code on lines 77 through 94 in Figure 4.8 provides an example.
var account1 = ("State Bank Personal", 1011.10) var account2 = ("State Bank Business", 24309.63) func deposit(amount : Double, account : (name : String, balance : Double)) -> (String, Double) { let newBalance : Double = account.balance + amount return (account.name, newBalance) } func withdraw(amount : Double, account : (name : String, balance : Double)) -> (String, Double) { var newBalance : Double = account.balance - amount return (account.name, newBalance) } let mondayTransaction = deposit let fridayTransaction = withdraw let mondayBalance = mondayTransaction(300.0, account: account1) let fridayBalance = fridayTransaction(1200, account: account2)
FIGURE 4.8 Demonstrating functions as first-class types
For starters, you create two accounts on lines 77 and 78. Each account is a tuple consisting of an account name and balance.
On line 80, a function named deposit is declared, and it takes two parameters: the amount (a Double) and a tuple named account. The tuple has two members: name, which is of type String, and balance, which is a Double that represents the funds in that account. The same tuple type is also declared as the return type.
At line 81, a variable named newBalance is declared, and its value is assigned the sum of the balance member of the account tuple and the amount variable that is passed. The tuple result is constructed on line 82 and returned.
The function on line 85 is named differently (withdraw) but is effectively the same, save for the subtraction that takes place on line 86.
On lines 90 and 91, two new constants are declared and assigned to the functions respectively by name: deposit and withdraw. Since deposits happen on a Monday, the mondayTransaction is assigned the deposit function. Likewise, withdrawals are on Friday, and the fridayTransaction constant is assigned the withdraw function.
Lines 93 and 94 show the results of passing the account1 and account2 tuples to the mondayTransaction and fridayTransaction constants, which are in essence the functions deposit and withdraw. The Results sidebar bears out the result, and you’ve just called the two functions by referring to the constants.
Throw Me a Function, Mister
Just as a function can return an Int, Double, or String, a function can also return another function. Your head starts hurting just thinking about the possibilities, doesn’t it? Actually, it’s not as hard as it sounds. Check out lines 96 through 102 in Figure 4.9.
func chooseTransaction(transaction: String) -> (Double, (String, Double)) -> (String, Double) { if transaction == "Deposit" { return deposit } return withdraw }
FIGURE 4.9 Returning a function from a function
On line 96, the function chooseTransaction takes a String as a parameter, which it uses to deduce the type of banking transaction. That same function returns a function, which itself takes a Double, and a tuple of String and Double, and returns a tuple of String and Double. Phew!
That’s a mouthful. Let’s take a moment to look at that line more closely and break it down a bit. The line begins with the definition of the function and its sole parameter, transaction, followed by the -> characters indicating the return type:
func chooseTransaction(transaction: String) ->
After that is the return type, which is a function that takes two parameters—the Double, and a tuple of Double and String—as well as the function return characters ->:
(Double, (String, Double)) ->
And finally, the return type of the returned function, a tuple of String and Double.
What functions did you write that meet these criteria? The deposit and withdraw functions, of course! Look at lines 80 and 85. Those two functions are bank transactions that were used earlier. Since they are defined as functions that take two parameters (a Double and a tuple of String and Double) and return a tuple of Double and String, they are appropriate candidates for return values in the chooseTransaction function on line 96.
Back to the chooseTransaction function: On line 97, the transaction parameter, which is a String, is compared against the constant string "Deposit" and if a match is made, the deposit function is returned on line 98; otherwise, the withdraw function is returned on line 101.
OK, so you have a function which itself returns one of two possible functions. How do you use it? Do you capture the function in another variable and call it?
Actually, there are two ways this can be done (Figure 4.10).
// option 1: capture the function in a constant and call it let myTransaction = chooseTransaction("Deposit") myTransaction(225.33, account2) // option 2: call the function result directly chooseTransaction("Withdraw")(63.17, account1)
FIGURE 4.10 Calling the returned function in two different ways
On line 105 you can see that the returned function for making deposits is captured in the constant myTransaction, which is then called on line 106 with account2 increasing its amount by $225.33.
The alternate style is on line 109. There, the chooseTransaction function is being called to gain access to the withdraw function. Instead of assigning the result to a constant, however, the returned function is immediately pressed into service with the parameters 63.17 and the first account, account1. The results are the same in the Results sidebar: The withdraw function is called and the balance is adjusted.
A Function in a Function in a...
If functions returned by functions and assigned to constants isn’t enough of an enigma for you, how about declaring a function inside another function? Yes, such a thing exists. They’re known as nested functions.
Nested functions are useful when you want to isolate, or hide, specific functionality that doesn’t need to be exposed to outer layers. Take, for instance, the code in Figure 4.11.
// nested function example func bankVault(passcode : String) -> String { func openBankVault(_: Void) -> String { return "Vault opened" } func closeBankVault() -> String { return "Vault closed" } if passcode == "secret" { return openBankVault() } else { return closeBankVault() } } print(bankVault("wrongsecret")) print(bankVault("secret"))
FIGURE 4.11 Nested functions in action
On line 112, a new function, bankVault, is defined. It takes a single parameter, passcode, which is a String, and returns a String.
Lines 113 and 116 define two functions inside the bankVault function: openBankVault and closeBankVault. Both of these functions take no parameter and return a String.
On line 119, the passcode parameter is compared with the string "secret" and if a match is made, the bank vault is opened by calling the openBankVault function. Otherwise, the bank vault remains closed.
Lines 127 and 128 show the result of calling the bankVault method with an incorrect and correct passcode. What’s important to realize is that the openBankVault and closeBankVault functions are “enclosed” by the bankVault function, and are not known outside of that function.
If you were to attempt to call either openBankVault or closeBankVault outside of the bankVault function, you would get an error. That’s because those functions are not in scope. They are, in effect, hidden by the bankVault function and are unable to be called from the outside. Figure 4.12 illustrates an attempt to call one of these nested functions.
FIGURE 4.12 The result of attempting to call a nested function from a different scope
In general, the obvious benefit of nesting functions within functions is that it prevents the unnecessary exposing of functionality. In Figure 4.12, the bankVault function is the sole gateway to opening and closing the vault, and the functions that perform the work are isolated within that function. Always consider this when designing functions that are intended to work together.
Default Parameters
As you’ve just seen, Swift functions provide a rich area for utility and experimentation. A lot can be done with functions and their parameters to model real-world problems. Functions provide an interesting feature known as default parameter values, which allow you to declare functions that have parameters containing a “prefilled” value.
Let’s say you want to create a function that writes checks. Your function would take two parameters: a payee (the person or business to whom the check is written) and the amount. Of course, in the real world, you always want to know these two pieces of information, but for now, think of a function that would assume a default payee and amount in the event the information wasn’t passed.
Figure 4.13 shows such a function on lines 130 through 132. The writeCheckTo function takes two String parameters, the payee and amount, and returns a String that is simply a sentence describing how the check is written.
func writeCheckTo(payee : String = "Unknown", amount : String = "10.00") -> String { return "Check payable to " + payee + " for $" + amount } writeCheckTo() writeCheckTo("Donna Soileau") writeCheckTo("John Miller", amount : "45.00")
FIGURE 4.13 Using default parameters in a function
Take note of the declaration of the function on line 130:
func writeCheckTo(payee : String = "Unknown", amount : String = "10.00") -> String
What you haven’t seen before now is the assignment of the parameters to actual values (in this case, payee is being set to "Unknown" by default and amount is being set to "10.00"). This is how you can write a function to take default parameters—simply assign the parameter name to a value!
So how do you call this function? Lines 134 through 136 show three different ways:
- Line 134 passes no parameters when calling the function.
- Line 135 passes a single parameter.
- Line 136 passes both parameters, with the second parameter following its parameter name amount.
In the case where no parameters are passed, the default values are used to construct the returned String. In the other two cases, the passed parameter values are used in place of the default values, and you can view the results of the calls in the Results sidebar.
Recall that Swift enforces the requirement that the parameter name must be passed for all but the first parameter. On line 135, only one parameter is used, so the name is not passed:
writeCheckTo("Donna Soileau")
On line 136, two parameter names are used, and the parameter name is specified prior to the amount string:
writeCheckTo("John Miller", amount : "45.00")
Default parameters give you the flexibility of using a known value instead of taking the extra effort to pass it explicitly. They’re not necessarily applicable for every function out there, but they do come in handy at times.
What’s in a Name?
As Swift functions go, declaring them is easy, as you’ve seen. In some cases, however, what really composes the function name is more than just the text following the keyword func.
As I touched on earlier, each parameter in a Swift function has the parameter name preceding the parameter. This gives additional clarity and description to a function name. Up to this point, you’ve been told that it must be passed when calling the function. Although it is good practice, it is not entirely necessary. When declaring a function, an implicit external parameter name can be notated with an underscore preceding the parameter name. Consider another check writing function in Figure 4.14, lines 138 through 140.
func writeCheckFrom(payer : String, _ payee : String, _ amount : Double) -> String { return "Check payable from \(payer) to \(payee) for $\(amount)" } writeCheckFrom("Dave Johnson", "Coz Fontenot", 1_000.0)
FIGURE 4.14 A function with an implicit external parameter name
This function is different from the earlier check writing function on lines 130 through 132 in two ways:
- An underscore and a space precede the parameters named payee and amount
- There are no default parameters
On line 142, the new writeCheckFrom function is called with three parameters: two String values and a Double value. From the name of the function, its purpose is clearly to write a check. When writing a check, you need to know several things: who the check is being written for, who is writing the check, and the amount the check is for. A good guess is that the Double parameter is the amount, which is a number. But without actually looking at the function declaration itself, how would you know what the two String parameters actually mean? Even if you were to deduce that they are the payer and payee, how do you know which is which, and in which order to pass the parameters?
Swift’s default behavior of insisting on the use of parameter names solves this problem and makes the intent of your code easier to understand; it makes very clear to anyone reading the calling function what the intention is and the purpose of each parameter. Figure 4.15 illustrates this.
func writeBetterCheckFrom(payer : String, payee : String, amount : Double) -> String { return "Check payable from \(payer) to \(payee) for $\(amount)" } writeBetterCheckFrom("Fred Charlie", payee : "Ryan Hanks", amount : 1350.0)
FIGURE 4.15 A function called with parameter names
On line 144, you declare a function, writeBetterCheckFrom, which takes the same number of parameters as the function on line 138. However, each of the parameters in the new function omits the underscore.
The extra bit of typing pays off when the writeBetterCheckFrom function is called. Looking at that line of code alone, the order of the parameters and what they indicate is clear: Write a check from Fred Charlie to Ryan Hanks for a total of $1350.
When It’s Good Enough
Parameter names bring clarity to functions, as you’ve just seen. In addition, Swift allows external parameter names to decorate a function declaration. This can be useful if you want to bring additional clarity to your function.
Line 150 of Figure 4.16 shows this in action. The new method, writeBestCheck has dropped the From in the name. Instead, it has moved to the first parameter as an external parameter name. Other external parameter names in this function declaration are to and total.
FIGURE 4.16 Using the external parameter name syntax
On line 154, the parameter names are used as external parameter names to call the function, and the use of those names clearly shows what the function’s purpose and parameter order is: a check written from Bart Stewart to Alan Lafleur for a total of $101. Note that when using external parameter names, the first parameter also requires the parameter name to be passed. This is different from what you saw earlier when your earlier functions weren’t using external parameter names.
func writeBestCheck(from payer : String, to payee : String, total amount : Double) -> String { return "Check payable from \(payer) to \(payee) for $\(amount)" } writeBestCheck(from: "Bart Stewart", to: "Alan Lafleur", total: 101.0)
To Use or Not to Use?
Parameter names bring clarity to functions, but they also require more typing on the part of the coder who uses your functions. Since they are optional parts of a function’s declaration, when should you use them?
In general, if the function in question can benefit from the additional clarity of having parameter names provided for each parameter, by all means use them. The check writing example is such a case. Avoid parameter ambiguity in the cases where it might exist. On the other hand, if you’re creating a function that just adds two numbers (see lines 156 through 160 in Figure 4.17), parameter names add little to nothing of value for the caller. You can just use the underscore (recall implicit external parameter names) and avoid passing the parameter name altogether.
func addTwoNumbers(number1 : Double, _ number2 : Double) -> Double { return number1 + number2 } addTwoNumbers(33.1, 12.2)
FIGURE 4.17 When parameter names are not necessary
Don’t Change My Parameters!
Functions are prohibited from changing the values of parameters passed to them, because parameters are passed as constants and not variables. Consider the function cashCheck on lines 162 through 169 in Figure 4.18.
func cashCheck(from : String, to : String, total : Double) -> String { if to == "Cash" { to = from } return "Check payable from \(from) to \(to) for $\(total) has been cashed" } cashCheck("Jason Guillory", to: "Cash", total: 103.00)
FIGURE 4.18 Assigning a value to a parameter results in an error.
The function takes the same parameters as your earlier check writing function: who the check is from, who the check is to, and the total. On line 163, the to variable is checked for the value "Cash" and if it is equal, it is reassigned the contents of the variable from. The rationale here is that if you are writing a check to “Cash,” you’re essentially writing it to yourself.
Notice the error: Cannot assign to value: 'to' is a 'let' constant. Swift is saying that the parameter to is a constant, and since constants cannot change their values once assigned, this is prohibited and results in an error.
To get around this error, you could create a temporary variable, as done in Figure 4.19. Here, a new variable named otherTo is declared on line 163 and assigned to the to variable, and then possibly to the from variable on line 165, assuming the condition on line 164 is met. This is clearly acceptable and works fine for your purposes, but Swift gives you a better way.
FIGURE 4.19 A potential workaround to the parameter change problem
With a var declaration on a parameter, you can tell Swift that the parameter is intended to be variable and can change within the function. All you need to do is add the keyword before the parameter name (or external parameter name in case you have one of those). Figure 4.20 shows a second function, cashBetterCheck, which declares the to parameter as a variable parameter. Now the code inside the function can modify the to variable without receiving an error from Swift, and the output is identical to the workaround function above it.
func cashBetterCheck(from : String, var to : String, total : Double) -> String { if to == "Cash" { to = from } return "Check payable from \(from) to \(to) for $\(total) has been cashed" } cashBetterCheck("Ray Daigle", to: "Cash", total: 103.00)
FIGURE 4.20 Using variable parameters to allow modifications
The Ins and Outs
As you’ve just seen, a function can be declared to modify the contents of one or more of its passed variables. The modification happens inside the function itself, and the change is not reflected back to the caller.
Sometimes having a function change the value of a passed parameter so that its new value is reflected back to the caller is desirable. For example, in the cashBetterCheck function on lines 172 through 177, having the caller know that the to variable has changed to a new value would be advantageous. Right now, that function’s modification of the variable is not reflected back to the caller. Let’s see how to do this in Figure 4.21 using Swift’s inout keyword.
func cashBestCheck(from : String, inout to : String, total : Double) -> String { if to == "Cash" { to = from } return "Check payable from \(from) to \(to) for $\(total) has been cashed" } var payer = "James Perry" var payee = "Cash" print(payee) cashBestCheck(payer, to: &payee, total: 103.00) print(payee)
FIGURE 4.21 Using the inout keyword to establish a modifiable parameter
Lines 181 through 186 define the cashBestCheck function, which is virtually identical to the cashBetterCheck function on line 172, except the second parameter to is no longer a variable parameter—the var keyword has been replaced with the inout keyword. This new keyword tells Swift that the parameter’s value can be expected to change in the function and that the change should be reflected back to the caller. With that exception, everything else is the same between the cashBetterCheck and cashBestCheck functions.
On lines 188 and 189, two variables are declared: payer and payee, with both being assigned String values. This is done because inout parameters must be passed a variable. A constant value will not work, because constants cannot be modified.
On line 190, the payee variable is printed, and the Results sidebar for that line clearly shows the variable’s contents as "Cash". This is to make clear that the variable is set to its original value on line 189.
On line 191, you call the cashBestCheck function. Unlike the call to cashBetterCheck on line 179, you are passing variables instead of constants for the to and from parameters. More so, for the second parameter (payee), we are prepending the ampersand character (&) to the variable name. This is a direct result of declaring the parameter in cashBestCheck as an inout parameter. You are in essence telling Swift that this variable is an inout variable and that you expect it to be modified once control is returned from the called function.
On line 193, the payee variable is again printed. This time, the contents of that variable do not match what was printed on line 190 earlier. Instead, payee is now set to the value "James Perry", which is a direct result of the assignment in the cashBestCheck function on line 183.