0 1 00:00:02,290 --> 00:00:05,670 Let's take a look at our view controller as it is at the moment. 1 2 00:00:05,680 --> 00:00:11,740 Notice how as we're adding more and more code, the role of the view controller is expanding and becoming 2 3 00:00:11,740 --> 00:00:13,290 less well-defined. 3 4 00:00:13,300 --> 00:00:19,690 For example, our view controller is calculating the BMI, as well as rounding the BMI to one decimal place. 4 5 00:00:19,720 --> 00:00:26,800 So this is a good place to pause and reconsider how we're organizing our code. Our Apple only get more 5 6 00:00:26,800 --> 00:00:33,450 and more complex going forwards. In the final version of our BMI calculator, 6 7 00:00:33,560 --> 00:00:41,060 we're able to change the height and weight. And depending on the BMI that gets calculated will end up 7 8 00:00:41,090 --> 00:00:46,480 with a different colored background and some different advice text. 8 9 00:00:46,630 --> 00:00:53,090 So if we think about how we would organize this information, the advice, the BMI and the background color, 9 10 00:00:53,510 --> 00:01:00,050 well, that seems like it's very much within the "M" of the MVC. And that's what we're going to do in this 10 11 00:01:00,050 --> 00:01:05,510 lesson. We're going to refactor our code to bring it in line with the MVC design pattern. 11 12 00:01:05,510 --> 00:01:13,320 The first thing we should do is, therefore, create our "M" or the model. And there it is in our project, we've 12 13 00:01:13,320 --> 00:01:16,150 already created a Model folder for you. 13 14 00:01:16,200 --> 00:01:22,480 So all we have to do is right-click on it and create a new file inside. 14 15 00:01:22,550 --> 00:01:28,800 Now, in this case, we're going to be creating a new Swift file. And then we're going to call this file 15 16 00:01:28,860 --> 00:01:36,820 CalculatorBrain because it's gonna be responsible for handling all of the logic of calculating our 16 17 00:01:36,820 --> 00:01:37,440 BMI. 17 18 00:01:38,200 --> 00:01:45,220 So inside this new file, I want you to create a new struct called calculatorBrain. So see if you can 18 19 00:01:45,220 --> 00:01:46,330 remember how to do that. 19 20 00:01:46,420 --> 00:01:49,510 Or if not, take a look at the Swift Cheat Sheet to remind you. 20 21 00:01:53,640 --> 00:01:55,650 Now, of course, to create a new struct, 21 22 00:01:55,650 --> 00:02:01,770 we need the struct keyword, and we can even use the code snippet from Xcode to help us. 22 23 00:02:01,770 --> 00:02:08,010 The struct name is going to be CalculatorBrain. And inside the calculatorBrain, 23 24 00:02:08,010 --> 00:02:15,690 We're going to create all of the properties and methods that are required to calculate our BMI to interpret 24 25 00:02:15,690 --> 00:02:20,130 our BMI, provide advice, and provide the appropriate color. 25 26 00:02:20,130 --> 00:02:27,060 So now if we go into our CalculateViewController, let's rip out all of the parts which it's not supposed 26 27 00:02:27,060 --> 00:02:35,880 to be doing.so including the bmiValue, as well as the calculation, and also turning into a string part. 27 28 00:02:36,390 --> 00:02:46,800 And instead, we're going to create a new object called calculatorBrain from our calculatorBrain struct 28 29 00:02:47,250 --> 00:02:50,650 and initialize it with our set of parentheses. 29 30 00:02:50,700 --> 00:02:58,250 Now that we've got access to this calculatorBrain object, we're going to go down into our calculatePressed 30 31 00:02:58,260 --> 00:03:10,140 method, and we're going to say calculatorBrain.calculateBMI. And we're going to pass over the height 31 32 00:03:10,410 --> 00:03:15,340 as one of the inputs and weight as one of the other inputs. 32 33 00:03:15,360 --> 00:03:22,200 Now, this will give us an error right now, but don't worry about it for now. And down here instead of getting 33 34 00:03:22,200 --> 00:03:29,010 the bmiValue which doesn't exist anymore which is why it's saying, "Use of unresolved identifier." We're going 34 35 00:03:29,010 --> 00:03:36,220 to get out calculatorBrain to provide that value, so we'll call getBMIValue. 35 36 00:03:36,330 --> 00:03:42,600 Now, of course, we don't have these methods yet. We don't have a calculateBMI that takes a height and a 36 37 00:03:42,600 --> 00:03:46,650 weight, and then later on it's able to fetch the BMI for us. 37 38 00:03:47,070 --> 00:03:49,020 Well, that is the challenge for you. 38 39 00:03:49,050 --> 00:03:57,780 So pause the video and see if you can make these two errors go away without writing any code inside 39 40 00:03:57,780 --> 00:03:59,190 the view controller, 40 41 00:03:59,190 --> 00:04:06,240 other than these two lines, as well as initializing the calculatorBrain. So keep the code exactly as 41 42 00:04:06,240 --> 00:04:07,500 I have here. 42 43 00:04:07,740 --> 00:04:15,840 And by changing the CalculatorBrain, let's see if you can make this code work and make our error go away. 43 44 00:04:15,840 --> 00:04:17,000 Pause the video now. 44 45 00:04:23,640 --> 00:04:30,630 So the first thing we're going to do is we're going to create this method calculateBMI over in our 45 46 00:04:30,630 --> 00:04:31,680 CalculatorBrain. 46 47 00:04:31,680 --> 00:04:40,320 So let's create a function called calculateBMI and the height needs to be a Float variable, and the 47 48 00:04:40,320 --> 00:04:43,030 weight also needs to be a Float. 48 49 00:04:43,320 --> 00:04:47,700 And then, once those things are passed in, we're going to calculate the BMI. 49 50 00:04:47,700 --> 00:04:55,410 So let bmi equal the weight, divided by height, times height. 50 51 00:04:55,470 --> 00:04:59,750 Now, that we've got the BMI calculated, well, that error will disappear. 51 52 00:05:00,360 --> 00:05:02,030 So let's address the next one. 52 53 00:05:02,040 --> 00:05:05,340 So we also need a method called getBMIValue, 53 54 00:05:05,340 --> 00:05:12,560 so I'm going to copy that. And it should be able to provide us a value in the form of a string, 54 55 00:05:12,570 --> 00:05:16,490 because in our ResultViewController, we're expecting a string. 55 56 00:05:16,560 --> 00:05:19,960 So we need to make this method inside our CalculatorBrain. 56 57 00:05:20,490 --> 00:05:29,850 So let's go ahead and create a method called getBMIValue and it, of course, has to return or output 57 58 00:05:30,000 --> 00:05:32,190 a string data type. 58 59 00:05:32,190 --> 00:05:37,970 So how do we get hold of this BMI value that was calculated in this other method? 59 60 00:05:38,040 --> 00:05:45,480 Well, why don't we go ahead and create a property for our struct, our CalculatorBrain? Let's call it BMI 60 61 00:05:45,870 --> 00:05:55,110 and let's make it a floating point number which is equal to 0.0 to begin with. 61 62 00:05:55,110 --> 00:06:04,050 Now, instead of creating this BMI here as a constant, we're simply going to set this BMI to be equal to 62 63 00:06:04,050 --> 00:06:06,180 the calculated BMI. 63 64 00:06:06,390 --> 00:06:13,320 Now that it's stored in this variable, we can get hold of it in our function here, and we can format it 64 65 00:06:13,410 --> 00:06:18,900 so that we end up with a bmiTo1DecimalPlace. 65 66 00:06:18,900 --> 00:06:23,550 Now, you can, of course, choose a less wordy name than I have, but I just want to make sure that when you 66 67 00:06:23,550 --> 00:06:30,570 look at this, you know exactly what it's trying to do. And this is going to be a formatted string which 67 68 00:06:30,570 --> 00:06:36,060 is going to be accurate to one decimal place, 68 69 00:06:36,060 --> 00:06:43,680 so "%.1f" And then we're gonna pass in the value of BMI as a float, and we should end up with a string 69 70 00:06:43,740 --> 00:06:46,090 which we can use as the output, 70 71 00:06:46,110 --> 00:06:49,500 so returning our BMI to one decimal place. 71 72 00:06:49,650 --> 00:06:54,480 Now, the final problem we have is that we get the error, of course, 72 73 00:06:54,480 --> 00:07:02,640 BMI is a property of our struct and because struct are immutable, in order to change it, we must destroy 73 74 00:07:02,640 --> 00:07:04,680 the old copy and create a new one. 74 75 00:07:04,710 --> 00:07:12,840 So we have to mark this method that changes our property as mutating. And now we should be able to take 75 76 00:07:12,840 --> 00:07:19,200 a look at our CalculateViewController. All of the errors should go away and if we run an app, we should 76 77 00:07:19,200 --> 00:07:23,940 have exactly the same values as before. 77 78 00:07:23,940 --> 00:07:25,800 And that's the solution to the challenge. 78 79 00:07:25,860 --> 00:07:31,740 We're now calculating the BMI in a separate struct and we're returning the BMI to the 79 80 00:07:31,740 --> 00:07:34,110 CalculateViewController as a string. 80 81 00:07:34,110 --> 00:07:39,780 Now, let's take a closer look at our struct. In particular, let's think a little bit more carefully about 81 82 00:07:39,780 --> 00:07:48,930 how we're handling this BMI property. Currently, our BMI property in our calculated brain has a default 82 83 00:07:48,930 --> 00:07:57,000 value of 0.0 to begin with. And we did this because when our CalculatorBrain object gets 83 84 00:07:57,060 --> 00:08:06,660 initialized from this struct, we must have a value for this BMI property. We can either do it here or 84 85 00:08:06,720 --> 00:08:15,120 if we simply declared it as a float data type, well, then we would have to initialize it when we create 85 86 00:08:15,180 --> 00:08:22,260 our CalculatorBrain right here. And round about now, I'm going to get an error right here saying, "Missing 86 87 00:08:22,290 --> 00:08:29,070 an argument for the parameter 'bmi.'" But if you think about it at this point in time, I don't actually know 87 88 00:08:29,070 --> 00:08:35,340 what the BMI is, because I have to wait for the user to toggle the sliders, then press the calculate button 88 89 00:08:35,730 --> 00:08:37,520 before I can actually get a value. 89 90 00:08:38,130 --> 00:08:45,090 So it really doesn't make sense to provide a value for the BMI when we initialize it. 90 91 00:08:45,210 --> 00:08:52,980 So how can we represent this a little bit better? In reality, what this BMI value really should be is 91 92 00:08:53,190 --> 00:09:00,240 it should really be equal to nil to begin with, and only when we actually calculate the BMI should it 92 93 00:09:00,240 --> 00:09:06,990 actually get a value. So does this remind you of something when we have to work with nil values? Of course, 93 94 00:09:07,020 --> 00:09:15,730 we need to turn this into an optional. But as soon as BMI becomes a optional floating point number, well, 94 95 00:09:15,820 --> 00:09:24,070 we've got errors down the line. And previously, all that we've done is simply to just force unwrap when 95 96 00:09:24,100 --> 00:09:25,360 it's safe to do so. 96 97 00:09:26,020 --> 00:09:34,390 But in this case, is it really safe to unwrap this BMI? Because right now, if this method getBMIValue 97 98 00:09:34,390 --> 00:09:40,930 gets triggered before this method, where we actually feel the BMI with a value, at this point, it's still 98 99 00:09:40,930 --> 00:09:41,500 nil. 99 100 00:09:41,950 --> 00:09:49,060 So if we're trying to turn a nil value to a one decimal place string, this is going to crash big time 100 101 00:09:49,060 --> 00:09:52,530 on us. And you can confirm this 101 102 00:09:52,530 --> 00:09:59,390 if you simply just go into the viewDidLoad of all CalculateViewController and call that 102 103 00:09:59,410 --> 00:10:03,460 calculatorBrain method code getBMIValue. 103 104 00:10:03,760 --> 00:10:13,240 And right now if I run my app, pretty much as soon as my view loads up, my app is going to crash and, of 104 105 00:10:13,240 --> 00:10:20,950 course, it crashes on this line right here, because "Unexpectedly found nil while unwrapping an Optional 105 106 00:10:20,950 --> 00:10:21,450 value. 106 107 00:10:24,460 --> 00:10:29,660 And we can see right now this BMI value right here is equal to nil. 107 108 00:10:29,770 --> 00:10:38,740 So therefore, this BMITo1DecimalPlace value, of course, is not going to be able to get calculated. 108 109 00:10:38,800 --> 00:10:45,730 So let's go ahead and remove this offending line of code. And we should also remove this exclamation mark 109 110 00:10:45,790 --> 00:10:49,160 which force unwraps this optional. 110 111 00:10:49,300 --> 00:10:52,550 So how can we solve this problem instead? 111 112 00:10:52,600 --> 00:10:56,070 Are there other ways of working with optionals? 112 113 00:10:56,370 --> 00:11:02,010 Well, to learn more about that, head over to the next lesson where we're going to do a Deep Dive on Swift 113 114 00:11:02,100 --> 00:11:02,790 optionals.