字幕列表 影片播放 列印英文字幕 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
A2 初級 美國腔 斯坦福 - 用Swift開發iOS 9應用 - 2.應用MVC (Stanford - Developing iOS 9 Apps with Swift - 2. Applying MVC) 79 6 馮凱嵩 發佈於 2021 年 01 月 14 日 更多分享 分享 收藏 回報 影片單字