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.