0 1 00:00:00,980 --> 00:00:01,290 All right. 1 2 00:00:01,290 --> 00:00:07,030 So we're going to continue building our calculator but, firstly, make sure that you've deleted that 2 3 00:00:07,310 --> 00:00:11,050 Test.swift file if you are following along with me and you created it as well. 3 4 00:00:11,050 --> 00:00:13,840 We don't need it and we definitely don't want it 4 5 00:00:13,840 --> 00:00:17,300 modifying our isFinishedTypingNumber variable. 5 6 00:00:17,320 --> 00:00:24,190 So, currently, our app works, namely, that it can accumulate numbers, but it doesn't actually do anything, 6 7 00:00:24,190 --> 00:00:24,490 right? 7 8 00:00:24,490 --> 00:00:31,750 When I say something like, you know, 12+3 = Nothing happens. It doesn't do any calculations 8 9 00:00:31,750 --> 00:00:34,260 yet because we haven't told it to. 9 10 00:00:34,270 --> 00:00:37,180 So let's go ahead and add in that logic 10 11 00:00:37,210 --> 00:00:40,920 and that functionality to make our calculator work. 11 12 00:00:40,930 --> 00:00:48,270 So if I pull up the calculator that's on my Mac, you can see that we've got three buttons up here. 12 13 00:00:48,350 --> 00:00:52,590 And the first button simply clears the board. 13 14 00:00:52,600 --> 00:00:55,520 It turns everything to zero. 14 15 00:00:55,780 --> 00:01:03,640 The second button will turn whatever it is that I've got in the display to the negative version of it. 15 16 00:01:03,640 --> 00:01:07,910 So if it was 2, then it becomes minus 2. 16 17 00:01:07,920 --> 00:01:10,050 But if it was minus 2, then it becomes 2. 17 18 00:01:10,330 --> 00:01:12,810 And the third one turns everything into a percentage, 18 19 00:01:12,820 --> 00:01:14,980 so it divides it by 100. 19 20 00:01:14,980 --> 00:01:18,960 So 90 will become 0.9, et cetera. 20 21 00:01:19,600 --> 00:01:27,610 So these are a little bit simpler because they only need one number to be able to execute the functionality, 21 22 00:01:28,000 --> 00:01:34,930 whereas these four are a little bit more complicated because we need to be able to say 5, then we hold 22 23 00:01:34,930 --> 00:01:40,200 that number in memory, and we press one of these buttons 5+ 23 24 00:01:40,240 --> 00:01:43,330 then now we've got somewhere which has got five stored. 24 25 00:01:43,420 --> 00:01:50,530 We've got the operation plus stored and we now have to add another number 2, and then we press the equal 25 26 00:01:50,530 --> 00:01:53,110 sign, and it does all of that calculation. 26 27 00:01:53,110 --> 00:01:56,730 So these four are a little bit more complicated than these three. 27 28 00:01:56,740 --> 00:01:59,470 So let's first tackle these three. 28 29 00:01:59,470 --> 00:02:06,580 So the place that we want to do this is inside our calcButtonPressed IBAction because what we want 29 30 00:02:06,580 --> 00:02:16,870 to happen is to, say, for example, if I typed 10, and then I pressed the percentage sign, then this IBAction 30 31 00:02:16,870 --> 00:02:24,620 will get triggered, and I want to be able to turn that 10 into 0.1, so divided by 100. 31 32 00:02:25,000 --> 00:02:33,310 So first things first, I need to get the value that's inside my label here as a number, as a double, ideally, 32 33 00:02:33,630 --> 00:02:36,890 because we'll be working with decimal places later on as well. 33 34 00:02:37,090 --> 00:02:44,090 So let's say let number = displayLabel.text, 34 35 00:02:44,140 --> 00:02:48,010 so now I'm taking whatever is currently in the display label 35 36 00:02:48,070 --> 00:02:54,060 when I pressed one of these buttons and I'm saving it to a constant code number. 36 37 00:02:54,100 --> 00:02:58,650 Now, this number is a local variable at the moment already. 37 38 00:02:58,660 --> 00:03:03,730 So it's only accessible inside the scope of the calcButtonPressed, 38 39 00:03:03,730 --> 00:03:07,230 so I don't have to also add a private in front of it. 39 40 00:03:07,450 --> 00:03:11,320 However, if I was creating this in the global scope, 40 41 00:03:11,350 --> 00:03:18,310 so if you're confused about global and local, make sure you have a look back at the video on scope. 41 42 00:03:18,310 --> 00:03:25,300 We spoke about this in more detail. But, essentially, we don't have to turn any local variables into private 42 43 00:03:25,300 --> 00:03:33,400 vars because it's already limited in scope and only accessible inside the block where it was declared. 43 44 00:03:33,700 --> 00:03:40,330 So, now that I've got that number saved, I want to be able to turn it into a double. Because, currently, 44 45 00:03:40,330 --> 00:03:44,040 if I hold down alt and click on number, you can see it's just a string, 45 46 00:03:44,080 --> 00:03:44,570 right? 46 47 00:03:44,680 --> 00:03:48,680 Because it's set to the text that's inside the displayLabel. 47 48 00:03:48,700 --> 00:03:54,210 So this will be currently equal to, essentially, the string 3 48 49 00:03:54,330 --> 00:04:03,460 and I want to turn it into the double 3.0. So I can do that by just doing a simple cast. So I can say Double 49 50 00:04:04,450 --> 00:04:09,680 and then I can wrap whatever it is that I want to cast into a double inside some parentheses. 50 51 00:04:09,880 --> 00:04:14,610 So, now if I check number, again, you can see it's now a Double? 51 52 00:04:14,710 --> 00:04:18,620 But it's not just a regular double, it's actually an optional double. 52 53 00:04:18,630 --> 00:04:25,900 And the reason for that is because if I'm turning text into number, there's a lot of cases where that's 53 54 00:04:25,900 --> 00:04:27,080 not possible. 54 55 00:04:27,130 --> 00:04:35,140 So, for example, if I tried to turn the string "A" into a Double, then what is that going to be? 55 56 00:04:35,380 --> 00:04:37,910 I have no idea and nor does the computer. 56 57 00:04:37,960 --> 00:04:41,790 So that's a case where this could fail this casting process. 57 58 00:04:41,800 --> 00:04:47,800 So that's why when you cast a string to a double, it will always produce a optional Double. 58 59 00:04:47,800 --> 00:04:49,600 Now, the opposite case, 59 60 00:04:49,600 --> 00:04:52,930 Let's say, we try to create a string, right? 60 61 00:04:53,320 --> 00:05:02,400 And we set that to equal String casting, something that was a number, say, 12, this is going to produce 61 62 00:05:02,490 --> 00:05:09,660 a normal String, a bog-standard string because when you cast from number to string, it's always possible. 62 63 00:05:09,660 --> 00:05:13,750 All it does is add some quotation marks around it and, bam, you've got a String. 63 64 00:05:13,920 --> 00:05:16,310 But the opposite fails occasionally. 64 65 00:05:16,440 --> 00:05:21,990 So, now we have this optional number which makes it very difficult to work with. 65 66 00:05:21,990 --> 00:05:26,240 So we want to either unwrap it or we have to do something with it, right? 66 67 00:05:26,310 --> 00:05:35,690 Now, one option is to simply unwrap this by putting a exclamation mark at the end of this group. 67 68 00:05:35,700 --> 00:05:42,750 So, now what this says is that I'm definitely sure that there is text inside the displayLabe, and that 68 69 00:05:42,750 --> 00:05:43,320 makes sense, 69 70 00:05:43,320 --> 00:05:43,630 right? 70 71 00:05:43,650 --> 00:05:47,590 Because even if we don't put in numbers, it's still got a zero in there. 71 72 00:05:47,610 --> 00:05:50,550 So that's a valid unwrap. 72 73 00:05:50,550 --> 00:05:59,950 So next, we're turning it into a double and we're saying that it's always going to work, namely whatever 73 74 00:06:00,120 --> 00:06:04,120 is in here is always going to be able to be turned into a double. 74 75 00:06:04,160 --> 00:06:09,420 And this is also a valid assertion because where does the number here come from, 75 76 00:06:09,420 --> 00:06:12,500 well, it comes from whatever I do in here. 76 77 00:06:12,630 --> 00:06:19,690 And all of this code only tries to put the numValue which is the current title of the sender, 77 78 00:06:19,830 --> 00:06:27,350 so one of the number buttons, any of these ones into here. And they can all be turned into a number. 78 79 00:06:27,540 --> 00:06:34,100 So if you were to force unwrap this, it's also not exactly wrong, because the logic is kind of valid. 79 80 00:06:34,350 --> 00:06:39,010 Now, the only problem is if you screw up on your linkages, 80 81 00:06:39,030 --> 00:06:41,570 so your IBOutlets and your IBActions. 81 82 00:06:41,580 --> 00:06:45,920 So there's many ways in which this can actually fail. 82 83 00:06:46,170 --> 00:06:52,590 Now, failing and crashing your app is not necessarily a bad thing because it allows you to figure out 83 84 00:06:52,590 --> 00:06:59,250 what other things are unexpected, and for you to test your app, and also to address all of these unexpected 84 85 00:06:59,250 --> 00:07:00,180 scenarios. 85 86 00:07:00,180 --> 00:07:07,040 Now, what you don't want is to, say, use an "if let" and just not catch this error 86 87 00:07:07,110 --> 00:07:11,330 if it does happen because it'll skip the code instead. 87 88 00:07:11,490 --> 00:07:19,800 So let's say that if instead of linking all of the number buttons to this numButtonPressed, I accidentally 88 89 00:07:19,800 --> 00:07:22,820 also linked one of the text buttons to it. 89 90 00:07:22,860 --> 00:07:29,760 So let's just delete the current linkage to calcButtonPressed and let's add in a new link to 90 91 00:07:29,760 --> 00:07:31,400 numButtonPressed instead. 91 92 00:07:31,420 --> 00:07:36,370 And now this IBAction is linked to all of the numbers as well as this button. 92 93 00:07:36,540 --> 00:07:42,300 So that means that when I press this button, it's going to try and put that into the displayLabel, and 93 94 00:07:42,300 --> 00:07:48,440 then, later on, it's going to try and turn that text into a Double which will fail. 94 95 00:07:48,480 --> 00:07:51,110 So let me show you what will happen. 95 96 00:07:51,150 --> 00:07:59,010 So at this stage when I type numbers and I press any of these calc buttons, nothing happens. 96 97 00:07:59,010 --> 00:08:07,350 It's all working fine. But if I press this button and the string gets put into the display label, when 97 98 00:08:07,350 --> 00:08:14,550 I press any of these calc buttons, our app is going to crash and it's going to show us that the line 98 99 00:08:14,550 --> 00:08:17,760 that caused this crash is this one, 99 100 00:08:17,820 --> 00:08:25,060 and the problem is a "Fatal error: Unexpectedly found nil while unwrapping an optional." 100 101 00:08:25,500 --> 00:08:32,460 Now, the optional here is this one Double question mark, and it's unwrapped, because we assumed that it 101 102 00:08:32,460 --> 00:08:36,630 would always be possible to turn this into a Double. 102 103 00:08:36,690 --> 00:08:40,140 Now, because of what I've just done just now, my little mishap, 103 104 00:08:40,170 --> 00:08:41,770 well, that's not possible anymore. 104 105 00:08:41,790 --> 00:08:44,970 Plus, minus does not become a number. 105 106 00:08:45,240 --> 00:08:50,040 So you might think, well, you know that means that we should never force unwrap anything because it's 106 107 00:08:50,190 --> 00:08:51,340 always bad, 107 108 00:08:51,420 --> 00:08:55,970 and instead, we should use maybe something like optional binding, right? Use an "if let." 108 109 00:08:56,160 --> 00:09:00,890 Well, I would argue that actually using an "if let" is not always the best option 109 110 00:09:00,930 --> 00:09:06,990 and sometimes you do want your code to crash, especially when you're testing it and when you're developing 110 111 00:09:06,990 --> 00:09:07,370 it. 111 112 00:09:07,500 --> 00:09:14,220 The reason for that is because when it crashes, it alerts you and you are forced to figure out why it 112 113 00:09:14,250 --> 00:09:16,290 crash and what caused it. 113 114 00:09:16,290 --> 00:09:19,820 Now, let's say I didn't want to force unwrap this. 114 115 00:09:19,950 --> 00:09:23,360 What are the other options that I have in my arsenal? 115 116 00:09:23,400 --> 00:09:24,240 Well, let's see. 116 117 00:09:24,300 --> 00:09:27,020 Let's try and use this optional, right? 117 118 00:09:27,030 --> 00:09:29,270 Let's try number + 2 118 119 00:09:29,310 --> 00:09:31,510 and I'm going to use command B to build. 119 120 00:09:31,680 --> 00:09:38,050 Now, I'm going to get a build failed and if I click on this little button here, it tells me that value 120 121 00:09:38,070 --> 00:09:42,530 of optional type 'Double?' must be unwrapped to a normal double 121 122 00:09:42,870 --> 00:09:46,850 in order to use it, for example, in this calculation. 122 123 00:09:47,070 --> 00:09:49,170 Now, there's two options that it's giving me. 123 124 00:09:49,170 --> 00:09:54,790 One is to force unwrap using the exclamation mark which is what we did previously, 124 125 00:09:54,810 --> 00:10:01,050 and if you click on this fix, you can see that it will force unwrap this number, or you can also force 125 126 00:10:01,080 --> 00:10:02,640 on unwrap over here. 126 127 00:10:02,790 --> 00:10:10,560 Now, the other option that is giving me is to use what's called a nil-coalescing operator using two 127 128 00:10:10,560 --> 00:10:16,530 question marks to provide a default when the optional value contains nil. 128 129 00:10:16,530 --> 00:10:24,420 So in this case, what I can do is if I can turn this string into a Double, then I can put two question 129 130 00:10:24,420 --> 00:10:29,960 marks and say, well, in that case, instead of giving back nil, let's just give 0. 130 131 00:10:30,120 --> 00:10:32,990 So now let's retest our app. 131 132 00:10:33,150 --> 00:10:37,560 And, again, I'm going to put the +/- sign into the displayLabel. 132 133 00:10:37,620 --> 00:10:42,570 I'm going to press one of the calculation buttons and you can see nothing happens. 133 134 00:10:42,570 --> 00:10:49,290 My app doesn't crash and it just continues, and it just continues along as if nothing happened because 134 135 00:10:49,350 --> 00:10:52,730 I managed to set that number to zero. 135 136 00:10:52,890 --> 00:10:59,610 So I would argue that this is actually worse than if you actually let your app crash when it's doing 136 137 00:10:59,610 --> 00:11:01,770 something unexpected. 137 138 00:11:01,770 --> 00:11:09,660 Now, when we leave this as a forced unwrap, it can be a little bit difficult to figure out why it crashed 138 139 00:11:09,990 --> 00:11:13,490 when our string can't be converted into a double. 139 140 00:11:13,740 --> 00:11:18,370 So an alternative here is to instead use a "guard." 140 141 00:11:18,390 --> 00:11:21,810 So we're saying guard let number = Double. 141 142 00:11:22,110 --> 00:11:32,310 And if this conversion process from string to double can't happen, then we're going to say, else throw 142 143 00:11:32,430 --> 00:11:34,190 a fatalError. 143 144 00:11:34,490 --> 00:11:40,890 And there's two types of fatalError where you've got the simple fatalError and it stops your app from 144 145 00:11:40,920 --> 00:11:43,010 executing, namely crashes your app, 145 146 00:11:43,170 --> 00:11:46,260 or you can have the one where you can put in your own message. 146 147 00:11:46,410 --> 00:11:47,590 So that's what we're going to use. 147 148 00:11:47,610 --> 00:11:55,530 So if we can't convert our displayLabel.text to a Double successfully and turn this into 148 149 00:11:55,530 --> 00:12:05,610 a nonoptional Double, then we're going to say, "Cannot convert display label text to a Double." 149 150 00:12:05,790 --> 00:12:12,180 So, now when our app crashes, we will get a far more explicit error message, 150 151 00:12:12,310 --> 00:12:16,950 and this means that three months down the line when we can't actually remember when anything does in our 151 152 00:12:16,960 --> 00:12:23,460 app any more, then if we typed this into the displayLabel and tried to convert it into a double, then 152 153 00:12:23,460 --> 00:12:24,940 our app crashes, 153 154 00:12:25,170 --> 00:12:31,540 and also we get a "Fatal error: Cannot convert display label text to a Double" 154 155 00:12:31,650 --> 00:12:37,360 which should hint to us that, oh, this must be related to this part, right? 155 156 00:12:37,410 --> 00:12:40,090 So, now that we've tested this and we fixed it, 156 157 00:12:40,170 --> 00:12:43,460 let's make sure that we don't persist this error. 157 158 00:12:43,470 --> 00:12:50,870 So let's delete that connection to numButtonPressed, and instead, make sure that we connect it again 158 159 00:12:50,940 --> 00:12:56,280 by holding down the control, and clicking and dragging, and we're going to connect it to calcButtonPressed, 159 160 00:12:56,280 --> 00:13:00,400 so that it'll be addressed in the calcButtonPressed IBAction.