Hey, guys. 1
Welcome to 2
another Swift Deep Dive. 3
And in this Deep Dive, we're going to talk about Swift extensions. Now, recently, I discovered that, apparently, 4
the very first London Underground was built in 1863 which is actually crazy if you think about it. 5
That was like the time of horse and carriages. But, okay, maybe a little bit later than that. 6
But imagine that if you build an underground or a train line, it's pretty likely that as your city grows, 7
you're going to have to add extra lines, extra stations, and extend the train line. 8
So this is kind of similar to how Swift extensions work. Extensions, essentially, allow us to add extra 9
functionality to our existing classes, structures, or other data types. And so that means if you build 10
a project and it was really, really great, but then your boss comes along and says, "Actually, you know what 11
we need to do for the next quarter, we need to add all of these extra capabilities." Instead of diving 12
into the original code and ripping bits out and switching things around, 13
we can simply just extend the functionality of our existing code. So the way that we would create an 14
extension would be something like this. 15
We would have the extension keyword, and then we can have the name of an existing type, 16
so that could be a class, that could be a struct, that could even be a protocol. 17
And then inside the extension, we would get to add some new functionality to that type. So here I have 18
a blank playgrounds which I've named extensions. 19
And notice how I'm importing UIKit now because I'm actually going to use it in the demo. 20
So the first thing I want to show you is how we can extend a custom type. 21
So for example, we know that we can create a double, let's call it myDouble and set it equal 22
to 3.14159, 23
the value of pi, 24
and we can round it down to however many digits we want to by turning it into a string, 25
let myRoundedDouble = String with a format of "%'.1f" 26
Let's say, we wanted to round it to just one decimal place, and then we put in the myDouble as the argument 27
that we want to round. 28
And now if I simply print myRoundedDouble and run my code, then you can see that I get 29
3.1. 30
So that's rounded to one decimal place. Success. 31
But what if I wanted instead to not create a string? 32
What if I just wanted another double, 33
but it was rounded to however many digits 34
I wanted it to be. 35
Well, in that case, what would I have to do. 36
Well, I could use an existing function that all double data types have access to, 37
which is called .round. 38
but unfortunately, what .round does is it will round it down to a whole number. 39
So let's change this "let" to a "var" and run our code and you can see that once that's rounded, 40
myDouble.round has now produced three, 41
but I actually want to specify the number of decimal places. 42
So, for example, I could say round to: and then I could say three places, 43
so put an integer in there. 44
But this method round to: however many number of decimal places, doesn't exist for the double data type. 45
So what can we do? 46
Well, we could simply create it ourselves, 47
right? 48
Let's create an extension up here and we start off using the extension keyword, 49
and then we specify the type that we want to extend. 50
So we want to extend the native Swift double data type. So it doesn't even matter that we didn't create 51
this data type or the fact that we don't actually know how it's done, all we need to know is that we're 52
going to get hold of the existing double data type, we're going to give it some new functionalities, a new 53
superpowers. And this functionality is going to be in the form of a method which is going to be called 54
round as well. 55
And notice how swift allows you to create new functions that have the same name as an existing function 56
like the round function as long as we have different parameter names. 57
So in this case, I'm going to have an external parameter name of "to" and an internal parameter name of 58
"places." 59
So that means we round to however many number places and this is going to be an integer data type. 60
So now within the body of my function, I have to figure out how to actually do this. 61
Well, if we take a look at our double, if we want to be able to use the round method, but modify it to 62
be able to round to a certain number of decimal places, what we would have to do is, first, we would have 63
to take my double, and then multiply the existing value by, say, let's say that we wanted to round it to 64
three decimal places, right? 65
Then we would have to multiply it by a thousand, 66
so 10 x 3. 67
And then we would take this new value of my double and then apply round to it. 68
And finally, we can take my double and set its new value to be equal to the current value divided by 69
a thousand. 70
And when I run all of that code, you'll see that in the first step, 3.1415 71
becomes 3,141, and then we round that off to an integer 3142, and then we divide it by a thousand again to 72
get 3.142. 73
So that's the process. 74
So let's try and replicate this inside our round(to places) 75
function. So first, we have to figure out how can we get 10 if they only wanted to round to one decimal 76
place, 100 if they wanted to round to two decimal places, and a thousand 77
if they wanted to round to three decimal places. 78
Well, we could simply create a number 79
called the precision number, let's call it, and we can set it equal to 10 to the power of the number of 80
places they want to round it to. 81
So it's effectively saying that if they want round it to three decimal places, then it's going to be 82
ten times ten times ten. But it's much easier using the power function. And then we want to set 10 to 83
the power of the number of places. So if we run this code as it is and we try to round my double to three 84
decimal places, you can see that right now precision number is equal to a thousand. 85
Whereas if I tried to round it to five decimal places, then precision number is going to be 10 to the 86
power of 5 which is one hundred thousand. 87
So that's working. And let's change that back to three, and now let's try to use this precision number 88
by implementing this calculation. 89
The next thing I'm going to do is I'm going to say let n = self which is the current value of 90
the double. 91
Notice how we're inside the definition of the double, 92
so when we create a new double like so, then this will be the value of self right here. 93
So if we let n = self, and then we say n = n * precision number. 94
So this line right here. 95
But instead of using a thousand, it depends now on the number of places that we want. And, of course, if 96
we want to change this "n," we can't have it as a "let," we must have a "var." And then we're going to say 97
n.round to round it down, and then we can write n = n / precisionNumber. 98
And finally, we're going to return n as the output of this round method and we can specify that it 99
should be a Double that comes out. The very last thing we have to do is we have to convert this "places" 100
which needs to be an integer 101
when the user is calling it because we don't want them to try to round it to 2.3 places. 102
That doesn't make any sense. So it has to be a whole number. But once it's inside here, we want to make 103
sure that we're all working with the same data type. 104
So in this case, because we have an integer to the power of an integer, we end up with a decimal number. 105
But what we actually need is this to be a double so that we can multiply a double by a double because, 106
otherwise, our data types won't match and Xcode won't let us actually do the calculation. 107
So in order to solve this, all that we have to do is just convert our places from an integer to a double. 108
And now you'll see that we can now delete this code here and I can simply round myDouble using this 109
new round method I've added that all doubles can now use, and I can specify the number of decimal places. 110
And now I get 3.12 if I round it to three decimal places and I will get 3.1 111
if I round it to just one decimal place. 112
And now that we've added this extension, whenever we create a new double, we can simply tap into that 113
capability that we've now given to all doubles round to a certain number of decimal places. 114
Now, because the double actually comes from the Swift Standard Library which is, in fact, open-source, so 115
you can actually see the source code of it when you head over to the GitHub repository that Apple has. 116
Now, if you dig through all of this, you'd actually be able to see the code of how it's implemented. 117
But with other things, especially things that come from UIKit, say, our UIViewController, UITextField, 118
that, of course, is not open-source code. 119
So we actually can't see how it's implemented at all and we don't have access to the code that's used 120
to create a UILabel or a UIButton. 121
But what we can do in this case is we can, in fact, extend those classes even though we don't actually 122
have access to it and we can't see it. 123
Let me demonstrate. 124
So let's create a new button which is going to be created from the UIButton, and then let's initialize 125
it with a frame. 126
So if the frame is going to be a CG Core Graphics rectangle and I'm going to give it just 0, 0 for position, 127
width and height, I'll say, it's 50 by 50. 128
So now that we've created our button, let's go ahead and give our button a background color, so that it's 129
actually visible. 130
So I'm going to say it's a red background color. 131
And now if I run my code in playgrounds and click on this little square here right now, it'll actually 132
show me that element that I've just created, my red 50 x 50 UIButton. 133
So now what if I wanted to have a round button, let's say, I wanted a circular button? Well, I could simply 134
just tap into the button's layer property .cornerRadius and set that equal to, I don't know, maybe 25, 135
and then, say, that button.ClipsToBounds = true. 136
Well, now once these two lines of code have run, you can see that my new UIButton is now a circle because 137
the corner radius is now 25 pixels 138
and if the width is 50, then that means it's actually just a circle. 139
But I don't want to do this every single time I want to create a new circular button. 140
So why don't I add this capability to the UIButton? So I can do that using my good old extension 141
and I'm going to extend this code for the UIButton class which I can't see. 142
I don't know what it looks like. But I do know that I want to have a function called makeCircular. 143
And in this case, all that it's going to do is it's going to take the current button, so we can address 144
that with "self," 145
set the clips the bounds to true, and then I'm going to set the self.layer.cornerRadius to be equal 146
to the current buttons frame.size.width, and I'm going to divide that by half to get a circular 147
button. 148
So, now I can use this extension by creating my button after I've created my extension. And now I can 149
say button.makeCircular. 150
This is now a valid method that all UIButtons have. 151
And when I run it, you'll see exactly the same thing happen. 152
I now have a circular button created. 153
Now, another thing that we can do with extensions that's really handy is we can actually extend our protocols. 154
If we simply define our extension, adding in a protocol, instead of a class or a struct, we can't actually 155
define the default behavior for the required methods. 156
So here I've got that file the Protocols Demo that we created earlier on when we were learning about 157
Swift protocols. 158
And one of the things that you might have worried about is that when we have a class, say, Bird, which has 159
the functionality of fly, well, once we've defined this method, say, "The bird takes off into the air," 160
then we can actually inherit this capability by inheriting from the Bird class. 161
And now we don't actually need to implement a fly method and we can already simply say 162
myEagle.fly. 163
And that would actually call the method in the Superclass Bird and we have it because we're inheriting 164
from Bird. 165
Now, how can we do something similar with protocols? 166
How can we avoid having to write out the same method implementation if we wanted to keep it the same? 167
Well, in this case, well, we've created our protocol CanFly with a required method of fly, 168
we can actually define a default implementation by extending our protocol CanFly. 169
So now that we've got this extension of our CanFly protocol, we can implement the fly functionality, 170
and we can say that whenever a class or a struct adopts this extension, it automatically has access to 171
a default version of the fly method which just says, "The object takes off into the air." 172
So, now once we adopt CanFly, we don't actually have that error anymore which says, "Hey, you said you can 173
fly, but you haven't adopted the fly method," because we've already got a default implementation that it's 174
going to fall back on. 175
So, now when we say something like let myPlane = Airplane, and because this airplane already adopts 176
the CanFly protocol, I can simply say myPlane.fly and it will already know how to fly because 177
it's just going to look at the default implementation that comes from that protocol CanFly. 178
And once my code runs, you can see that's the one that it's executing. 179
So this is also what's happening when we adopt our UITextFieldDelegate, but we're actually not required 180
to implement any of these text field delegate methods. 181
You can see that even if I comment them out and I hit command B to build, and we've got our UITextFieldDelegate 182
delegate implemented, I'm not getting any errors here. 183
These are all optional because they already have a default implementation. 184
And in fact, when you take a look at the UITextField by option clicking on it, you can see that it's 185
got loads of delegate methods. 186
Imagine having to implement all of them. But in this case, we don't have to because they all have a default 187
implementation. So that's the core functionality of extensions. 188
You've created something already. 189
You want to give it more functionality. 190
You want to extend the train line. You create an extension of that data type and add the new functionality 191
inside the curly braces. 192
The last thing I want to talk about on the topic of extensions is that they can actually be really handy 193
for organizing our code as well. Because for every data type, we can create an extension that adopts a 194
different protocol. 195
So for example, in our code here, if we have a lot of delegates, you can see that it's going to start building 196
out in this class definition here. And our code is already getting quite long and quite messy. 197
So we can simply extend our WeatherViewController class. And for each extension, adopt a different delegate 198
protocol, and then have all of the code organized into separate extensions. 199
So that's what we're going to do in the next lesson.