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