Advanced US 190 Folder Collection
After playing the video, you can click or select the word to look it up in the dictionary.
Loading...
Report Subtitle Errors
Stanford University. >> Okay, well, welcome
to Stanford CS193P. This is spring quarter of 2016, and
this is lecture number 2. And today, we are going to talk
about MVC, okay, I'm gonna try and really briefly cover that
because I know only about half of you know what MVC is, and
it's a very important part of doing any iOS development.
And then after I'm done with that, I'm gonna continue
the demo from last time, we'll use MVC and learn yet
some more things about Swift and Objective C, all right?
All right, so MVC, what is it? As I mentioned last time, it's
essentially a way of dividing up your application or
your source code into three different camps,
okay? The three camps pictured here are the model camp.
The model camp is what your application does. Okay,
nothing about how it's drawn on screen or
anything like that, okay. It's not how it's displayed,
it's just what it is. So for a calculator app,
what it is it's a calculator, so the model is probably gonna
be the part that does calculating,
okay. Next piece is the controller.
The controller is how your model is displayed on screen.
Okay, it's kind of the how. This is basically all your
UI logic, goes into your controller, all right? And
the view, you can think of as your controller's minions,
okay, the things that the controller's gonna use to put
things on screen. So that's buttons and labels and
tables and all those kinda things that the controller
needs to display what's in the model and
to get input from the user to update the model as well,
okay? So those are the three camps.
Now it's one thing to decide where things go
based on the description of the camp, but a really
important piece of it is the communication between camps,
what's allowed, what's allowed, what's not.
And when communication is allowed, how do you do it,
okay, in iOS? How, how is that communication facilitated?
So, to help with this, I've kind of, drew, drawn here this
little Y in the middle. It's kinda like road signs, okay?
It's like double yellow at the bottom there is don't cross.
And then solid white is yeah, you can cross, but
you're not really supposed to generally do this without
being very careful.
the traffic is going in the same direction, so
you can pretty much crossover.
Probably wanna put your turn indicator on,
but off you go. Okay, so let's talk about how that works for
these three camps. First let's talk about controller talking
to the model. The controller can talk to the model all it
wants. It knows everything about the model.
It can send any message it wants to the model.
The controller is in complete control of the model. Okay,
and the controller needs that because the controller's
job is to present what's in the model to the user or
to get information from the user and update the model.
So it needs full control, so that's a full green arrow,
dashed white road sign, road line there can do anything at
once. Same thing on the other side, the controller obviously
needs to be able to use its minions however it wants to
display the model. And most of the time, the connection
between the controller and its minions is via an outlet.
And you remember we had an outlet on Monday, right?
It was the display?
You remember that, it was a var instance variable.
Display with an optional UI label. And that connection is
how the controller was talking to it's view.
That label, that UI label was part of it's view.
It was a minion in it's view. So that's
full green communication, kind of do whatever it wants,
controller knows everything about both sides.
It has to. Let's talk about the model in the view.
Those never speak to each other. Why is that? Simple,
the model is UI independent,
so there's absolutely nothing it has to say to the view,
which is completely UI dependent,
that's all the view is. The view is just the minions
of the controller. And so, you know, it makes no sense for
these two to talk to each other. So that fire, that's
double yellow line, don't ever do that in this class, okay?
No communication there at all. Okay, all communication in
the model, in the view goes through the controller.
All right, what about from the view to the controller?
Can the view, like a label and stuff like that,
talk to its controller? Well, yes and
no. The problem with the view is all the minions
in there are generic objects like UIButton or UILabel.
Those were written by Apple years ago.
They know absolutely nothing about a calculator.
So there's way to kind of for them to talk to a calculator
and know it's a calculator. Okay, so there's limited
communication between the view and the controller. But
off course the view needs to talk to the controller because
it's the controller's minions and things happen in the U.I.
and need to tell the controller what's going on so.
The kind of communication we have there has to be blind and
structured.
Blind meaning the objects in the view don't know what class
they're talking to. 'Kay?
Cause view, buttons don't know anything about calculator view
controllers. And it's structured because
since there is no knowledge of the Objects on either end.
They have to communicate in a well-defined, pre-defined way,
okay. So let's talk about some of those structured ways that
the view minions talk to the controller. One of them
you learned last time is target action, okay. So
target action's very simple, the controller hangs a target
on itself by defining a method with at sign ib action on it,
usually, in Xcode, so
that little dot will work, okay. And
then the view when it wants to talk to the controller simply
calls that method and that connection. Okay.
The action being sent, from the view controller,
is wired up usually with control drag. You saw us do
that. It can be done in code. But 99% of the time we control
drag to create this target action connection.
So there's an example. Very simple communication between.
Menu in the View like the UI button and the Controller,
the other method. Okay? Simple one. All right, what else,
what other kind of communication we had
besides Target action? Well, sometimes the View needs to
communicate something a little more complicated than just
I was touched or something like that. Okay. For example,
it might be a school view, that's a generic view minion.
And it might need to tell the controller,
hey, this guy just started scrolling. Okay.
Or the person zoomed into this zoom scale. All right.
So let's notify the controller cuz the controller might need
to know that and react to that, okay.
Maybe it effects the model when you zoom in or out.
Also maybe the view like the scroll view needs to make sure
it's okay to do something, like if the scroll view says
should I allow vertical scrolling right now?
Maybe it wants to ask the controller that. So you have
a lot of messages that have words in them like should,
will, and did, okay? That the minions wanna ask
questions of the controller involved with controller.
Okay? So, [COUGH] This is done via what's called
a delegate. And we're gonna talk about delegation next
week. And the word delegate is appropriate here because
it's essentially the view's minions are delegating
some responsibility to the controller Okay. The way this
is implemented is very simple. Delegate, the delegate is just
a property in the view and that property,
you might ask, what's the class of that property,
because the view doesn't know anything about the calculator
view controller. The answer is, it's not gonna be a class.
It's going to be what's called a protocol. Okay, and
we're gonna talk about what protocols are.
Protocols are basically just a description of a bunch of
methods that the other guy promises to implement. Okay,
and so if you can imagine if the controller
would promise to implement these will, should, and did
things, then the viewer could talk to it even if the view
doesn't know what class it is. Okay, no similarly There's
an important aspect of MVC which is the views,
okay, the view can not own the data they are displaying. Now,
how are they going to display it if they don't own it?
Well, they're going to ask for it from the controller all
the time and the controller is going to get it from
the model. Okay, so that's another kind of protocol but
instead of will did and
should you've got messages in that protocol like give
me the data at this location and how many pieces of data
are there, okay? Things that are asking about the data so
the viewer can figure out what's going on and
display it, okay. And that's also done with delegation,
although we call that Delegate the Data Source. Okay, so
there'd be another property on some views called
the Data Source, which is this protocol based pointer,
basically, to another object and
the controller sets itself as that so it can get involved.
And providing the data for the view.
Kay? So those are the ways that the view can communicate
to the controller. You can see they're all pretty defined,
well-defined ways they're not just open ended.
Mkay? Now, this leads to a situation where
the controller's job can be described as interpreting and
formatting the model data for the view. Okay.
It also interprets view input for the model. So
it's an interpreter between both. That it,
controller job so that's really where all your UI logic
is in there. Okay, how bout the model?
Can it talk directly to the controller?
Absolutely not because the controller is
your UI logic.
The model is UI independent. So there's absolutely no way
the model could have anything to say to the controller.
However, what happens if the model,
which is UI independent, has some data that changes,
okay? So it's maybe the model is representing data on
a network. In some ways change is gonna be on the network and
it's changing. How does the model let the controller know?
Well, to do this we use what we call a radio station model,
okay? So the radio station is just a thing that
the model can set up,
set up its own radio station.
And it broadcasts on that radio station whenever,
whenever anything interesting happens. Okay,
and then the controller just tunes in to that station. So
the model is not really talking to the controller.
It's just talking to anyone who wants to know.
What's going on in the model. Now all that communication on
that rad, radio station since it's done by the model has
nothing to do with UI. It's about the data in the model.
I have new data, my data changed, those kind of
messages are going out on this radio station, okay?
Now other greater stations can be worked between other camps
besides the model and the controller, and
some have asked, hey, can I just create a view that tunes
into the model directly, and short circuit the controller?
And the answer is no, you don't wanna do it that way.
Okay? You would want to have the controller tuning in
to the model. And having the controller set up
this generic view thing to display the data.
Question? [INAUDIBLE] Standpoint, it's easy to
understand the controller view log model just like your idea
of how this things implemented in the software?
>> So the question is, so
it's easy to understand what the controller and
view are, they're displaying the UI.
The model is less easy to kinda conceptualize,
what that is, so what is the model? Really the model
it takes a little more design but to design the model you
have to think about what is it my app does fundamentally,
independent of how it would be displayed. Like imagine I
wanted a calculator and had a command line interface where I
could type five times three equals and
it would work. Okay, well that's a user interface but
the calculation, the actual multiplication and stuff,
that would be in the model. So the model is more about
trying to understand what it is your application does,
not how it's displayed. That's the separation that we have to
do in this design. >> So it's
kind of like an algorithm? >> Yeah,
it's more of the algorithms, the data, the databases and
stuff like that are more in the model. And you'll see it
by experience. we'll deal with the calculator today and
you'll get an example how that plays out. Okay.
Now, this all only builds one MVC, okay? One MVC,
generally an iOS, controls one iPhone screen or
maybe on an iPad it's two pieces or
three different pieces on the iPad screen. In other words
this is only controlling a little part of your app.
To build a real app we have to take these MVCs,
make a whole bunch of them and then combine them, okay?
That's how we make a big app, all right? Now, when we do
that, it, it's still important that the communication is well
defined and basically the MVC, an MVC can only serve as
part of the view of another MVC, okay?
Do you see how this is arranged up here?
If you look at any of the purple controllers up there
you notice that any arrow they have to another MVC
goes out that view side, okay? So we always wanna
think of these MVCs as part of the view of another MVC.
And there are some MVCs like tab bar controller that's
an MVC that's provider iOS. Where you might have three or
four other MVCs as part of its view. And those are the things
when you press on the tabs at the bottom,
you see a different MVC, right.
So, that's what we built app at four MVCs let say,
one of them is the top level tab bar controller.
And then we have let say, three other MVCs.
And those three MVCs might do completely independent things
and as we build this we really want each MVC to be completely
self-contained, just like when we design
objects we want them to be completely self-contained. We
don't want them reaching into the internal implementations
of other objects, right? So and sometimes we're building
an object orient system here out of MVCs as well.
Okay, now you'll see how all this works in week three.
We'll start doing multiple MVCs and it'll all make sense.
Okay, one thing we don't wanna do of course is
build something when MVCs are [LAUGH] not working together.
If these arrows start going in every which
way direction then there's gonna be now way to understand
how your app works once it gets to a certain complexity.
It's just gonna be beyond your comprehension.
Okay, so, we don't want this. This is bad. All right,
so the demo I'm gonna dive right into here.
Again, this is a slide you can look at later,
important things that I'm gonna cover in this demo.
Cuz I'm not coming back to the slide so let me summarize,
what's coming up? On Friday, we do have this debugging,
session. It's at 1:30 in this room,
okay? I highly recommend you go to that,
especially if you've never done debugging in Xcode,
cuz you'll kinda be wondering how the heck it all works
otherwise. Next Monday we'll be talking about more Swift,
that's when your first reading assignment is due and
your second reading assignment will go out.
And then next Wednesday we're gonna start about talking
about custom drawing in iOS.
What if we wanna not just use a button and a, and a label,
but we wanna draw our own stuff? And that's when
programming assignment one will be due before lecture and
programming assignment two will go out after lecture.
Okay? Any questions,
you all ready to jump in this demo? All righty, here we go,
I'm just gonna pick up right where we left off with
I'm gonna go to developer here as our calculator, all right.
I'm gonna, when I wanna relaunch it,
I could just launch and get the splash screen here. And
then, click on this to open it and here it is and
the, before, if you remember where we were, we only had
a pi button and then the keypad. That was great and now
we wanna add more buttons, and that's what we're going to do.
We're gonna add more operations and
more sophisticated operations, like multiplying and
things like that.
Before I do that I wanna talk a little bit about a feature
in Swift that can really make your code read a lot better.
You notice here that we have this type conversion.
String and pi, right? Where when the pi button gets
pressed, we have to convert pi to a double which is a string.
Well if I think ahead about all the operations I'm gonna
wanna add to my calculator there all doubles,
everything is doubles, not strings, okay?
So am I really gonna have for all these operations,
all kinds of converting back and forth between strings and
doubles as I try to put the results into the display or
get the number out of the display?
That is gonna end up being really tedious,
okay, and it's gonna make my code kind of a mess,
lots of type conversions back and forth.
Wouldn't it be cool if I had a var called Display Value which
was a double, and this bar automatically tracked what was
in that display? In other words if I ever got
the value of this, it would be the value of the display
as a double. And if I ever set the value of this, it would
set the display. Wouldn't that be cool? Right, that would
make all the rest of my code a lot easier because I would
be all in double land and
Is not having to be doing this string version?
And the answer is we can absolutely do that kind of
var, a var that tracks something else, okay? This
var, our user is in the middle of typing, is just stored.
That true false value is stored somewhere with this
object. This one, instead of being stored, it's going to be
calculated, okay? And we call this a computed property. And
we do it by just putting curly braces after it, okay.
And inside this curly braces,
we're gonna put some code to calculate
the value of this property. Both when we get it, okay. And
when we set it. So, we have this get and
set, keywords here. And inside here, we just put code to get
the value of display value. And set is the code that gets
executed when someone tries to set the value of this var.
Okay? Super simple. So, what's the implementation of this?
Really easy when someone tries to get the display value
I'm just going to return the display's text. Okay?
Unwrapped, but of course, this is a string. Right? Okay? And
this is supposed to be returning a double. So,
I need to convert this string to a double. So, I'm gonna say
double. That, okay, now this is still not gonna work,
okay? Why is that? Let's look at our error. It says
the value of optional double is not unwrapped. Look it's
trying to unwrap this. Okay, that's really weird. See it's
pointing an exclamation point at the end of this double.
I didn't have to do that down here.
When I converted from this Double to a string, I didn't
unwrap it. Why is this? I'm trying to create a double
here using this string.
Why do you think this is returning an optional double
instead of a double? >> Because it might not be
convertible. >> Correct.
It might not be convertible. Right? If I press hello in
there as the string. Double of hello, eh, I don't know. Okay,
now again it could return zero or something else but
really it wants to say, I don't know. I can't do it.
And the best way to do that is with an optional. So, some
constructors. Okay? Some of these initializers for various
classes can return optional versions of the thing.
In the case where they can't necessarily create one for
you. Okay? So that's really kind of awesome.
So let's go ahead and unwrap that. Okay now this would,
again, this would crash if we ever put hello in here, it's
gonna crash. So, we're kinda designing our codes assuming
this is always going to have a number. How about setting it?
Okay? Here we want to set the display's text
equal to what the person is setting the display value to.
Okay? When someone sets the display values they're
going to say in their code display value equals five,
right? So, how do I get the five in here, in this set?
And the answer is there's a special key word called new
value. Okay? This new value is going to be the double that
somebody set. Okay? Display value equals something. Now,
I want to put this in display text, but, of course,
this what type is this right here? The double, right?
Because they said display value equals something and
it's a double. And this has got to be a string. So
I've got to convert this to a string. Just like I did below.
That, okay, can always convert a double to a string so
there's no optional, stuff going on.
And that's it, okay. I've now invented a new property,
that is calculated. And every time I ask for
its value I'm gonna get what's in the display's double.
And every time I set it, it's gonna set the display. Pretty
cool? And it makes our code like down here a lot better.
Instead of having this go down here, we're just gonna say
displayValue = Pi, okay? We don't need to do this
type conversion in reference displayed text, okay?
Everyone understand that? And, this is going to make it a lot
easier to add new things. Let's add another property, or
a another, operation here. I'm gonna add square root, okay?
So let's go here into square root.
The square root symbol I'm gonna get from the edit.
If you go into edit menu of most Mac apps you'll see this
emoji and symbols thing at the bottom,
brings up this, window or you can have a lot of emoji, but
you can also have math symbols and, down here here's square
root. Just the square root symbol, okay.
So I'm gonna put the square root symbol on this button.
Square root. Okay? And, then it's already wired
up if I hold over here you can see it's hooked up because I
copy and pasted the pi button. We can see it's okay here
because I didn't copy and paste the digit button.
If I right-click on it we can see that it's only gonna send
perform operation, right, so that's all good. And
all I need to do here is say if the mathematical
symbol equals, that square root thing then the display
value equals the square root of the display value.
Okay? So, you can see that this code is really nice.
If I didn't have that I would have had to get the display
text, convert it to a double, do the square root, convert it
back to a string, and put it back into display text.
See how that would have been a mess? Okay?
And, this is only just the very first one I added.
If we add a whole bunch more it's gonna be even more and
more leverage to have this thing. But
mostly I'm showing you this because I want you to see what
computed properties look like.
We use them all the time in Swift, and we're going to use
them yet again in this demo, and you should get comfortable
with the fact that not all your properties are stored
some of them might be computed like this. All right.
I want to add more operations now, but I have to be careful
here because this code really does not belong in my
controller, okay? Because this is the code of what my app is.
It's a calculator and I'm doing calculations here. So,
this needs to move into a model class.
Okay? So now, it's time to do MBC here and
move this stuff into a model class. So,
what's our model class gonna look like? Let's create it and
kinda design in an API for it and then we'll get back and
use it here. Okay? So, to create it, okay, in
fact to create any new file in x code, you're gonna go file,
new File. Okay? File, new file. And when you go here,
it's going to say, what kind of file do you want to create?
And of course, we want to create an iOS Source file.
Okay? Not watch OS or something.
And here we're going to create a Swift file. If we were
creating a Cocoa Touch Class, like a new view controller,
we would go here. But if we're going to create just a model
class, we go here. So I'm going to double-click.
It's going to say where do you want to put this?
I'm going to put it in the same group, calculator,
that all my other swift files are in.
You see, ViewController.swift there.
I'm going to call it calculator brain because it's
going to be the brain of our calculator.
It's going to be the model for our calculator.
Then click create. Here it is right here. You can see that
the very first thing, it imports Foundation,
not UI Kit. Never import UI Kit in a model file because
the model is UI independent. So it would never do that.
If you find yourself importing UI Kit, you're doing it wrong.
Okay? So, Foundation is what we want. Foundation is that
core services layer, kind of the basic stuff,
non-UI. Base stuff. By the way, let
me show you how you can put different things on each side.
So I've got calculator running over here,
what if I wanna have my controller still be over here?
And you do that with these things at the top. Okay?
The top line here is actually changeable. You can pick
other things to show. So, for example, I can go show my
controller here. Okay, now I can have them both on
screen at the same. Which is kind of convenient, especially
if I have a class that I'm using in another class. I can
see its API here, and use it over here. All right, so
I'm going to create a new class called Calculator Brain,
and we know how to do that. Right? We know how to do that.
Okay, class Calculator Brain. What's its super class?
No superclass, right? CalculatorBrain, this model,
it doesn't inherit from anything.
It doesn't need to inherit from anything, okay?
So it's just a base class. All right, now let's
talk about what its API is. Everyone knows the phrase
API, I hope. That means the interface through which we're
going to be programming, using this, CalculatorBrain.
It's all the methods and properties in it.
So, I'm gonna do a little function called setOperand,
okay, which just takes a Double, okay? That's gonna be
part of it. So if I'm using my CalculatorBrain, I'm gonna set
an operand. Then I'm gonna have another function in here,
called performOperation, which is gonna operate on
that operand. And the argument there is gonna be a String,
which is the mathematical symbol, okay? And then lastly,
I'm gonna have a var, which is the result of the operation,
which is gonna be a Double.
And I'm gonna do something interesting here, instead of
just having this be a public var that could be set and got.
Because the setting of this doesn't really make sense for
anyone using my CalculatorBrain to set this.
I set it internally, okay, because of performOperation.
So I'm actually gonna make this computed and
only implement the get side of it, okay?
I'm not gonna implement this set, so
now this becomes a read-only property. Do you, do you all
remember another a read-only property we used last time?
Current title in button, okay? So current title in button is
a computed read-only property in button. That title, that
current title, is probably gotten from a UI label or
something that the button is using to draw its title, okay?
It comes from somewhere else, that's why it's computed,
okay? So I'm gonna do the same thing here. So this is how you
can make a property be read-only to the callers,
okay? Yeah. >> So can we use the get for
comparison, not just for assignments,
like with equal equal sign? >> Okay, so
the question is, is the get used for comparison?
Well, comparison is actually quite interesting in Swift.
The equals equals operator is like a function, and
it takes those two sides as arguments.
And those two sides have to implement certain methods
if they wanna be comparable, okay?
Now, we're not, we're not far enough along in terms of our
understanding of Swift to see exactly how that works. But
the answer to your question succinctly is no,
the get really doesn't have anything to do with equality.
Equality is just a function that is different,
okay? All right, so, I'm gonna return 0 for
right now, okay? Just to get rid of my little, error there.
But eventually, we're gonna have to implement this,
internally and make it work.
Now, I wanna talk a little bit about APIs right here, okay?
So far, every method and property we've done
in this whole class has been essentially public. Meaning,
any class can call any of the methods in any of the classes
we created. For example, all of our controller vars,
okay, and functions could all be called by some other class.
Now, that's bad, okay, that's bad.
For example, displayValue,
we wouldn't want some other class setting the displayValue
in the calculators through this controller. Because we
managed that displayValue by what our model calculates,
right? So this is internal implementation.
In fact, all of this is internal implementation or
control. We do not want other classes to be able to call it,
unlike these three, which are external, okay? They're, they,
we want people calling these in CalculatorBrain.
That's how our CalculatorBrain works.
If people couldn't call this,
they couldn't even use the CalculatorBrain.
So how do we specify that difference between something
that should be called by other people or not? We do that with
the private keyword. So I'm gonna add private, okay,
this private keyword right here,
to all of my functions and methods over here.
I don't, this is not really part of Swift again,
this is kind of an Xcode thing, so
I put it after that. But otherwise,
you put it there, and we're gonna put it for all of these.
We're gonna make all of these be private.
And as you program, okay, you're gonna see that one of
the evaluation criteria on your homework is that you
properly make things private when they should be private.
And I generally would err on the side of making it private.
It's a lot easier to make something private and
decide to make it public, than to use something public,
go back later and
have a whole bunch of coders start using it, and
then decide, no, no, I want that to be private.
Then you break all those other people. So err on the side of
private first, and then making things public, okay?
Now, it's actually possible to look at something and see what
its public interface is by going up here to the top and
picking Generated Interface. This will show you the public
API of the class in the main window on the left there.
So we're gonna look at the public API of CalculatorBrain.
You can see that it has that setOperand, performOperation,
and result. Notice this looks just like current title,
right, where it's saying this is a read-only thing.
We don't see any implementation here,
this is purely the API, okay? So
no implementation here. Also notice this says internal,
you would think this might say public, okay? But there's
actually a slight difference between internal and public.
Internal means it's public within your module.
Public would mean it's public to everyone in other modules,
so consider UIkit.
UIkit has hundreds of public methods that we can call.
But it also has hundreds, if not thousands of internal
methods that only other UIkit classes can call between
themselves. We don't even know what they are, okay? So, but
for your purposes,
since you're always gonna be working in the module,
which is your app, internal means public,
basically. Let's go look at our controller now, and
let's look at its public API, okay? So here I selected it,
look over here. And it says, there's only one public thing,
userIsInTheMiddleOfTyping. I didn't mean that to be public.
I wanted that to be private, too, I just forgot to put
the private on there. So if I go back over here and
say private, okay, then you'll see it goes away. So now we
have no public API here. Now, it's still completely usable,
because in Interface Builder, we can wire up to this
controller and make it appear in a tab bar controller,
all those things. We can do all that without having any of
the internal methods here be public.
Okay, so we're gonna go back to my, oops, sorry.
I'll go back to my brain over here, it's got my controller
over here. All right, so we've got brain and
controller. So let's think about how we're can use this
model over here, okay? We haven't implemented this yet,
but we've defined its public API. So
how can we use that over here? Well, we really wanna replace
all of this business with using our model, right? Cuz
this is where we were doing model things, calculations.
So we don't want that, okay? We wanna get rid of that, and
we want to start using our model here. Well, to have
a model in our controller, we need to be able to talk to it,
that big green arrow, okay?
So we need a private var, which I'll call brain,
which is gonna be a CalculatorBrain, okay?
And this is the var that we're, it's gonna create our,
we're gonna create our CalculatorBrain.
And we're gonna talk to it to do all the calculations, okay?
So this is just that big green arrow I showed you on that,
those previous slides, where the controller talks through
this to get to the model. Now, how about creating this thing?
Where do we create this?
Well, you can see that we have an error up here,
no initializers again.
That's because this var,
like any other, has to be initialized. And
I'm gonna create a CalculatorBrain here. And
to do that, I have to call one of its initializers. And every
time you create a new class, you get one free initializer,
which is an initializer that takes no arguments, okay,
kinda the basic initializer. So
I'm using that CalculatorBrain initializer, it came for free.
I don't have anything that I need to initialize anyway.
So, that's perfectly fine, okay, so I've created it. Now,
notice that this right here, do we need this?
No, because Swift can infer that
brain is a CalculatorBrain from that = right there, okay?
So we do not wanna put colon CalculatorBrain. All right, so
now that we have this brain, kay, and it's created here, we
can use it to public API right here, to make things work.
Well, one thing we know is that when the mathematical
symbol comes through here, we wanna ask the brain to perform
that operation. Okay, so we're gonna pass that mathematical
symbol as the operation, we know that. We also
probably know that after it's done performing the operation,
we probably wanna put in the display the result,
the brain's result, this thing right here. Right? And
also at the beginning of the perform operation,
if we're in the middle of typing a number,
we better set that number at the operand for
the calculator to work on it. If we go 235 square root,
we got to put that 235 in as the operand for the brain.
So we better say if the user is in the middle of typing
a number then brain.set operand to be whatever's
in the display. You can see even here, having this display
value thing makes our code read really beautifully.
Okay. We can probably put this inside this if, because
no need to set it false if it's already true. Okay, so
that's it! That's all we need to do to hook our model up
to our controller. Okay. And we've removed everything
in our controller that has to do with actually calculating.
We've basically given it all off to the model to do.
So now we have to implement this, okay?
We've gotta implement this brain over here.
I'm gonna make that be the main window here.
And how are we gonna do that?
Well, I'm gonna have a data structure here for my brain
which makes pretty much sense, which is gonna be private,
which is gonna be called accumulator. It's gonna be
a double and it's going to accumulate the results, okay.
As operations are performed, it's accumulating the result.
Okay, anyone who knows how calculator's built,
it has internal calc, accumulator. So,
this is our accumulator. Notice that I,
as soon as I put this in here I get this error.
Again, calculator brain has no initializers, that's because
I don't initialize this. So, I'm gonna say this equals 0.0.
Once I do that I do not need this because 0.0,
Swift always infers that. Or any something dot something,
it always infers it to be a double, okay. So that makes
this be a double. You see? If I made this no dots just zero,
then it's gonna infer this as an int, okay.
So, good thing to know there. So now that I have my
accumulator, the result is always just the current state
of my accumulator. So that's easy, open my result. And
same thing when someone says the operand, that kinda
resets my accumulator to be whatever that operand is.
Okay. So those are all easy to implement. So that just leaves
this guy, perform operation. That's the heart of my model.
That's the thing that's really gonna do some calculation.
Now I could right here go back to what I was doing in my
controller which is to have some, if there is an essence
is here, well actually I'm gonna use switch. So
switch is the same as another language but
much more powerful in Swift and
also much more important in Swift as you will see. Okay.
Switch It's very important thing is Swift.
So, I can switch on this symbol that's legal.
The switch on a string. Okay. And I just put the cases that
I wanna try. So, we have for example pi. And if pi happens,
I wanna set my accumulator equal to pi,
okay. If it was for example, square root,
let's go do that. My square root symbol back, here it is.
So if we get square root, then I'm just gonna say that my
accumulator equals the square root of the accumulator. Okay?
So, this is basically getting us back to exactly where we
were before but now we have a model.
Notice we have an error here.
That's because one thing about switch, you must consider
every possible value of this thing you're switching on.
Now, this is a string, so it has infinite possibilities,
okay? Now, we could spend the next few years saying case A,
no, we don't wanna do that. Instead, we're gonna put
default break, so default means, if you can't match any
of these other ones than just break out of this, okay? Now
notice my indentations gotten a little wonky here, I'm gonna
teach you something fun. If you select a curly braced
region including your whole file, and hit Ctrl+I.
It'll reformat it, okay? Re-lay it out. And
I strongly recommend that when you turn your homework in,
you select all and do Ctrl+I. Okay?
That way you'll be using the default indentation, even if
you prefer something else, use the default one because people
reading your code are gonna be able to understand it better.
Okay. And believe me, you'll adjust to whatever indentaki,
indentation style this thing enforces on you, okay?
If you start getting, if you are a computer scientist and
you start getting religious about things like indentation,
you're heading down a pretty rocky road,
okay. Because when you wanna work in the real world you're
gonna have companies that say this is the way we do it,
And if you sit there whining I don't like to do it that way,
get used to it.
well, you'll probably get fired. Okay? So don't do that.
So here we're just gonna let the Xcode do our indentation
for us. So this is all we need to do right here, okay.
This is full implementation. We can go back and
run our app and it's exactly the way it was before,
but now we're using a model. Okay. So
here we go, let's try 4-5, that's working square root.
That looks like it's working. We'll just be sure by picking
a number we know the square root of, pi seems to work,
square root. Okay, so we're back to where we were,
that's nice. Now, they'll, thing about this now is I'm
about to go add a whole bunch more operations here. And
if for every single one I have to do the math, do the math,
do the math each one,
this is gonna be a lot of duplicated code
in here. Because every time I have a unary operation like
square root, it's exactly the same. If I have cosign or
square root or anything it's exactly the same.
The only difference is these four characters. Square root,
or cosign or whatever. Same thing for these constants,
only this part will be different on every line,
even for binary, like multiplier, whatever.
It's probably only the function that does
the calculation that's gonna be different. So I'm gonna
factor this stuff out, so that all of these things,
like pi and square root and multiply are in a table, okay?
And I'm just going to have this only be doing the generic
calculations, generic constants,
generic numeral operations, generic binary operations.
And it's gonna look in the table to find out what to do.
Doesn't that seem like a much better design,
more extensible, less code, etc? So
that's what were gonna do. So let's create that table, okay?
And were gonna call that table operations. And
it's going to be the class, it's actually not a class.
Dictionary, okay, so dictionary is a Swift thing.
It is a generic type. You're probably used to that in Java.
So you specify right here when you're declaring this
what the keys and values are, what type? And so I want one,
I'm gonna start out just doing the constants.
Let's have this table only do constants, okay,
like pi. Okay? So I'm gonna have my keys be string.
That'll be the name of the constant, like the pie
character or whatever. And the value's gonna be a double.
So that'll be like M under bar pie or whatever. Okay? So,
I've declared it here. Now I'm actually gonna initialize
it because remember I have to initialize all my bars.
You can initialize a dictionary right on the fly
just by using the open square bracket. Notation and
you just put like for
example pi cuz key colon m under bar pi the value.
Okay so I'm basically filling up the dictionary here.
Let's do another constant how about E that's M under bar e
everyone know what e is 2.71 or whatever it is. Okay?
So, we could add a whole bunch more of these things into our
table. Again, we're only doing constants right now,
we're not doing square root and those kinds of things.
So, that changes my code over here.
Instead of having all that stuff right there,
I'm just going to let Constant equal Operations sub symbol.
So,this is how you look something up in a dictionary.
Okay? Here's the name of the dictionary, right here, and
you look it up with square brackets and the thing to look
up. Okay? And now, I could just say my accumulator equals
that constant. Okay? But, this is not gonna work. Why? Let's
find out. Error, it says, value of optional double?
not unwrapped. Uh-oh, it's optionals again.
Okay, what's happening here? It wants to unwrap constant.
In other words, it's saying this is an optional double.
Why would the thing,
this dictionary does not contain optional doubles,
it contains doubles? So, why would looking this symbol up
in that dictionary return an optional double?
Anybody have an idea? Someone besides you, cuz you got it
right before. [INAUDIBLE]. >> Yeah?
>> I think their scenarios
might not have that key? >> Correct!
Exactly the same thing as before, okay? This dictionary
might not contain that key that we're looking up. So,
it's gonna return nil to say I couldn't find it.
So, we simply need to unwrap it. Now, this is dangerous,
because maybe somebody's using my API, and
they perform an operation that I don't understand,
now I'm gonna crash. That's not very friendly. So,
here I'm gonna use if, the if let and set my accumulator,
and I'm just gonna ignore any operation that I don't
understand. I'm not going to affect my accumulator.
Just leave it. Okay? All right, so let's run, make sure
this works. All right. So, the square root's not gonna work,
cuz we don't have square roots in our table here,
we only have constants.
But, we have these are still working, and
pi is still working. Okay. So that's good. So
we didn't break pi, at least. And if we had an e key,
then the e key would work as well. All right. Now,
we want to extend this to do square root. Okay.
How the heck we going to do that?
I mean, really, all we want to do is just say square roots,
whoops, I shouldn't have done that. Square roots,
lets get our friendly neighborhood symbol for
square root here. Okay? Square root we really want to
put square root [LAUGH] right here.
Okay? The square root function.
That's really what I want, what I want to do.
And, like, If I had cosine I'd really want to put the cosine
function here. Okay? Now this is obviously not a double.
[LAUGH] That's not going to work.
So, this can't be a double. This has to be something else.
Okay? It has to be something that would work for a double,
and would also work for a function. Okay? How are we
gonna do that? Well, we're gonna implement a new type,
okay, and it's similar to class. It's called enum. Okay?
I'm gonna call this enum operation, and
inside this enum, I'm gonna have all the different kinds
of operations I know how to do. Now, you're probably used
to enum in other languages. What is enum in
In most languages it is a discrete set of values, right?
An enum has to have discrete values. Same thing in Swift.
It has a discrete value. So, for example, it might
be a constant, or maybe it's a unary operation. Or it might
be a binary operation, or many it's equals, the equal sign,
which is kind of a special operation, okay.
So, enums are the same. What's different about enums in Swift
is that they're like classes in that they can have methods.
Okay? So, I can go down here and say funk,
you know, something, take some arguments, return something.
I can do that down here. Okay? Enums are allowed to have
methods. Now, they can't have any vars. Okay? They can have
computed vars, but they can't have any storage vars because
this is essentially their storage. Okay? The enum.
The other thing about them is they can not have inheritance,
so you can't have an enum that inherits
from another enum, which probably would be weird
anyway. So that is not much of a restriction. Okay?
The other thing about enums is they're pass by
value and I am just going to post while I talk about that
until I show you struct, which is another pass by value
data structure, in a moment. Okay? So, here is operation,
that's great. So, now I can change pi, that's an operation
dot constant. Okay? Comment that out for a second.
This is also an operation of constant. This is
an operation dot unary operation. Okay?
And this is also an operation dot uniary operation. Okay,
cool. So, we can now change this double to the type
operation. Okay? And errors go away. These are all operations
and it all works. Now, small problem here is that,
we've lost track of the actual constants and, functions, and
we've commented them out. They're not even involved
here. So, this obviously had not solved the problem.
It's a step on the way to solving the problem, but
it has not solved the problem. All right, the other thing is,
obviously down here, looking up constants like this and
making the cumulative, this doesn't work,
this only works for constants, so we're not gonna do that.
So, how do we look things up now for operations?
Well, we're gonna do a similar thing here, okay,
we're gonna say. Let, we can if,
if let operation equal operations sub-symbol.
Okay? But, now this operation is going to be one of these.
Okay? It's going to be one of these enums, right?
If I click on it,
you see, it's a calculator brain dot operation.
Yeah, notice also I defined this enum inside this class so
its full name is calculator brain dot operation.
You can nest these things.
You can even put classes inside classes if you want and
they'll just, it's just a namespace thing right?
The names will be whatever dot whatever dot whatever. Okay?
So, I've got the operation there.
Now, I'm going to switch on this operation, and
I know that the cases can be constant. Okay?
And, I'll just break on all these for now. So,
it could be a constant. It could be a unary operation.
It could be a binary operation. Or
it could be equals. Okay?
And remembering switch I have to define every single option,
but I don't need default here because there are only
four possible things that an operation could be. So,
I've got a case for all of them in my switch.
Question? >> Two things.
Why is operation, are we not referring to the same
operation as the enum operation method?
Because it's not capitalized. >> Yeah,
this operation? >> Yeah.
>> Not capitalized makes it
a local variable, we're making it a local variable here yeah.
And, actually, that's a really good opportunity for
me to talk about how you capitalize okay?
All types you want to be capitalized, like calculator,
brain, dictionary, operation, string, double.
Do you notice they all are capitalized?
Operate, everything, okay, is capitalized.
All local variables and vars are lowercase first letter and
then capital letter for all the subsequent words in there.
So it's called Camel Case, you guys know of,
have heard of that before? So, that's how you want to do
all your naming. If you don't do that,
you're going to get in trouble with me. Okay? So, I know some
people like to use lower case for class names, forget it.
You can't do it in Swift. Just don't do it, okay.
It'll be allowed, but you'll get in trouble, so
don't do it. Okay? Well,
you had a second question? >> Yeah.
>> Why are we using the dot in
constants? Are we referring to operation dot,
okay that's my confusion. >> So, why did I say dot
constant here instead of just saying constant?
And the answer is yeah, really we're doing operation dot
constant, but Swift can infer that it must be operation
because it knows this is an operation. Okay?
Is that because it's within the operation's dictionary?
>> Its part of the enum for
operation, you see, operation is not really,
we're not inside the dictionary here. We pulled it
already out of the dictionary. >> So, how does it know,
is it intelligent enough to distinguish even though you
computed a lower pace operation,
that it's referring to the email with
the uppercase operation? >> Okay.
It knows that this lowercase operation is a capital
operation because I pulled it out of this dictionary, and
it knows that that dictionary has operations as its value.
So, when I pulled out its value, it knew it.
Okay? There you go. All right. So this is all going good
except for, again, we don't have the pi and the e and
the square root and the cosine in here.
So how are we gonna get those things in there? And
the answer is, you actually already know it.
You've heard it before. Associated values. Okay?
Remember optional has that associated value. All
enums have associated value. In fact, optional is an enum.
Okay? This is what optional looks like if you were to look
at it. Enum Optional, case None, that's the nil case,
case Some with associated value, T. And then,
the optional is generic type. Just like dictionary,
it has this generic type. So this T could be any type, and
that's how optional works. Okay?
So, we can do the same thing down here, we could associate,
for example, a double with constants. Okay?
Because constants need a double, M under broad pi,
we need that thing. Okay? And so, we're doing the same thing
that optional does. Associating a value
with our constants. So, we have this constant double,
then here, when we declare the constant we have to provide
the associated value which is m under bar pi.
Okay? Now we can get rid of our comment there.
Same thing here, we can take this M under bar E, and
associate it with this constant, oops. Okay,
see how we're doing that association? Now,
how do we get this associated value out when we're
looking at a constant down here? Right, here we switched
on the operation, we know that this is a Constant, right?
We looked it up in the operations Dictionary.
And we found that it's a constant,
let's say, like this one. How do we get it?
You do that by right here saying, let associated,
you know, constant, value, or whatever you want to call this
is, this is just a local variable, but you can call it
anything you want. Okay? That will make this local variable
glom onto this associated value. okay? And
so, now we can say accumulator equals the associated constant
value. Okay? So, that's why I said switch is really
powerful it does this kind of pattern matching to get these
associated values out, so you do that with switch. Okay.
Now, associated constant value, it's kinda yucky.
I'm just gonna call it value. Okay. I only called it that
long thing just to show you it can be called anything and
that it is the associated value, but you can probably
call it value. Okay, you got that? All right. Let's run and
see if this works. It's only going to work for constants,
cuz that's the only one we're, we've done any associated
values for yet. But here we go, this is still working,
Pi works, okay, square root, not implemented yet.
All right? So, let's do square root, okay? So square root,
what would be the associated
value of a unary operation? Don't be shy. What?
A function yes. It's a function. Okay? So,
how do we make a function be associated value here? Well,
the lucky thing is that in Swift,
functions are types just like any other type. Okay? There's
no differences in Swift's mind between a function type and
a double. Exactly the same. It can be used in all the same
circumstances, arguments to functions, associated values,
local variables, anything can be of type, a function. And
not only that, it's not a generic function,
it's a function with certain arguments and return values.
And how do you declare such a type?
How do you say that that's a type here? You just type it.
So, this is a function that takes a double and
returns a double. Okay?
That's the associated value of unary operation.
It's a function. So, here when we want to associate a value,
we have to put In here, just like we put a double here for
this one. All right? Here we have to put a function that
takes a double and returns a double like, I don't know,
square root. Okay? Or maybe,
cosine. Okay? Everybody got that?
Now, same thing down here.
We got to grab that associated value.
So, here I'm going to say let and again I can say associated
function, but I'm just going to call this function. Okay?
Now I have this is a local variable of type function that
takes a double and returns a double. That's its type. Okay?
That's the type of this function. In fact, watch,
I'll click on it. Look at it's type. It's a function that
takes a double and returns a double.
How do I use a variable like that? Accumulator.
Oops, not accessor. Accumulator. Okay?
Now, again this is just a local variable.
I could call this foo and then I would put foo here. Okay?
Its just a local variable. That's all it is. And
it happens to be, its type is a function that takes
a double, returns a double. All right. Everybody cool with
that? All right, let's run again, see if this is working.
All right so 81 square root, excellent. Okay?
Executing this associated value, it looked up that
square root, found that it was an Unary Operation with this
associated value, went down here and performed operation.
Found it here, grabbed that associated value,
and then I used it to update my accumulator.
Question? >> So, so
you just specified the types and I'm surprised that you're
in, operation does not require [INAUDIBLE] because your
dictionary could potentially just pull anything,
any kind of- >> [COUGH]
>> We know that dictionary can
only have an operation in it, right. You can only have one
of these and this only has four possible cases.
Even though any given case might have any associated
value, it's still the actual case of that operation.
There's only these four.
So down here, when I switch on it,
I only have to cover those four cases. No more. Okay?
All right, what about binary operation?
Okay, well, binary operation, a little more complicated and
why is it more complicated? Because if you think about
the way a binary operation works like multiply, 3 times
5 equals. Okay, when I press times I don't have enough
information to update the accumulator yet. I need the 3
and then the equals, it's only when the equals is hit that I
have enough information to actually do it. So,
in binary operation here,
I'm still going to grab that binary function out of there.
I can't actually perform that function like I can here. So
Okay? But,
I'm going to have to salt away that function and the operands
so far and wait til equals happens then I can do it.
Okay? But, I still need, just like unary operation,
I need to have an associated value with a binary operation.
What do you think that looks like? Another function,
right, that takes two doubles and returns a double. Okay?
Like multiply.
So, that's just a different kind of function. Okay?
And so now I can go up here and add multiply, so
let's go ahead and get the, a, the mathematical symbol for
multiply out of my emoji and symbols.
Here it is right here. Multiply, okay? And
that is an operation tha's BinaryOperation.
And look it wants a function that takes two Doubles and
returns a Double. So I'm gonna put a function there called
multiply, which doesn't exist in Swift, so I'm gonna have to
go write that. By the way, we have another thing here
which is equals, which is Operation.Equals, okay?
So i's a kind of a special operation there,
okay? So, it's complaining here because multiply doesn't
exist, all right? So, I'm going to write multiply.
Here it is, I'm gonna make it a global function even,
just like square root. Func multiply,
okay, takes one argument that is a Double.
Takes another argument tha's a Double.
Returns a Double, and it just returns op1* op2,
okay? So I've created this new function, multiply,
here it is, and I can now use it right here. Sound good,
you understand that? Question? >> Why is that
outside. >> Yeah, so
why did I put this outside?
Because I wanted it be a function, a global function.
Not a method in this class, okay? So I just wanted its
scope to be wider. >> So it's more of a style?
>> Yeah, it's kind of a style.
A little more of a style thing. All right,
so so now we have this binary operation here,
we have to salt away the binary function like times,
and the accumulator so far, the 5, in 5 times 3 equals,
the 5 and the times we have to wait, salt them away. So
I'm gonna salt them away in a data structure, and I'm,
gives me a chance to teach you another data structure.
You know class,
you know enum, here's another one called struct. Okay?
Now you know struct from other languages, of course.
I'm gonna call this struct PendingBinaryOperationInfo,
okay? And it's just gonna contain these two things I
want. One of them is the binary function that I'm going
to do. What's the type of this? Something that
takes two doubles and returns a double, that's it's type.
See, I'm just declaring, that is a type,
it's a type like any other type, like int, okay?
We also need to keep track of the firstOperand for
this binary function, which is gonna be the accumulator so
far. And that's gonna be of type Double, okay?
Now, what is a struct? Okay, we know class, we know enum,
what's struct? Okay, struct is very much like class.
Almost identical, okay? It can have vars, stored vars, and
computed vars, no inheritance, okay? But the big difference
between struct and class, is that structs, like enums,
are passed by value, whereas classes are passed by
reference, okay? What does that mean? All right, so
passing something by reference means that that thing lives in
the heap, okay, lives in memory somewhere, and
when you pass it around to methods or
something like that, you're really passing a pointer
to it. And so, when you give it to someone else, they have
the same one you have, because you both just have a pointer
to the same thing that lives in the heap.
That's passing by reference, okay? Hopefully you know that
much of computer science, that that's pass by reference,
okay, and that's what it means in this scenario. So
if I had a class like calculator brain,
and I pass that brain around, I'm talking about the same
calculator brain all the time. Now I can instantiate another
one in the heap and have a different one,
but, but I'm pointing, when I create one I'm pointing to it,
and I'm passing the pointer to it around. Path by
value means that when you pass it, it copies it, okay?
Some would think of it as it's being passed on this stack,
the call stack of the function.
But that's not necessarily how Swift implements it.
But the semantics of it, are that it is copied.
So if you have a, let's say an array, which is a struct,
okay? A double is a struct, it turns out. An int is a struct.
A string is a struct. These are all structs, okay?
And so if I passed an array to some other method, and
then I added something to that array,
it would not be added back in the caller's array. The caller
would have that array without that thing added, okay,
cuz it would get a copy of it. Now you would think, whoa,
this is gonna be really low performance,
because what if I had an array of 10,000 items and
I passed it, it's gonna copy 10,000 things. My God,
my code is just gonna grind to a halt. No, Swift is really
smart about when you pass a bi-valued struct,
it doesn't actually make a copy of it until you try and
touch it, okay? If you try and mutate it, then it'll make
a copy as necessary, maybe not even a full copy, but
it'll mutate it. So if you're passing something and
you don't touch it, then you are gonna be sharing it, okay?
But, all of that is behind the scenes performance
enhancement, you don't know anything about it.
From your point of view it copies it. Structs always get
copied, okay? Understand the difference there?
A very important difference between structs and classes.
And enums are like structs. All right, so
I've got this right here. Now, notice that I didn't set these
equal to anything, but I didn't get that warning,
no initializers. Now usually if I put a var in a class,
if I don't put initial, then it says,
no initializers, right? So, why is it not saying here?
That's because for structs, unlike classes, classes we got
a free initializer. What were the arguments to it? Nothing,
right? Like calculator brain, parenthesis, no arguments. So,
that's the free initializer you get for classes.
For struct, the free initializer you get,
is an initializer that, whose arguments are all of its vars,
every one of its vars, okay? So let's go ahead and
call that, because here in BinaryOperation,
I need to create one. So I'm gonna create a private var
here. I'm gonna call it pending. It's gonna be of type
PendingBinaryOperationInfo, and it's gonna be an optional.
So here I am creating my first optional.
It's an optional struct, okay? Why am I making this optional?
Because this PendingBinaryOperationInfo is
only there if I have a pending binary operation.
If I haven't typed times or divide or something,
I don't have one of these, so I want this to be nil, okay?
I want this pending var that's holding this pending stuff,
to be nil at that point. And then when I have one,
I'll set it to something, okay? And
that's exactly what I'm gonna do here in BinaryOperation,
I'm gonna say, pending. That's this thing right here, okay,
= PendingBinaryOperationInfo.
And when I open parentheses, look. I got a constructor for
this PendingBinaryOperation that has these two things
as its two arguments. See, binaryFunction, and
firstOperand. So now I can apply these values, this is
function, and the firstOperand is the accumulator. Okay, so
now I've created one of these pending hoo-has right here,
and that's all I've done. I pressed time, but
I'm doing 5 times 3 equals, I press the times, all I did was
create one of these structs, and put the times and
the 5 in there. Now in Equals, right here,
I'm gonna say if pending != nil. So
if I have a pending operation, then I'm going to evaluate it.
So 5 times 3 equals works, but if I just say 5 equals, I
don't have any pending times, so I'm just gonna ignore this.
So I'm only gonna do this if I have a pending one, and
what am I gonna do? Well, I'm gonna set my accumulator =,
evaluating that pending function, which is pending!,
because it's an optional, .binaryFunction.
Called with the arguments of the pending!.firstOperand, and
my current accumulator. Okay,
and now pending is nil, because I no longer,
I just handled that pending thing, so
now I no longer have a pending operation anymore.
That make sense? Okay, so let's go take a look and
see if this works. All right, here we go. Let's try,
we don't have a times button. Let's go back to our UI and
add a times button [LAUGH]. In fact, we'll add of our binary
operations here. Okay, so, I'm gonna just copy and paste.
Put this here, paste another one. Here, we'll put all my
binary operations across the top here, paste, okay.
Go here, we'll go to our emoji and symbols things.
Here's times. We'll put divide right next to times.
We'll put plus right here. We'll put minus right here.
We can put some other buttons down here too, like maybe
we'll put, cosine, yeah, let's put cosine in there.
Let's cosine. Did I have another one? E I guess I had,
right? Put E in there too. E.
Let's also put a equal sign. Gotta have that.
We'll put it down here. Equal sign. And here, in this empty
space is just begging for me to put something there.
I'm gonna put period. Because in your homework,
you're gonna have to add the capability to be able to
enter floating point numbers. So you'll need this one.
So I'll put it there for you. Okay? All right, so here's our
nice UI. Looks really pretty. And let's run it.
All right, let's try 4 x 5 =, woo hoo, it works.
A miracle the first time. Okay,
let's try something a little complicated.
How about this? Well, let's do some other things.
The square root's still working, cosine, let's play pi
cosine. That worked, cool. All right, there's E, 2.71, nice.
Here's something that doesn't work though, watch this.
7 x 8 x 2 x 3, uh-oh,
this is not working. And this is not working because it's
requiring me to press equals to evaluate minor operations.
So I have to go 4 x 7= x3 = x8 =, really, what I want is
an automatic equals anytime I press a binary operation.
So I can go 4 x 5, and when I go times, it doesn't equals.
And then let me do it. So let's just do that real quick.
Let's go back to our brain. I'm just gonna take this
little thing that equals uses and
make it into a function, private func, we'll call it
executePendingBinaryOperation. It's just gonna,
I'm just pasting in the exact same thing.
I'm gonna call that here, executePendingBinaryOperation.
And I'm also gonna call it here. Okay, I personally like,
if I have any of my cases that go onto second line,
I like to put them all there. I just think it looks a little
nicer than having sum on the end. It doesn't really matter
to SWF, but I just think this looks a little nicer. Okay, so
we did that. So that'll fix that case.
All right, the last two things I wanna do,
okay let's take this 4 x 7 x 8 x 9 = okay.
Now the last thing, two things I wanna do is one,
I'm gonna show you how to make, divide and plus and
minus work without creating a whole ton of these little
extra functions like multiply cuz that's really gross. And
then second, I'm gonna make our UI stretchy, so
it works with landscape and portrait.
Okay, those are the two things I'm gonna do. All right,
so let's go ahead and make our other four operations here,
which is divide, plus and minus.
So I have to bring up our emoji again. Okay, so
this one will make the divide. We'll make this one be plus.
And we'll make this one be minus. Okay,
now here I could have a function called divide, and
another one called add, another one called subtract.
And then, I could go up here and make one of these for
divide, and one of these for add and one. Okay, but
if I start doing that, what a mess. Okay, I-I've hardly even
gained anything by having this nice table of operations,
if I also have to have a separate function for
everything I want to do. Well, SWF is gonna take care of us
on that front because it implements closures.
How many people know what closures are,
the computer science term closures? Okay,
hardly anybody, whoo, okay. So a closure is basically,
you can think of it as an inline function,
okay? But it's an inline function that captures
the state of it's environment. And we're gonna see why
that's important later in the quarter. But, for
now, you can just focus on the inlined function part of it.
So, I can actually take this multiply function, okay? I'm
just gonna select the function without the word multiply. And
I'm gonna cut, and I'm gonna paste it right in here. Okay?
Now, I can't quite do that. I have to do two things. One, I
have to take this curly brace and put it at the beginning so
that the arguments here is inside the curly brace,
because the whole closure has to have curly braces beginning
to end. And then, since I need to separate this
from the rest, I also put the word in right there. Okay, so
that's how you make a closure.
It's exactly the same as a function, except for
the curly brace starts before the arguments and you put in
after the arguments. Got that? Now, this doesn't look, so
that I don't need function multiply any more.
So this doesn't look that much better. It's still kind
of a mess. But we're gonna use type inference, yeah, to make
this look a lot better. Now, remember that SWF knows that
this binary operation takes a double, two doubles, and
returns a double. So this is all redundant, okay?
SWF can infer that that's a double.
It can infer that's a double.
And it can infer that it re-returns a double. So
this is wow, all of a sudden looking a lot better already,
okay? We can make this look all in one line. Okay,
that's looking pretty good. I mean, that alone is probably
really, really good. However, it gets better than that,
because closers also can have default arguments.
The default argument names are $0, $1, $2,
$3, however many arguments it has.
So you can put those as the names of the arguments
instead of having op one or op two whatever you
want to call it. Okay?
And if you do this, then you don't even need that. Okay?
If you use the $0 and $1, and since this is a double, and
SWF can infer that this return's a double, you don't
need return right here. >> [LAUGH]
>> Okay?
>> [LAUGH]
>> So we've cleaned up our
code quite a bit. And in fact, now divide is just this. And
add is just this. And subtract is just this.
Okay? So that's closures. Super powerful.
Used a lot in the iOS API. You're going to be
able to use it in your code to your heart's content.
It's very fun. Let's make sure it actually
works. All right, 7 x 8 = all right,
divided by 5 equals? Looks good. minus nine equals?
All right, so all of our things here are working. Okay?
We also could, so we can do that for
our UnaryOperations as well. What if we wanted, for
example, something like change sign. Let's go find something
to be change sign. How about this? That's not really
[LAUGH] a change sign symbol, but I'm gonna use it for that.
I could say change sign is Operation.constant, or
.UnaryOperation, sorry. UnaryOperation and
I need a function that takes a double returns a double,
how about -$0. Okay, that changes the sign of
the one argument. And Swift is smart enough to know that this
has one argument. And that it is returning that argument and
that unary operation is double double, so
it knows that this must be a double. It'll even infer that.
Okay. All right, so that's it for our calculator brain.
And if we look back at our calculator brain and
the code in it. All the code
here has nothing to do with UI.
It's purely about calculating and it's super-extensible.
If you want to add more operations here, all you need
to do is to provide the type of operation and
what's specific to that operation.
All the calculation is done is this very simple
function right here, the only complexity of which is this
pending binary operation thing we have to do. By the way,
this right here, this struct, should also be private. Okay,
this struct which is calculatorBrain.PendingBinary-
Info, that's its full name, that should be private as
well, because we're only using that internally.
Same thing with this operation.
It should be private.
Cuz we're not using it in our public API and same thing
with this operation, should be private. Okay, should make
everything private that you can make private, okay?
Make the things public that you intend to support forever
in your object. Okay? So let's do that UI thing I was telling
you about. Let's go back to our story board here, and
we want to make this thing so that when we,
let's see what it looks like now, actually. Okay,
so our UI, we know it doesn't look very good,
this is not lined up. This is kind of nice right here but
it's not lined up. But what happens if we
rotate to landscape? The way we do that is Hardware,
in the simulator, Hardware > Rotate Left and Right, okay?
I'm gonna use command keys to do it. Cmd+arrow. That really
looks bad because I can't even say equal six times four.
Okay. I can't even use this UI, it's so
bad, okay? So, we need to fix this UI so
that when it's in portrait, it's using the whole space.
Laying the buttons out to make it work and,
when it's in landscape, it's using the whole space and
the buttons are a different shape. Okay,
how are we gonna do that? Well I'm gonna do that by taking
each of these and putting them in a little stack. And then
I'm gonna take the five stacks and stack them together.
And then I'm gonna stack this whole thing with this, okay,
and create a stack of stacks. And then I'm going to bind
the left, top, right and bottom edges of that whole
thing to the outer edges of my UI. That way,
when the outer edges of my UI change, that thing will
change. And the stacks automatic gonna how to,
you know, reallocate the space. Okay, simple as that.
So that's what we're gonna do. So let's make stacks here.
The way we do that, we select the things we want to stack.
We go to editor Embed In > Stack View. Okay, and that's
gonna put it in a stack view here. Now, we can also go over
to the inspector and inspect some things about this stack
view like I want some spacing, 10 points between each one.
Also, you see how the cosine one is wider then the dot?
I don't want that, I want them all the same, so
I want it to distribute its space equally. Okay, so
now they're all equal. Okay,
same thing here. Okay, 10 points, and
fill equally. Now, by the way, there is no command key for
this, but you could go to Preferences over here,
Xcode > Preferences, and go to the key bindings and
give it a command key if you wanted.
If you were using stacking a lot, like I am,
you could do that. So let's put these in here.
10, fill equally. This one. Oops.
10, fill equally, and this last one. [BLANK
AUDIO] All right. Now I have these five stacks right here.
Okay, horizontal stacks. Now I'm going to take them and
put them in a stack. Okay? So
I'm going to put them in a vertical stack. [NOISE] Okay?
Now, these, I want, here, to all be spread out.
So right now you see the alignment is leading, so
it's putting all these things on the leading edge?
I want them to fill instead, so they fill the whole width.
Okay? I also want spacing here, okay?
10 between all of them, so I've got kind of a nice
little key pad. Now let's stack this with this.
So I'm going to select both of these and stack. Okay,
put them in a stack together. Again, I want spacing. I want,
do definitely do not want fill equally here, because that
would make this blue thing the same height as this big stack.
So we don't want that, we just want Fill. That means they're
gonna be their natural size, okay? [COUGH] So for this,
it's gonna be the size that fits this text and for this,
it's gonna be a size for
all those stacks to fit their contents. All right,
now I'm gonna finally use the blue lines. Okay, because I'm
gonna put this thing up in the upper-left corner right here.
Okay? And I'm gonna anchor it to that corner and
here's how we do that. We use the Ctrl key,
just like we did when we were dragging to the code.
We can also drag between elements in the UI. So,
I'm gonna drag between this, stack thing and this
outer container. So, I'm just dragging up to its top edge.
Now when I do, when I Ctrl+drag between things,
I can constrain them to be related in some way.
Like I could make them be equal widths. I can make this
thing be the same width as the container view. Or
I can do what I want, which is constrain the vertical spacing
of this to the top layout. In other words,
kind of attach that to that, so I'm gonna create that.
And you can see it creates this little I-beam,
this little tiny I-beam right there. Okay,
I'm gonna do the same thing to this edge, right here.
I'm gonna attach the leading space to the container margin,
okay? And I can do the same thing. Now,
by the way, when you do this, be careful when you Ctrl+drag,
you wanna make sure the thing you're dragging from
is the entire stack. Don't be,
you know, just Ctrl+dragging from this eight or it'll
actually pin the eight to the edge. Okay, you want to pin
this whole stack view and I'm going to show you how you can
select the whole stack view in a second here. Let's drag
this over, this is going to be the trailing space.
Okay. And now, here I'll show you how to, if I click on
this thing right here, it's selecting the two. But I want
to select the whole thing, so I'm going to do Ctrl+Shift.
Ctrl+Shift, okay, see it down in the lower left there,
Ctrl+Shift? Ctrl+Shift-click. When you do that,
it says, what thing under the mouse do you want to select?
Do you want to select that outer container, the big stack
view, or this little, interior stack view? So here, I want
the big stack view, the one that contains the whole thing.
All right, so and then when I Ctrl+drag, I'm being careful
not to Ctrl+drag from one of these buttons.
And here, I'm Ctrl+dragging from one the spaces there,
okay. So this is to vertical space into the bottom.
And so now I've tied them to the edges. Unfortunately,
I've tied these two edges too far away from the edges.
Okay. I wanna tie these two edges to right up next to it.
And the way I do that is, I can do it via the Inspector
right here, by clicking on this I-beam, you see.
This constant saying how far it is.
I can also double-click on this I-beam. And
it puts up a little thing here. So, I don't want her
to be 338 points away, I want her to be either some standard
value, or if a standard value doesn't make sense here,
which it doesn't, that's why it's grayed out,
then I'm gonna put it 0 points away. Bam. Okay?
Same thing I can do down here. Let's double-click this one.
Here, a standard value is available, so
I'm gonna click standard value. And
now it's putting its standard value from the bottom. Okay.
Now, when it's stretched there, it made these tall.
Okay, so that means we did something bad with our,
you know, spacing of the things, which is,
what did we do wrong here? Those are all fill equally.
Yes. How about this guy right here? Maybe this,
this guy fill equally. Okay we want this internal one.
Okay, this internal stack view, to be fill equally.
Glad I made that mistake, so I show you how to do that,
okay? So, we've got this all equally spaced out. This
looks pretty kind of funny in a square, but I bet it's gonna
look pretty good in portrait and landscape,
let's go take a look. All right here's portrait. Hey,
that looks pretty darn good. 4 times 8, you know, plus 9
equals. Square root, okay, cosine, pi, cosine. Excellent,
let's take a look at landscape, woohoo! It worked,
okay. So, very little work here. And we can make our UI
stretchable, okay? Now, later in the quarter, we're gonna
have more sophisticated UIs than just these stack things,
but we'll still be using that Ctrl+dragging to the edges.
Now, your homework assignment is to reproduce everything
I've done in these two days.
Add that floating point number,
add a little text field that shows a history of all
the things that have been typed in, and
add some more buttons. So you're gonna be doing outlets,
actions, and a little bit more. And that's
basically your entire homework okay? It's all posted.
See ya next week. >> For
more, please visit us at stanford.edu
    You must  Log in  to get the function.
Tip: Click on the article or the word in the subtitle to get translation quickly!

Loading…

Stanford - Developing iOS 9 Apps with Swift - 2. Applying MVC

190 Folder Collection
馮凱嵩 published on November 14, 2016
More Recommended Videos
  1. 1. Search word

    Select word on the caption to look it up in the dictionary!

  2. 2. Repeat single sentence

    Repeat the same sentence to enhance listening ability

  3. 3. Shortcut

    Shortcut!

  4. 4. Close caption

    Close the English caption

  5. 5. Embed

    Embed the video to your blog

  6. 6. Unfold

    Hide right panel

  1. Listening Quiz

    Listening Quiz!

  1. Click to open your notebook

  1. UrbanDictionary 俚語字典整合查詢。一般字典查詢不到你滿意的解譯,不妨使用「俚語字典」,或許會讓你有滿意的答案喔