CS193P: Developing Applications for iOS with Swift Lecture 2 Notes

MVC, Calculator Demo continued…

What is MVC?

  • MVC is basically away for us to divide up our apps into 3 different camps, namely the Model, View, and the Controller.
  • The Model has to do with what your application does. It has nothing to do with how the app is displayed, or drawn on screen.
  • For a Calculator app, the Model will probably be the part that does the calculating.
  • The Controller has to do with how the Model is displayed, or presented on screen (UI Logic).
  • The Views are your Controller’s minions. These are the things the Controller is going to use to put things on the screen like buttons, labels, tables, etc. to display what is in the Model, and to get input from the user to update the Model.
It’s one thing to know how to divide your code into these three camps, but a crucial piece of the puzzle is the communication between them. That is, what’s allowed, and not allowed.
  • The Controller must be in complete control of the Model, and can talk to it anytime it wants. It needs total access because its job really is to present what is in this Model to the user, and to update the model based on user input.
  • The Controller needs to be able to use its minions however it wants to display the Model.
  • Most of the time the connection between the Controller and the View is via an Outlet. In Xcode this shows up as an @IBOutlet. For example, the label we used for the display in the calculator was an outlet that the Controller used to display numbers and results of calculations performed in the Model.
  • The Model is UI independent, and the View is completely UI. They should absolutely never talk to each other.
Can the View talk to the Controller? Yes… and No….
  • The problem with the View is that all the minions in there are generic objects like UIButton and UILabel. These were created by Apple years ago, and know absolutely nothing about the app you are building (in this case a calculator). However, sometimes changes in the UI need to be communicated to the Controller. The way the View communicates with the Controller then has to be “blind”, and “structured”.
  • The communication is blind in that the objects in the View don’t know what classes they are talking to. For example, buttons don’t know anything about CalculatorViewController.
  • The communication is structured because since there is no knowledge of the objects on either end they have to communicate in a well defined, predefined way.
What are some of the structured ways that the View can talk to the Controller?
  • Target-Action: This is very simple as the Controller can hang a target on itself by defining a method, ie. an @IBAction. The View then calls this method when it wants to talk to the Controller. We used this Target: Action way of communication in our calculator app when we made the touchDigit function because we wanted a press on the button to invoke a method in our calculator and tell us which button was pressed.

Sometimes the View needs to communicate something a bit more complex than “I was touched”. For example, a ScrollView, which is a generic View minion might need to tell the Controller that someone started scrolling, or indicate that a person zoomed in to a specific zoom scale. That is, it wants to notify the Controller because the Controller may want to react to that. Perhaps the Model is changed when you zoom in or out. Furthermore, maybe the ScrollView needs to ensure whether it is ok to do something. It may need to ask the Controller the question: “Should I allow vertical scrolling right now?”. We have a lot of messages that involves words like Should, Will, and Did that the minions want to ask the Controller. This form of communication is done via what we call a delegate.

  • Delegate is an apt term here because the View’s minions are basically delegating some particular responsibility to the Controller.
  • The protocol doesn’t really know about the class, but it is ensuring that whatever we are communicating with will respond to a specific set of methods or properties. The controller then signs up to listen for these methods. We’ll learn more about this delegate pattern, so not to worry if it is a tad confusing right now.
  • There is a special type of communication that exists between the Views and the Controllers because the Views cannot, and should not own the data they display. For instance, let’s say we have an iPod Touch with thousands of songs on it. Should we make the generic tableView which basically just shows my data in a list be the owner of all my music? That would be insane especially from a performance stand point, not to mention synchronizing issues. So if Views can’t own the data, what do we do?
  • Well the View is basically going to use another protocol, called a dataSource, but this time with a different set of questions than should, will, and did. It will ask questions like: How many items are in this data? Give me the item at this location, or index etc. So every time, say a tableView wants data, it asks the Controller, which in turn almost alway turns around and ask the Model. This makes for some nice optimizations. Since the tableView can only show about say 10 data items given the confines of our screen, it only asks for 10, out of the thousands of music on our phone. Then as we scroll, it keeps asking for more data, only keeping the ones that is currently in the display.

What about the Model communicating with the Controller?

  • So the model can’t communicate directly to our Controller, but what happens if there is a change in the Model, wouldn’t we want to update the Controller, so that this change can be reflected in the View. For example, let’s say I deleted one of the songs in my Model, then the Controller should know so that that song isn’t displayed in say a tableView.
  • Well the mechanism in how we set this up is to create that behaves kind of like a radio station. It then broadcasts things like ‘hey something about me changed!.’ The Controller then tunes it to this radio station, and when it hears a broadcast, asks the Model what changed since Controller to Model communication is a nice green line.
  • Now, when we build our app, we aren’t just building one giant MVC. We can have a series of MVCs all communicating with each other, with each other MVC connected via a View.
  • For example let’s say we are building a Calendar app.
  • Each of these different Views are three different MVCs, but their Views are connected to each other such that the Year MVC, is connected to the Month MVC, which is connected to the Day MVC.
  • Note that it is perfectly fine for different MVCs to share the same Model, and for Models to communicate with other Models.

What we don’t want is this:

That wraps it up for MVCs for now. Let’s dive back into creating our Calculator app so that it fits, or conforms to this MVC design paradigm that we just learned about.

  • Recall where we left off from our calculator, it was performing the operations or calculations in our Controller. Since these operations have nothing to do with the UI, we should put these type of functionality in our Model class. Create a new Swift file, and call it CalculatorBrain.
import Foundation
  • Since this is going to be our Model Class, we import Foundation, and not UIKit.
  • If we look in our navigator we see that we have a Model: CalculatorBrain, View: Main.storyboard, and Controller: ViewController.
  • Now we need to think about our Model, and what its public API is. API stands for Application Programming Interface. An API is a set of subroutines, definitions, protocols, and tools for building application software.
  • So we need to think carefully about what our model allows other people, like Controllers, to do.
  • We could opt to create a class, but lets opt to use a struct data structure as the model for our calculator.
  • In Swift, Structs are a lot more powerful than in other languages. In fact most of the data structures we have seen so far like String, Double, Array, Dictionary etc. are under the hood implemented as Structs.
  • There are two main differences between a struct and a class in Swift. Classes have inheritance, whilst structs do not. Thus, if you’re building an object that might need to be subclasses to be extensible, then you probably want to make it a class. The other difference is that classes live in the heap, and we have pointers to them. Structs do not live in the heap, and they are passed around by copying them. That is, classes are passed by reference, and structs are passed by value. This is a significant difference.
  • One might think that since these are passed by value that it will be highly inefficient to be copying them around, but the Swift compiler optimizes for this, and these value types are basically copy-on-write.
  • So back to that question about what our public API does. We know that somehow we will need to perform the operation, or calculations, so we’ll have a function for that.
func performOperation(_ symbol: String) {
}
  • We will let some mathematical symbol be the operation we are performing.
  • Now if we’re performing operations, we must need operands to perform on, so we’ll need another function that sets the operand. Let’s have our entire calculator work with Doubles, so let the the operands be of type Double.
func setOperand(_ operand: Double) {
} 
  • Note that we use _ as our external parameter because we do not want when we call this function that it’s like this:
setOperand(operand: 5.0)

It makes more sense for us to call it like so

setOperand(5.0)
  • The former is rather verbose, but from the latter reads more succinctly, and its very evident from the name of the function that 5.0 is our operand.
  • Thinking about it, we will also need to get the result of these operations. In Swift like manner, lets create a computed property to get us that result.
var result: Double {
}
  • One think we need to be concerned with though is that we don’t want anything else to be setting our result. How do we make this read only? Well, just like currentTitle, on a button we can just make it get.
var result: Double {
get {

}
}
  • If we think about the internals of our calculator, we know that a calculator accumulates its answer as it’s calculating, so let’s create a variable that captures this running result. Other people should not be able to access this property so make it private.
private var accumulator: Double
  • Notice that even though we haven’t initialized our variables we don’t get that no initializers error message like we had for classes. That is because Structs automatically get an initializer that will initialized all their uninitialized variables.
  • Let’s think about that accumulator for a bit. When the user first starts up the calculator, the accumulator would be in the Not Set state, so it makes sense to make it an optional Double.
private var accumulator: Double?
  • If we have this internal accumulator, how can we use that to implement our functions?
  • setOperand basically just sets the accumulator to the operand because when you set a new operand that just replaces whatever was in our accumulator. To get the result, we just return the accumulator. We do have some errors though. It is saying that ‘self’, meaning our CalculatorBrain is immutable, and asks us to fix it by adding mutating to the beginning of the function.
  • This brings up another difference between Structs and Classes. Whenever we have a method that changes the value of the struct itself, we have to mark that method as mutating.
  • We do have another error. That’s because result is a Double, and accumulator is an Optional. Should we just unwrap this like so then?
return accumulator!
  • Absolutely not! There are going to be many instances when our accumulator will be in the not set state, and unwrapping a Not Set Optional will crash our program. When the calculator first starts up the accumulator will be in the Not Set state. Also, when we are performing an operation say, 5 * 3, the accumulator will remain unset as we enter 5 * until we hit 3, and then it’s Set, and provides the result.
  • We should also make result an Optional because if someone asks for an answer before we can evaluate the operation, we should just not return a result. For instance, if the user asks for 5 * then we can’t provide them a result.
var result: Double? {
get {
return accumulator
}
}
Model: Left, and Controller: Right on the screen at the same time.
  • Let’s use the public API that we created to handle what we previously implemented in the Controller. We see that we don’t need to change touchDigit, or displayValue because those behaviors are mostly things that have to do with the UI. That’s one of the cool perks of doing MVC. Our UI is independent of our Model, or vice versa. We do need to take out performOperation though.
  • Let’s rewire this @IBAction performOperation method to use our Model to perform the operation. Ok so if we are to perform an operation, we know that if the user is in the middle of typing a number we need to set that number as an operand.
if userIsInTheMiddleOfTyping {
// set operand
}
  • Since setting the operand is something my model should do. How do I communicate with the CalculatorBrain from the Controller? Let’s create a calculator brain for our Controller. It will be private because we don’t want anyone else coming in and messing with this controller’s calculator brain.
 private var brain: CalculatorBrain = CalculatorBrain()
  • Since Swift can infer the type for brain, we can just use the more succinct…
private var brain = CalculatorBrain()
  • Now that our controller can talk to our Model, we can now set the operand, which would be the displayValue. Once we set the operand, we are no longer in the middle of typing.
  • So now when we get the mathematical symbol from the button pressed we will just ask our brain (Model/API) to perform the operation with that symbol.
  • So when we click the button to perform an operation the Controller responds to the click via target-action, and is saying hold a minute now, I’m a UI guy. I don’t know anything about how to perform an operation. You know what let me ask my Model!
  • Once the operation is done, there is one more thing for us to do. That is, we need to set our displayValue to our brain’s result.
displayValue = brain.result
  • We can’t just write the above code though because result is an Optional. Let’s unwrap it the safe way using if let.
  • Now we have completely taken all the calculations of our calculator out of our Controller. This means that we can now focus our attention on building a very powerful Model.
  • Let’s fix this implementation of performOperation that we copied over into our Model. Well we need to change mathematicalSymbol to symbol since the internal argument is symbol. Since we are now in the Model, and not in the UI, we don’t have displayValue, so let’s just set those values to the accumulator.
  • We still have some issues here because accumulator is an Optional. We cannot find the square root of an Optional. We could first safely unwrap the accumulator before we find its square root value with an if-let.
  • Hmm we still have some error messages, what might those be? Well, remember our CalculatorBrain is implemented as a Struct, which is a value type. In this performOperation method, we are changing our Struct, so we need to mark this method as mutating.

We now have an MVC version of our calculator. Build and Run …

Our calculator still works! Now we will use some cool Swift features that we might not see in other languages to make our calculator a lot more extensible. Right now, if we want to add more operations to our calculator we will have to do a ton of different switch cases, then we might have to unwrap their values with if-let, and that’s just for unary operations. Imagine when we need to add binary operations. Thus, we need to build an engine that can easily deal with constants like π, unary operations like √, and binary operations like +.
  • Delete everything in the performOperation method because it’d be a mess if we continue like that.
  • Let’s think about making our calculator more extensible starting with constants. Wouldn’t it be cool if we had, say a table with the constants and their corresponding values, like so:
[ "π": Double.pi,
"e": M_E
]
  • That way when we want the value of a constant, we just look it up in our table. How do we create a table in Swift? We use a Struct called a dictionary. Let’s create this dictionary and call it operations. Dictionaries in Swift can have a generic type, so it can be any type as long as the key, that we use is hashable.
  • So now in our performOperation method, we just retrieve from our dictionary the value of the symbol sent to us.
  • Why do we need to use an if-let when getting the value from a dictionary? That’s because maybe what we are looking for might not be in the dictionary, and so it’s value will be an Optional with a value of nil.
  • When we build and run, we see that the only constant we now support, π, works! This is now more extensible at least for constants that the implementation we previously had. It’s pretty weak that we can only do this for constants only though, wouldn’t it be cool if our table can look like this:
  • This way it wouldn’t just work for constants. However, we have a challenge. Our dictionary’s value for a key must be a Double, but what is the type of square root? It’s really a function if you think about it. So we need to create a dictionary with mixed types in it. Let’s create that type using an enum. An enum in most languages is usually just a simple data structure with discrete values. In Swift, enums are more powerful than other languages as we’ll soon see. Let’s make our case just deal with constants and unary operations for now.
  • Once we have defined our type we can now let our dictionary work with this type instead of a Double.
  • Note that our dictionary type is now <String, Operation>, and not <String, Double> like previously. We use dot syntax to access the different cases of our type. π is a constant so we do Operation.constant. Square Root is a unary operation so it’s type is Operation.unaryOperation
  • Let’s change our performOperation to this for now:
  • We no longer save the retrieval of the value in our dictionary in a constant variable because now we will get back an operation which could be either a constant, or a unary operation.
  • In creating this dictionary with the new types though we have actually lost the fundamental meaning of the operations. We know that π is a constant, but we have no way of getting its value.
  • Let’s think back to how Optionals are defined in Swift. An Optional is basically an enum with two states: Not Set, in which case the value is none or nil, and a Set Case in which the value is Some.

In the some case we have an associated value. This associated value isn’t unique to Optionals. It is a behavior that we can apply to all enums. So what we can do is that when we define our types, we can give them an associated value. To relate this to our dictionary, we can just provide the associated value of that operation. In the case for a constant, the associated value will be a Double.

constant has an associated value of Double
  • The corresponding change in our dictionary for constant case would look like this.
  • Notice that the associated value we give for the constant value, π, is the value of π as a Double (Double.pi). M_E is the Double value of the constant e. We’ll deal with unaryOperators and even binaryOperations a bit later.
  • Once we have the operation to perform how do we get the associated value out. We can switch on the varying operations to see if it is a constant, or unaryOperation.
  • As can be seen where the Double is higlighted for a constant, that’s basically saying, hey I want to help you get your associated value out. So how do we get that value out? We could create a variable there that captures that value, like so:
case .constant(let value):
  • This variable value will have the associated value, so we can then set the accumulator to that value.
  • Swift cases don’t fall through to the next case so we don’t need the break statements in each case. In addition, we do not need a default case in this enum since we have exhausted the two possible states that an operation can currently be.
  • Let’s try to tackle unaryOperations. This might be a bit tougher. What we would really love is this:

That is, set our associated value as a function. Can we do that in Swift? Of course! One of the cool aspects of Swift is that functions are first class citizens. They are like normal types, just like Double, String, Struct, or whatever.

  • How do we make our associated value be a function like square root or cosine. We those functions take a Double as input and produces a Double as output. Essentially the function signature should look like this
(Double) -> Double
  • All we do is add this as the associated value for our enum’s unary operations.
  • Let’s play around with this type functions business for a bit in an Xcode playground.
  • We create a variable, f, that has the type (Double) -> Double. The sqrt function has the same type, so we can make f equal sqrt. Now we can call f(81), and since f is equal to square root, we get 9. We can even let f = cos, since cosine is a function that takes a Double and returns a Double.
we see cosine of π/3 (60 degrees) is 0.5
  • We can even create our own function. Let’s write a function changeSign that just changes the sign of a value. Since changeSign has the type (Double) -> Double we can set f to changeSign, and when we do we get the value of 0ur input, 81, changed to -81.
  • Let’s even use this function we just wrote changeSign and add it to our calculator. Place it at the top of the file as a global function.
  • We copy and paste any operation button since it is already hooked up to performOperation. Our new button will now share that same IBAction, or method. Change the title of the button to plus or minus. You can find these different symbols clicking the edit menu, and then Emoji & Symbols.
  • Also, add the ± operation to our operations dictionary. It is a unary operation, and the method that corresponds to this operation is the changeSign method we wrote, as a global function.
  • Let’s go back to our performOperation method. Remember how in the case of a constant, we were able to get the associated value, like this:
case .constant(let value):

We can also get the associated function for a unary operation like so:

case .unaryOperation(let function):
  • Note that you can name the variables to get grab the associated values almost anything you want, though, value, and function seem like very apt names.
  • We then call this function on our accumulator, and that will be the new value of our accumulator. Currently, we are doing this:
accumulator = function(accumulator!)
  • We should safely check whether our accumulator is nil though. We could use an if-let, but another way to safely unwrap our Optionals is just simply check that the Optional isn’t nil.
  • Our calculator now works perfectly fine for constants and unary operations. We should try tackling binary operations now. This is a bit more challenging as we are really doing two operations. For instance 5 * 3 = 15 will put us in a weird pending state as soon as we hit *. Hitting = then finalizes the operation. Thinking about it, we are going to need a data structure that can remember the 5 * until we hit the = button.
  • Let’s add an equal operation, and a binary operation to our list of operations that our calculator can handle. Notice that the type of a binary operation should be one that takes two Doubles and return a Double.

Add the corresponding operations to our dictionary. We will experiment with * for developing how we will do binary operations.

  • Where is our function multiply though?
  • Let’s create multiply as a global function at the top of our file, next to the function changeSign.
  • Before we can deal with the binary case in our performOperations method we should create that data structure that allows us to hold on to the pending operation eg. 5 *
  • Our data structure will be a Struct that holds our function and the first operand.
  • In Swift Structs can have methods. We could add a method that given the second operand, will complete the binary operation. So for example, say we want to multiply 5 * 3 to get a result of 15, our PendingBinaryOperation data structure will grab on to the 5 *, then when it receives the second operand 3 it will perform the multiplication.
  • This method on our PendingBinaryOperation doesn’t need to be marked as mutating because it isn’t actually changing the PendingBinaryOperation. We should also make this data structure private.
  • Let’s create a pending binary operation in our model
  • We made it an Optional because we are not always in a pending operation, and in that case this variable will be in the Not Set state.
  • So when the user hits *, for example, which is a binary operation we are going to create a pending binary operation.
  • The first operand in this pending binary operation will be whatever is already in the accumulator, so we should make sure we safely deal with this since accumulator is an Optional. Also, when we are in this weird pending state, our accumulator is waiting for some result, so in this state it is nil.
  • What about when the = button is hit?
  • We made a function to perform the pending binary operation when the = button is hit.
  • The ? after pbo does Optional Chaining. It unwraps the Optional. If the Optional is nil then it ignores the rest of the command. For now though, let’s just unwrap with the !, but make sure to check the pbo, and our accumulator is not nil.
  • Let’s set this result to our accumulator. We still get an error because we need to mark our function as mutating since it is modifying the state of our calculator brain.
  • Once we have performed this pending binary operation we will no longer be in a pending binary operation so we set pbo to nil.
  • pbo is a pretty bad name. Let’s rename that to something more descriptive, such as pendingBinaryOperation. Double click on pbo, click the arrow that appears next to it, then click edit all in scope. Type the new name and this will rename all occurrences of the old name with the new one.
  • pbo has been renamed to pendingBinaryOperation in our file.
  • Now that our Model is now fully equipped to handle binary operations let’s add some binary operations to our UI.
  • Note that multiplication has the title x, and not *. Make sure to change that in the operations dictionary.
  • Since we also need to added ÷, x, –, and +, to our dictionary, perhaps we should add those functionality to our dictionary as well.
  • Do we really need to now go and implement global functions like we did for multiply, and changeSign for minus, divide, and add? All of a sudden our new approach of using a dictionary appears like we haven’t gained much. How are we going to fix this?
  • We are going to use a feature in Swift called Closures. A closure is a function embedded right in-line our code.
  • We could actually replace multiply in our operations dictionary with the body and parameters of the multiply function we created.
  • Copy this from our function multiply, and paste it right in-line.
(op1: Double, op2: Double) -> Double {
return op1 * op2
}
  • It doesn’t quite work as yet, as we need to replace the open brace { with the word in, then place the { at the beginning.
  • Now we don’t need that global function multiply in the beginning because the closure, which is an anonymous function is handling multiply right in line our code. This looks a bit ugly though. Let’s use Swift’s capability for type inference to make this look a lot neater.
  • Swift knows that since our closure is really a binaryOperation’s associated value, so it knows the type of op1, and op2. Basically, it knows the function prototype.
  • Swift also knows our function returns a value, so we don’t need return. Placing this on one line, and we are definitely looking better!
  • Swift also provide some argument names in the order $0, $1, $2…, so we can actually just get rid of op1, and op2.
  • Oh wow, aren’t we looking good! The essentials of what we are doing is write here without the distractions of extraneous types.
  • Now we can easily complete the table for the rest of our binary operations, even for our function changeSign, which we can now delete the global function.
  • Now we can add as many operations as we want with just one line of code.

Build and Run …

Our calculator works!!

Let’s simulate a rotation of the device by clicking hardware in the simulator, and then one of the rotate options, or you can hold down command, and right arrow key. Our calculator is now unusable. We can’t even use the = button.

Our UI should be able to work in any orientation or for any device. Let’s get to it!

  • Our basic design strategy would be to stack the rows of buttons on top of each other. Then stack this block of buttons together with our display to get one block of UI elements, then we will provide constraints for this block and the edges of our device.
  • Select a row of buttons, hit editor, then embed, then stack views. This operation is so frequent that we have a special button for it in Xcode in the lower right hand part of the screen.
Embed in stack view is second from the left.
  • As the attributes inspector indicate this is a horizontal stack view. Give it a spacing of 10, and make sure that distribution is set to fill equally, since we want all our buttons to be the same size. Repeat the above process for all our buttons.
  • Let’s now stack these horizontal stacks of buttons vertically. Select all the buttons and click on the embed in stack button. It’s smart enough to automatically provide a vertical stack,
  • It did push them all to the left though. We want all our buttons to take up as much space, so let’s change the alignment of our vertical stack to fill, and of course we need some spacing. Set vertical spacing to 10.
  • Let’s now stack our display and our key pad. Select them both and embed in a stack. Set the spacing to 10, and set the alignment to fill.
  • Now we have a beautiful looking UI as one block. All we need to do now is tie the edges of this UI to the edges of the device.
  • Select the entire block and use the guided blue lines to place it near the upper, and left margins as shown below.
  • Now control-drag from this block of UI elements towards the top of the canvas margins and a pop up with provide some options basically asking what kind of relation ship do you want the top edge of your UI to have with the top edge of the device. Select Vertical Spacing to Top Layout Guide.
  • Do the same for the left, by tying it to the Leading Edge, then to the right, by the Trailing Edge, and then to the Bottom edge.
  • Now our UI is pinned some distances from the margin. Double click on these blue lines for vertical towards the bottom, and horizontal towards the trailing edge and set the constant value to 0, or a standard value if available since we want our UI to be directly on these edges.
  • It’s trying to stretch out one of the stacks. We need to set its distribution to fill equally.
And that’s it for today! Now we have a perfectly working calculator that not only looks good in portrait, but when rotated in the landscape orientation as well.