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