Hey, guys. 1
Welcome to another Swift Deep Dive. In this Deep Dive, 2
we're going to talk about Closures. 3
Closures are essentially anonymous functions or functions without a name and they're essentially a self-contained 4
package of functionality that we can pass around and use. 5
So we've seen a lot of functions up till now. And we know that when we create a function, we always start 6
with the func keyword. 7
And then we give our function a name, and then we have a section that's enclosed in parentheses that 8
represent the input parameters and the input parameter type. 9
And finally, we have this little dash and angle bracket to represent the returnType, i.e., what is the 10
data type of the thing that we will output from our function. 11
And in the body of our function, i.e., between the curly braces, we get our function to do something, and 12
then finally, if our function has an output, then we get it to return the output. 13
So, essentially, our function is a little bit like a bread cutting machine. 14
You have an input, say, your bread, and then you pass it through your function and you get an output. 15
Something happens to the bread, it gets cut up in slices. 16
You can determine how thick or thin the slice is. But, essentially, all it is is a bunch of functionality 17
that's packaged together and given a function name so that we can refer to it later on when we need 18
that functionality, so we can call the method or call the function by its name, giving it inputs and receiving 19
the outputs. 20
So up till now, we've seen three variations on function. 21
We've seen functions that only do something without needing an input or having an output. 22
And then we've seen functions which take an input and then we've seen functions which do all three, i.e., 23
a function that takes input and has an output and does something in between. 24
But let's now look at the step beyond that. 25
The interesting thing about functions is that you can actually pass a function as an input to another 26
function and you can also have a function as the output of another function. 27
So I can show you this better in playgrounds. 28
So I've got a blank playground here. 29
What I'm going to do is I'm going to create a function that's called calculator, and what it does is 30
it takes a number n1, and it also takes another number n2, and they're both integers, and the calculator 31
also returns an integer. 32
So all that our calculator is going to do right now is it's just going to add n1 to n2, and it returns 33
the result of the first number added to the second number. 34
So let's give it a spin. Let's say a calculator and give it to numbers 2 and 3. And we can see the output 35
is 5. 36
So all is working as expected. 37
Now, say, if we wanted to change the calculator to a multiplication calculator, then we would have to go 38
into the body of the calculator and change the return statement. 39
Now, what if we wanted to be able to pass in a function as an input to our calculator function in order 40
to determine what sort of mathematical operation it should perform? 41
So in order to do this, we're going to create another function, and I'm going to call my function add, 42
and it, again, takes two numbers. 43
So let's call it no1 and no2, 44
and it returns a number as well. 45
So now we're going to return no1 + no2. 46
So this is our basic addition operand, essentially, and we're going to go and modify our calculator 47
so that it not only takes two numbers as inputs, but we're gonna give it a third input in the form of 48
a function. In order to put this function in as an input, we're going to have to boil down this function 49
into its data types. 50
So if we have a look at this function, you can see that it takes two inputs, both in the form of integers 51
and returns an integer as an output. 52
So if you boil down this function to its data types, essentially, all it is is int, int, and returns an int. 53
So if we take this representation of that function and we pass it in as the data type of our third parameter 54
which we'll call operation, 55
so as you can see, we've got three parameters as inputs for our calculator function, n1, n2, so those 56
are the two numbers that we're going to give our calculator. 57
And then, finally, we've got a parameter called operation and the data type of operation is in the format 58
of a function, i.e., it's not just int, it's not just string, but it's actually the boiled down version 59
of a function, i.e., two integer parameter inputs and returns a single integer. 60
Now, the next thing to do is to change this return statement. At the moment, our calculator is still returning 61
the first number multiplied it by the second number. 62
But instead, what we want to do is we want to call this function that's currently called operation, 63
so we can, instead, return operation and we can give it the required inputs. 64
So as we specified, it takes two inputs and returns one output, and the two inputs have to be integers. 65
So this is where we're going to pass in our n1 and n2. 66
So now, Xcode is complaining over here because this is no longer how you call the calculator function 67
because instead of having two parameters, it's now got three. 68
So let's try calling it again and get Xcode to type out the function for us. 69
So the first number is, again, two, second number is three, and the operation is going to be "add," so that we 70
pass in this function as an input parameter. 71
And, essentially, what happens is that two goes over here, three goes into here, 72
and the operation, this function add gets passed in over here as the operation. 73
And what we return from our calculator function is we return by calling this function that's under the 74
operation parameter passing in the original inputs n1, n2. 75
So whichever function gets passed in as the operation will process these two numbers. 76
So in this case, we passed in the add a function as the operation, and the n1, n2 then goes over 77
here as the inputs, and we return n2 + n2. 78
So if I create another function, say, let's call it multiply, then I can have again two inputs. 79
So no1, 80
no2. 81
And it also returns an integer. 82
So in this case, we're returning no1 multiplied by no2. 83
So if I go ahead and change the function that we pass in to our calculator by changing it to multiply 84
instead, then you can see that the result changes to 6, because now instead of this add function being 85
passed in as the operation, we've actually got the multiply function being passed in here. 86
So I hope you'll believe me now when I tell you that you can pass in functions as inputs into other 87
functions and you can also use functions as the return type. 88
So now let's clean this up a little bit because we've actually got quite a few lines of code to do something 89
quite simple. 90
So to make things clear, I've got a multiply function that gets passed into the calculator as an input 91
and it's used to perform the calculation. 92
So at the moment, everything's very wordy and we've got quite a few lines of code. 93
Now, if I wanted to cut this down, I can instead use a closure. 94
So closures if you remember are anonymous functions or you can consider functions as named closures. 95
But, essentially, they're just packages of functionality which you can pass around freely. 96
So if we have a look at the structure of our function, which we're very familiar with, we've got a section 97
that's the input. 98
Remember, that's the part between the parentheses, 99
and then we've got a part that's the output. 100
So that's the part that comes after the dash and angle bracket. 101
And then we've got this func keyword which we've been using every single time we created a new function. 102
And finally, we've got a part that represents the name of the function. 103
So in order to convert this function to a closure, all we need to do is to remove the keyword func and 104
the name of the function. 105
And then we move this open bracket from the end of the line to the beginning of the closure and we replace 106
it instead with the keyword "in." 107
So now we've successfully converted a function into a closure. 108
And as you can see, it's pretty anonymous. 109
All you have is a block of code which you can pass around freely into other functions or set as a variable. 110
So let's try and do the same with our multiply function. Instead of calling this multiply function, 111
what we can do instead is we can pass in a closure. So remember the first thing we do is we delete the 112
func keyword and the name of the function. 113
The next thing we do is we move this open bracket to the beginning of the block of code and we replace 114
it with the keyword "in." 115
So now we have our closure and we can put it in here as an input parameter. 116
So you'll notice that nothing has changed, output wise, it's still working as it did before, but we've 117
now cut down on two lines of code. But it gets crazier than that. We can actually cut down this code even 118
further. 119
So as you know Swift has the ability to infer data types based on the value. 120
So if you have a variable "a" that contains a number, then Swift will automatically assign the data type 121
int to this variable, and that's called type inference. And we can use that here as well. 122
So instead of explicitly declaring the data type of these parameters, we can actually delete it 123
and it still works fine. Because when we pass in these inputs into this closure, the compiler will be 124
able to figure out what the data type of these parameters should be. 125
And you can do the same thing with the return type. 126
So you can actually get rid of this part entirely because we have a calculation in here, i.e., the first 127
number multiplied by the second number, the output has a data type based on the inputs. 128
So we can actually get rid of this part, too, and get the compiler to infer the data type of the output. 129
Now, our code is beginning to look a lot shorter than it did before. And I'm not even done yet. 130
We can also get rid of this return keyword because we're inside a closure and there is a operation that's 131
being carried out. 132
The compiler can infer that you want something to be returned based on the simple fact that you're processing 133
something inside your closure. 134
So we're pretty much at the point where everything can be expressed in one line. 135
Now, you might think that it can't get any shorter, but it can. Because in Swift, closures has the ability 136
to provide anonymous parameter names. 137
So here we've given our parameters names: no1 and no2. 138
And later, we refer to those parameter names to carry out our expression. 139
But instead of doing this, you can actually use the anonymous parameter names. 140
So in Swift, a dollar sign with a zero refers to the first parameter, and a dollar sign with a 1 refers 141
to the second parameter, and so on and so forth. 142
So instead of expressing this line like so, we can actually just change it to first parameter multiplied 143
by second parameter. 144
So now if I assign the result of this calculator function to a constant and then I print out this result, 145
you can see that we've still got the same result as before even though we've cut down four lines of 146
code, everything is still working exactly the same. 147
And to take this to the extreme, we can actually cut down this line even further, believe it or not, because 148
in Swift there is a rule whereby if the last parameter in your function is a closure, i.e., everything 149
between the open and closing curly braces. 150
That's what our closure has simplified down to. Then you can actually omit the last parameter name and 151
you can close the input section, and simply just have the closure trailing at the end. 152
So this is called a trailing closure. 153
So, again, as you can see, that has done nothing to change our result. 154
Everything is still working as it did before. 155
But instead of all of this, we now just have a single line. 156
So one of the nice things about closures is that they can dramatically simplify your code and cut down 157
on your code. 158
But the downside to that is that readability suffers, because for somebody who is not very familiar with 159
Swift coming in and looking at this line of code, it can be quite confusing. Because as you remember, we 160
went through many many steps in order to cut down all of this expressive syntax into something that 161
is very, very succinct. 162
Now, you can argue that shorter is better, or some programmers might argue that this has way more style 163
then something like this, which is true, but you always have to strike the balance between simplicity 164
and readability. 165
So that decision is mostly up to you. If you become more and more familiar with the language, you might 166
actually have a preference of having a vastly simplified code file. 167
Now, of course, this is just a made-up example to show you the process of passing in functions as inputs 168
or passing in functions as outputs. 169
This code doesn't stand up in real life because it's so much easier to simply just write "n1 * n2 170
equals the result," 171
right? 172
So let me show you how we would use this in real life. 173
Now, say, if we have an array with a bunch of numbers and we want to be able to add 1 to each of the 174
items in the array. 175
Now, the roundabout way of doing this is using what's called a for-loop and you can iterate through each 176
of these items in order to add 1 to every one of them. 177
But there's a much simpler way of doing this because there is a function called map that Swift provides 178
that allows you to transform every single item in a collection type. 179
So collection types are arrays or dictionaries, basically, collections of items. 180
So let me show you how it would work. 181
We first have to define a rule in order to transform our array. 182
So if I create a function code addOne and all it does is it takes one number and it returns a number, 183
but, of course, with 1 added to it. 184
So, essentially, we return n1 + 1. 185
So that's pretty simple, right? 186
But then what we can do is we can tap into our array and we can use the map function to transform this 187
array. 188
Now, the map function takes an input in the form of another function. 189
So if we use addOne here and you go ahead and hit enter, you can see that your array gets transformed 190
with every single item being one greater than previously. 191
So that's pretty neat. 192
But why do we have to write so much code when we know all about closures? And I'd like to pose this as 193
a challenge to you. 194
Can you write the code to turn this into a closure instead? Pause the video, check the closure syntax, 195
and I'll show you the solution in a bit. Ready? 196
Here's the solution. 197
So if you remember, in order to turn this into a closure, we delete the func keyword and would delete 198
the name of the function. 199
Then we move the open curly brace to the beginning of the code block and we replace that instead with 200
the keyword "in." 201
So now we can put this, instead of using that function name. 202
And, of course, we can use some of those snazzy tricks that we learned about previously where we first 203
used type inference to make the compiler infer the data type of the input as well as the output. We can 204
use 205
an implicit return, because we only have one expression inside our closure, so we can get rid of the return 206
keyword. 207
And finally, we can get rid of this whole thing by just simply using the shorthand notation for the parameters 208
and we say the first parameter plus 1. And we can go even further and get rid of these parentheses 209
because our closure is the last item in the list of parameters, i.e,. it's trailing. And if we print the 210
result of this out, you can see that we've mapped our previous array to increase each item by 1. So Swift 211
has three of these really useful high-level functions called map, reduce, and filter. And you can do some 212
really useful things with them. 213
For example, you've realized that your array contains integers, right? But we actually want to convert each 214
of them into strings. 215
So what you can do instead is this. Let's say, we create a new constant code newArray and we set it to 216
equal array.map, and we use the string interpolation method where we enclose the integer inside 217
a string. 218
So now if we print out this newArray, you can see that it's composed of entirely strings. So we've essentially 219
stringified each item in this array by simply writing a very short bit of code and using the map functionality. 220
So in essence, you'll find that closures can be a very succinct way of expressing your code. And in the 221
cases where you're passing in functions into other functions, it can make your code a lot more readable. 222
Now, of course, that depends on your own familiarity with the syntax and other people's familiarity 223
if you're working on a project together. But all in all, closures allow you to package anonymous blocks 224
of functionality and pass it around in your code. 225
So essentially, this is the syntax for a closure. 226
You have the parentheses that surround the input parameters. 227
You have the dash and angle brackets that specify the return type, and the "in" keyword denotes the beginning 228
of your closure body. 229
So this is the functionality that your closure is going to perform and everything is contained inside 230
these curly braces. And the closure doesn't have a name or a funky word and you can recognize it by this 231
syntax. If you'd like to know more about closures or review what we've discussed in this lesson, I recommend 232
having a quick look at this section in the Swift Language Guide. 233
As always, you'll find the link on the course resources page, and I'll see you on the next lesson.