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.
