Chapter 19: Tipsy Tip Calculator App

Have you ever been at a restaurant with a group of people, received your check and felt embarrassed because you didn't know how much 15% of your bill was? You are about to save yourself a lot of heart ache while you watch all your friends experience anxiety. You are about to build the best looking tip calculator you have ever seen (in our humble opinion of course).

What you will learn

  • In this chapter you will build an app that will give you your first real taste of MVC in a project. Take your time and have fun!

Key Terms

  • MVC
  • Model
  • View
  • Controller
  • Data Encapsulation

Resources

Download here: https://github.com/devslopes/book-assets/wiki


Model - View - Controller

When you, as an app user, open an application you are drawn to several buttons, a menu bar, some images and much more. You are looking at the User Interface or UI for short. When you see information on your phone's screen, you may have stopped to wonder where the data on your screen comes from and how it gets there. That data, as we call it, is all handled behind the scenes so the user doesn't have to worry about it. Where that data is stored is called the Model layer of the app. Data can't just appear out of thin air and must be stored somewhere. The UI has to know where to find it and the Model has to know where to send it.

This communication between the Model and the View(UI) is handled in the View Controller. All three of these make up one of the most, if not most, used practices in iOS. It is called, Model - View - Controller or as you will hear all throughout the industry MVC.

Project creation and building the UI (User Interface)

Let's begin by creating a new project Single View Application application and call it whatever you would like. In this case we are calling it TipsyTipCalculator.

Once you have created the new project select Main.storyboard found in the project folder on the left hand side of Xcode. This should be your starting point (Figure 2.3.1).

Figure 2.3.1 Screenshot 2016-10-18 15.13.07.png

Now go to the search bar at the bottom of the inspector column and type in UIView.

Drag a new view onto the View Controller and change its color to whatever you want. In this case we will be using #00CA79 (Figure 2.3.2).

To change the color by selecting the Attributes Inspector (attributes-inspector.png) found in the right column or the Inspector Column.

Click on the Background dropdown menu and select a color.

If it is the first time you are opening the color selector you will most likely see a round color graph.

At the top of window you will see five different selections. You can choose from the circle graph the color you desire or click on the slider icon and you will be able to enter the Hex Value provided above.

Ensure RGB Sliders is selected from the dropdown menu located above the sliders.

Screen Shot 2016-11-22 at 5.42.32 AM.png

Now that you have chosen a color drag the view to the top left corner of the View Controller and stretch it to the right (Figure 2.3.2).

Then we need to give it some constraints (Figure 2.3.2).

On the bottom of the screen click the Pin menu and set the following constraints to the view (Figure 2.3.2):

0 from the left 0 from the top 0 from the right Height of 60

Click Add 4 Constraints at the bottom of the Pin men.

Figure 2.3.2 Screenshot 2016-10-19 16.22.16.png

Now that we have the the top menu bar lets add a UILabel to it with the title of the app (Figure 2.3.3). In our case we are going to name it Tipsy.

Type UILabel in the search bar at the bottom of the inspector column. There should only be one object to choose from – "Label".

Click and drag the label to the middle of the navigation bar you just created. This will put the label inside this view. Now we need to add some constraints to keep this label in the center of our navigation bar. When you add those constraints, they will be bound within the green navigation bar.

You want to make the label big enough for the user too see easily but not too big to overtake the navigation bar it is in.

To increase the size of the label itself hover over the white boxes surrounding that label. You will see black arrows that indicate the ability to stretch or shrink the label.

Lets change the text of the label to read "Tipsy" (Figure 2.3.3). To do so, click on the label and open the Attributes Inspector. Locate the Label field in the Attributes Inspector. It is directly beneath the Text drop-down menu. Edit the text in that field and press Enter to update it.

Then change the color to white by clicking the Color drop-down menu in the Attributes Inspector and selecting White Color.

Now change the font size to 24. If your text dissapears at all it means the label is to small and you need to increase it :).

Next Change the Alignment from Left to Center.

Once that is done open the Pin menu (pin-button.png) and give your label a fixed Height and Width. Do so by checking the box next each word and Add 2 Constraints at the bottom of the Pin menu (Figure 2.3.3).

Figure 2.3.3 Screenshot 2016-10-20 09.36.45.png

Since we know we want this label to stay in the center of the view we are going to give it two constraints that make it do so (Figure 2.3.4).

Open the Align menu (align-button.png), found to the left of the Pin menu. Check the box next to both Horizontally in Container and Vertically in Container and click Add 2 Constraints.

Figure 2.3.4 Screenshot 2016-10-20 09.35.46.png

Lastly lets change the main view color to E1F0F1 (Figure 2.3.5). Make sure to select the highest level view as shown in the image below, then locate the Background color in the Attributes inspector on the right. You should remember how to change the color by now ;).

Figure 2.3.5 Screenshot 2016-10-20 13.34.37.png

Next, using what you learned, I am going to provide an image of the layout of the rest of the app interface (Figure 2.3.6). Do not add constraints yet. You are just adding UI objects.

The following is a list from top to bottom of all the objects you will need. Add the following objects in to your View Controller just as seen in Figure 2.3.6:

  • UITTextField
  • UILabel
  • UISlider
  • UIImageView
  • UILabel
  • UILabel
  • UIImageView
  • UILabel
  • UILabel
  • UILabel
  • UISlider
  • UIImageView
  • UILabel
  • UILabel

Figure 2.3.6

Screenshot 2016-10-20 10.30.40.png

Now that you have all of the above objects in the View Controller lets make sure they stay there with constraints.

Select the UITextField and give it the constraints it needs by clicking the Pin menu (pin-button.png) (Figure 2.3.7).

Pin it to its current location and give it a height of 50.

Figure 2.3.7 Screenshot 2016-10-21 01.03.30.png

Next, select both the Tip Percent Label and the first slider and put them in a Stack View. Do this by holding shift while you click on the objects you desire (Figure 2.3.8).

Once selected click the Stack button (Screen Shot 2016-11-22 at 6.03.38 PM.png) found two icons to the left of the Pin menu.

Figure 2.3.8

Screenshot 2016-10-21 01.10.18.png

Then put each row of objects in its own Stack View (Figure 2.3.9).

Notice all of the objects squished together. Do not worry, that is normal. We will fix that in minute.

Notice in the image below on the left-hand column there are five different Stack Views.

Figure 2.3.9 Screenshot 2016-10-21 01.14.31.png

Next, select each Stack View from within that column by holding Shift while clicking on each one (2.3.10).

Figure 2.3.10

Screenshot 2016-10-21 01.21.31.png

Put all of those Stack Views into one Stack View by clicking the Stack icon while all five Stack Views are selected.

Again, the elements appear a bit squished (Figure 2.3.11).

Figure 2.3.11 Screenshot 2016-10-21 01.24.40.png

Next, make sure the main Stack View is selected and give it the following constraints in the Pin menu (Figure 2.3.12):

Screenshot 2016-10-21 01.46.22.png

  • Top = 46
  • Left = 10
  • Right = 10
  • Bottom = 200

Then, in the Attributes Inspector on the right you will see Alignment and Distribution. Make sure Alignment is set to Fill and Distribution is set to Fill Equally.

Figure 2.3.12 Screenshot 2016-10-21 01.29.39.png

Now select the first Stack View within the main Stack View and change Distribution to Fill Proportionally and change the Spacing from 0 to 16 (Figure 2.3.13).

Select the Tip percent label and give it a Width of 70 inside the Pin menu.

Select the top-most slider and give it a Width constraint of 237.

Figure 2.3.13

figure-2.5.13-img.png

For the rest of the Stack Views I am going to provide you with the images and you need to make sure everything is changed on yours to match mine. Consider this a challenge to get better at using constraints 😉.

Stack Views are tricky and take a bit to get used to so if you are feeling a bit overwhelmed, take a breath and keep on going. Practice and repetition is what makes this coding stuff stick in your mind.

Figure 2.3.14

Screenshot 2016-10-21 02.20.07.png

Figure 2.3.15

Screenshot 2016-10-21 02.21.32.png

Figure 2.3.16

Screenshot 2016-10-21 02.24.46.png

NOTICE we added width constraints to the label and slider just as we did to the previous label/slider Stack View.

Figure 2.3.16a

Screenshot 2016-10-21 02.25.48.png

Figure 2.3.16b

Screenshot 2016-10-21 02.28.44.png

Last Stack View

Figure 2.3.17

Screenshot 2016-10-21 02.30.32.png

Now let's run the project to see what we came up with (Figure 2.3.18).

Figure 2.3.18

Screenshot 2016-10-21 03.28.32.png

You did it! The UI (User Interface) of our View layer is all set up to be connected to code now. If you try out the app in the Simulator the sliders move back and forth. We used Stack Views to make our lives easier. Now imagine having to add individual constraints to each and every one of those objects... Scary!

Keep in mind, Stack Views will not always be the best choice, just as the storyboard might not prove useful with certain instances.

Lets move on to some coding where the real fun begins.


Model

First right-click on the yellow project folder with the name of the app. In our case it is TipsyTipCalculator. Then find New Group and click on it. This will create a new folder which you will then entitle Model.

Do the same process again but this time name the group Controller. We will come back to the Controller later.

For now right click the Model folder and click New File. Then choose Swift file. Now name the file TipCalcBrains and create the file.

You should now have a file structure as follows:

Figure 2.3.19

Screenshot 2016-10-21 03.46.30.png

Click on TipCalcBrains.swift to open it in the code editor.


Helpful Hint: If you would like multiple files open so you can compare, double-click on a file and it will open in a new window.


This is where you should be at to begin coding (Figure 2.3.20):

Figure 2.3.20 Screenshot 2016-10-22 13.44.11.png

In our app, there are several pieces of data that we must store to calculate the tip for a meal. What we need to do now is create variables in our Model which store that data, while making sure the data is secure and cant be changed or altered by other files.

From looking at the TipsyTip app we see we need a variable that stores the bill amount, a variable that stores the tip percent, a variable that stores the tip amount and a variable that stores the total amount. For now we will just work on everything except the bill splitting feature.

(2.3.21) First create a class named TipCalcBrains and initialize the variables we mentioned above as type Double with an initial value of 0:

private var _billAmount: Double = 0
private var _tipPercent: Double = 0
private var _tipAmount: Double = 0
private var _totalAmount: Double = 0

To make sure only the TipCalcBrains file can see and change these variables we must add the keyword private like so:

Figure 2.3.21 Screenshot 2016-10-23 07.31.25.png

You might be asking yourself why we made these variables private and if they are private how can the data be stored when the user enters it? This, my friend, is where Data Encapsulation (a.k.a "Data Hiding") comes into play.

Since we're using private variables, we have hidden the data from any other entity that may want to access it. But for the purposes of our app, we need to be able to access it.

Run the project again if you do not have it running already. Looking at our app interface, you can see that there are two instances when the user needs to be able to see and update data (read/write) and two instances when the user needs to only see the data (read-only). I want you to look at the running app and determine which instances are in need of the ability to be read and written to and which are in need of only being read.

It is okay if it didn't jump right out at you. From the UI we can see the user will see and update the _billAmount when they enter the bill amount in the UITextfield and the _tipPercent by changing the position of the UISlider you created.

First create a variable named var billAmount of type Double, then a space followed by an open bracket then press enter to close it (Figure 2.3.22).

You might be thinking, why does this variable have a bracket? I thought only functions have brackets. You just learned something new, didn't you? Creating this variable in such a manner allows us to safely access the private variable.

Inside the brackets we are going to create a getter and a setter. A getter retrieves data for the purpose of seeing it. A setter can then update that data.

Figure 2.3.22 Screenshot 2016-10-23 07.48.32.png

To create a getter simply write the word get followed by an open bracket {. Push enter to close it.

Inside of the brackets return _billAmount.

Now create a setter by writing set next to the closing bracket on the same line of the closing getter bracket. To close your setter, end it with a closing bracket just like in Figure 2.3.22.

Inside the brackets you are going to set the new value by using Xcode's built in function newValue, which updates the variable with the new data for you (magical!).

To do so write _billAmount = newValue.

Now do the same with the tip percent.

_tipAmount and _totalAmount need only be seen. Therefore we need only a getter for each variable. If setters are not needed then we need only to return the desired variable within the brackets (2.3.23).

Figure 2.3.23 Screenshot 2016-10-23 08.08.10.png

Once you have created the last two getters lets initialize _billAmount and _tipPercent with the values the user will update (Figure 2.3.24).

Figure 2.3.24 Screenshot 2016-10-23 08.14.11.png

Next we need a function that calculates the tip (Figure 2.3.25).

Figure 2.3.25 Screenshot 2016-10-23 08.17.02.png

Look at you! You just created your first Data Model. That is so cool! Now you will see how to connect the Model layer with our data with the View layer (UI) you created earlier. Let's get to it!


View Controller

Remember that group we created called Controller? Drag the ViewController.swift file into that folder and open it by clicking on it. This is the file that controls the data passing between the View (UI) and the Data Model. You will notice mine is titled MainVC.swift (2.3.26). If you would like you can change yours but if you do be sure to change the reference name in the storyboard from ViewController to MainVC. You can do that by locating the Identity Inspector located directly to the left of the Attributes Inspector. You don't have to do this, but it's good to know how in case you want to.

Figure 2.3.26 Screenshot 2016-10-23 08.58.35.png

Okay, moving on.

Now get rid of all the junk you don't need so it looks as follows:

Figure 2.3.27 Screenshot 2016-10-23 09.05.55.png

You will notice on line 9 import UIKit. This tells the file that you are going to need access to all the methods and variables in UIKit.

The function viewDidLoad() is called when the view has been created and data is ready to be presented to the user.

Now that we better understand the UIViewController let's get to the fun stuff!

Select the Main.storyboard file then select the View Controller from within Interface Builder.

On the top right corner of your Xcode you will see the following icons (make sure they match mine exactly):

Figure 2.3.28 Screenshot 2016-10-23 09.45.47.png

The two blue rings represent the Assistant Editor. This allows you to have a split view with the Storyboard on one side and the View Controller file on the other. Whatever you click on in Interface Builder, the associated code file will load in the Assistant Editor. In our case, we want to edit our View Controller file, so click on the View Controller and it should automatically open the correct file. If for some reason it does not you can manually find the file by clicking the Automatic button at the top of the Assistant Editor and navigating to the appropriate file from within the Manual folder:

figure-manual.png

After you've followed the directions above, you should be seeing this in Xcode:

Figure 2.3.29 Screenshot 2016-10-23 10.24.39.png

We need to connect the objects we created in the View layer (UI) to the View Controller. There are a few ways to do this but we are only going to use one method for this tutorial.

Create an @IBOutlet for each object you intend to store data in by adding the following code within the MainVC class like so (Figure 2.3.30):

Figure 2.3.30 Screenshot 2016-10-23 21.15.37.png

Now Ctrl + Click and drag from the enter bill amount TextField to the enterBillTF Outlet and release from the mouse or track pad first (Figure 2.3.31). Make sure to hover over the variable name itself or it will not highlight.

Figure 2.3.31 Screenshot 2016-10-23 20.47.58.png

Do the same for the tip percent label, tip slider, tip amount label and total label.

Open the Inspector column (Screen Shot 2016-11-23 at 5.48.48 AM.png) by clicking on the icon at the upper right hand corner. We need to set some values for the slider.

Select the slider and open the Attributes Inspector (Figure 2.3.32) then change the Value to 0.15, the Minimum to 0.1 and the Maximum to 0.25.

Just as you guessed the Value the initial value of the slider when the app first opens. The Minimum and Maximum values represent the minimum and maximum values that your slider can understand.

Figure 2.3.32

Screenshot 2016-10-23 21.04.03.png

Now close the Inspector column by clicking the icon (Screen Shot 2016-11-23 at 5.48.48 AM.png) at the upper right hand corner.

We are back to the split screen with the Storyboard open next to the View Controller. Lets make it one screen so we are just working in the MainVC.swift file.

Next we need to create an instance of our data model, TipCalcBrains, so we can access the methods and variables (2.3.33) within it. We will then give the parameters billAmount and tipPercent arbitrary values of type Double.

2.3.33 Screenshot 2016-10-23 21.17.39.png

Now we need to create two functions to let the View Controller know its time to calculate the entered data from the user.

We do this by creating an @IBAction for the bill amount and the tip amount.

To create an @IBAction simply hold down the control key on your keyboard while you click and drag over to the View Controller into and empty space below the viewDidLoad function but still within the brackets of the MainVC class (Figure 2.3.34). You should see a small box pop up.

Figure 2.3.34 Screenshot 2016-10-23 21.43.01.png

Change the Connection to Action and the Event from Value Changed to Editing Changed. This is telling the View Controller every time the text inside this textfield changes you let me know so I can execute the @IBAction we just created.

In the Name field, type billAmountChange then click Connect. An @IBAction will be created with the name you supplied.

Now do the same for the tip slider except the Event should be set to Value Changed (Figure 2.3.35).

Figure 2.3.35 Screenshot 2016-10-23 21.47.51.png

Close the Assistant Editor so that you have just the MainVC.swift open. We are going to test the app up to this point to make sure everything is good to go.

Inside each @IBAction you created put the following line of code in its respective function:

  • print("my text changed")
  • print("Hey the slider changed")

Figure 2.3.36 Screenshot 2016-10-23 22.13.59.png

Before we run the program we need to change a few things. Back in Main.storyboard open the Attributes Inspector and click on the bill amount Text Field. Double-click the text saying "enter bill amount here" and press ⌘ + X to cut it. Paste it into the Placeholder space in the Attributes Inspector and press Enter to save it.

Also scroll down to where it says Keyboard Type and change it from Default to Decimal Pad. That way the user can enter correct data in number format. Finally, change the text color to the same green color we have been using in this app.

Okay, run the app and tap on the bill amount text field to put some numbers in. Then slide the slider back and forth. You should see those print statements at the bottom of Xcode (2.3.37).

Figure 2.3.37 Screenshot 2016-10-23 22.24.02.png

You are doing a great job. I know this is a lot to take in. Trust me, I have been in your shoes many times and walked the path of confusion and less understanding. Just keep pushing through, going back over what we have discussed and with practice you will get it.

So we have the View Controller communicating with the View (UI) and we created an instance of the data model. Now we need to utilize what is in that data model.

Since we know the @IBActions are working go ahead and delete the print statements.

Lets create two functions. One will update the variables we created in the TipCalcBrains.swift (the data model) and the other will update the UI with the calculated totals for the tip and the overall bill (2.3.38).

Figure 2.3.38 Screenshot 2016-10-23 22.40.35.png

Now I want you to look long and hard and understand each line of code. Do not just copy the above and hope all works out for the best. If you do that you are only cheating yourself.

Remember that you are updating the tipPercent variable in the Model with the value of the slider and then casting it to a double.

Then you are doing the same for the bill amount except first you are casting the value as a string then getting the doubleValue. The method you are using to access and update these variables is called Dot Notation. I want you to study more about that on your own time. Use what I just explained to you to then understand the other lines of code you see.

The last function we need to create is for the purpose of updating the tipPercentLabel (2.3.39).

Figure 2.3.39 Screenshot 2016-10-23 22.52.49.png

Nothing will happen if we do not call the functions we just created – updateBillUI() and tipPercentValue(). Before you look where to put them I want you to think about where you would call each one. In fact I want you to try to figure it out first before you look at the following code.

Figure 2.3.40 Screenshot 2016-10-23 22.56.38.png

Run the app and you should be able to enter the bill amount and change the tip percent.

Figure 2.3.41

Screenshot 2016-10-23 23.03.53.png


Wrapping up

Wow! Look how far you have come. You just built your first real app that puts into practice the concept of MVC or Model View Controller. The Model is where you store your data, the View is what displays th data to the user and the controller communicates between the Model and the View. Make sure to complete the following exercise to solidify what you have learned.

Exercise

Isn't there another part of the app that splits the bill dependent upon how many people there are? You are exactly right! I am leaving that up to you. Using everything you learned thus far I am %100 confident you will be able to finish on your own. Once you finish you will have an app you can use every time you go to a restaurant.

Once you have finished that its time to move on to some really cool stuff. Have fun and never give up!