DISCLAIMER This tutorial is intended for people who are just starting out with iOS programming. It doesn’t assume any familiarity with Xcode and only assumes basic knowledge of Swift programming and OOP.
In this tutorial you’re going to make a tip calculator iOS application.
First Steps
To follow along with this tutorial you’ll need to have Xcode installed. Xcode is an integrated development environment (IDE) containing a suite of software development tools developed by Apple for developing software for OS X and iOS. You can download Xcode here.
To get started open Xcode. By default Xcode displays a new window with recent projects and various options to create new projects. Click the “Create a new Xcode project” button to create your first Project. Alternatively you can click File -> New -> Project
(⇧⌘N
) in the menu.
Xcode will present you with a set of templates to start your project off.
Go ahead and select the simplest of the provided templates, the Single View Application
and hit Next
. As the name implies this will create an application with a single screen that is initially empty.
Next off, Xcode will give you a set of options to configure your new project.
Enter “Tip Calculator” as the Product Name
and make sure to set the Language
to Swift
and Devices
to iPhone
.
The Organization Name
and Organization Identifier
are used when you want to share your app on the AppStore or run your app on an actual iOS device.
Organization Name
should be your name or the name of your company.
Organization Identifier
are usually in the format “com.name” and are used together with your app name to uniquely identify your app.
Don’t worry about the Use Core Data
, Include Unit Tests
and Include UI Tests
options for now. You’ll get a chance to use all of them in your iOS developer career :-).
Hit the next button and Xcode will ask you where to save your project. After you have selected a destination, you’ll be greeted to the screen below.
Getting to know Xcode
There are 3 areas of interest in Xcode:
The Content Area
, located in the middle that shows you the currently open file or resource.
The Navigator
, located on the left, currently shows you a list of files in your project. Among other things, you use the navigator to open certain files in your project.
Clicking on a file will open it in the content area. Notice that files can be grouped together in folders, these are calledgroups
. Unfortunately groups
in Xcode do not directly correspond to folders in the file system.
To hide/show the navigator either press the “hide/show navigator button” in the top right or use the keyboard shortcut⌘0
.
The Utilities Area
, located on the right, shows information about the currently open file. Items in the utility area appear based on the file you’re editing. Most often you’ll use it when editing user interfaces.
To hide/show the utilities area
either press the “hide/show utilities button” in the top right or use the keyboard shortcut ⌘⌥0
.
Now that you’re slightly familiar with Xcode’s interface, let’s get to work building the app!
Getting to know Interface Builder
Open the Main.storyboard
file from the project navigator. Storyboards
are the files that contain your application’s user interface (UI). Storyboards allow you to visually lay out your app , in some sense they’re similar to tools like Photoshop or Illustrator but intended for the sole purpose of building application interfaces. The tool that you use to edit storyboards in Xcode is called Interface Builder (IB)
.
Let’s look at some of the components of interface builder.
1 . The Content Area
located in the middle is where you can see the UI you’re currently working on, it’s currently empty.
2 . The Document Outline
on the left shows you a list of all the components that are part of the storyboard. Clicking on an item in the document outline will select it. Alternatively you can click on the item in the content area.
Currently there’s only 1 ViewController
in the storyboard, for now you can think of a ViewController
as being a screen in the app.
To hide / show the Document Outline
press the hide/show document outline
button in the bottom right corner of the content area.
3 . The Attributes Inspector
in the top part of the Utilities
panel on the right. Here you can edit properties of the currently selected item. For example try to change the background color to red.
4 . The Components Library
in the bottom part of the Utilities
panel. Here you have a list of components that you will use to build your user interface. Try dragging some items into the content area
just to get a feeling on how this works.
Perhaps you noticed that the currently displayed view is square, that doesn’t look like any iOS device that currently exists. This is because by default Interface Builder
wants you to design your views so they can adapt to different sizes across all different device screens. You can find out more about designing interfaces for different screens here.
For this tutorial we’ll keep things simple and build the UI for exactly one device.
Go ahead and select the View Controller
item in the Document Outline
.
In the Attribute Inspector
expand the Size
dropdown, and select one of the iPhone sizes. If you want to run the application on your own iPhone than you should select the appropriate size based on the device you have:
3.5inch – iPhone 4S 4 inch – iPhone 5 / 5S 4.7 inch – iPhone 6 / 6S 5.5 inch – iPhone 6+ / 6S+
For the purpose of this tutorial I’ll be working with the 4 inch iPhone.
After you’ve selected the appropriate size your interface will look similar to this:
Creating the Interface
Let’s start building the interface. First you’ll have to add some labels
. Labels
are read only components with static text used to identify different parts of the user interface.
Find the label
component either by scrolling through the Component Library
or by searching for “label” in the search field at the bottom.
Now drag a label
to the top middle part of your view controller. Notice that Interface Builder
provides helpful blue guidelines that help you center your label
and add some margin at the top.
Now, change the text of your label
to “Tip Calculator”, you can do this either by double clicking the label
or by changing the value in the Attributes Inspector
under Text
.
Notice that the label
is no longer centered, you’ll have to recenter it, but first lets make the text bigger. Increase the font to 25 points. This can be done via the stepper next to Font
in the Attributes Inspector
.
Next notice that the label
has become cut off once you increased the text size.
You could manually adjust the size of the label
, but a better solution is to use the shortcut ⌘=
. This runs the command Editor -> Size to Fit Content
.
Now drag the label
to center it, remember to use the blue guidelines to help you out.
Next add a label
below the title label
, on the left edge of the screen and change it’s title to “Cost:”.
You’ll want a text field
next to the cost label
, a text field
is an component that displays editable text. Tapping on a text field
will allow you to edit the text in it when you run your app.
Resize the the text field
until you hit the right margin of the screen.
Next in the Attributes Inspector
change the textfield Placeholder
property to “Cost of your meal”. The placeholder is displayed when the textfield has no text, offering the user some hints on what they should type in the text field
.
Your view will look like this now:
Next add another label
, aligned to the left below the cost label
. Set this label's
text to “Tip Percent”.
Add another label
below the “Tip Percent” label
. This label
will display the actual percent that you want the tip to be. Be generous and set it’s text to “20”.
Let’s make the number stand out a bit. Make the text bold by editing the label’s Font
property in the Attribute Inspector
. Click the “T” button to the right of “System 17.0” and from the Style
dropdown select Bold
.
Again the text becomes cut off. A problem you’ll encounter later is that different texts with the same number of characters can have different sizes. For example “11” is shorter than “44”. You should make the label
wider to accommodate different texts. Remember to keep it centered.
Also we’ll want the text in the label to be centered rather than being aligned to the left. From the attribute inspector select the Centered
button to the right of Alignment
.
Next you’ll add a slider
to the UI. A slider is used to select a value from a possible range of values. Go ahead and drag a slider
to the right of the “Tip Percent” label
. Adjust it’s width until you hit the right margin and place it vertically between the “Tip Percent” and “20” labels
.
By default a slider
has a value between 0 and 1. But for our app we’ll want to use the a value between 0
and 100
. Go ahead and edit the Minimum
, Maximum
and Current
values in the Attributes Inspector
, make sure to set theCurrent
value to 20
so that it’s synced up with the “20” label
. The slider's
UI will update whenever you set one of these attributes.
Finally we’ll want to add some UI that displays the final cost of a meal. Add a label
titled “Final Cost:” below the “Tip Percent” UI.
To the right of the “Final Cost” label
add an empty label
. This label
will be filled programmatically based on the cost of the meal and the tip percent.
Let’s make a final aesthetic change to the design. We’ll change all our labels
to a lighter shade of black. Go ahead and select the “Cost:”, “Tip Percent” and “Final Cost:” labels
. You can simultaneously edit multiple objects that have the same properties. From the Attributes Inspector
expand the Color
dropdown and choose Dark Gray Color
.
You can run the app right now using the iOS Simulator
. First you’ll have to select the device you want to run the app on from the iOS Device
dropdown. If you followed along exactly with this tutorial you should select iPhone 5
.
After selecting the device just press the “Run” button in the top left corner or use the shortcut ⌘R
.
The iOS Simulator
application will start and display the screen of your app. Notice that you can drag the slider and enter text in the text field
. The app isn’t functional of course, you haven’t written any code yet. We’ll fix that up next!
The iOS Simulator
enables you to simulate different iOS and Apple Watch devices and several versions of the iOS operating system. Essentially the iOS simulator let’s you test your app without using an actual iOS device.
Some essential iOS Simulator shortcuts:
⌘⇧H
– Equivalent of pressing the home button on a device
⌘→
and ⌘←
– Rotate the device clockwise / counter-clockwise
⌘K
– Show / Hide the keyboard
Making Connections
Now you might wonder, “laying out my design in interface builder is all fine and dandy but how do I actually make all of this work ?”.
You’ll need a way to access your UI components from code, for example you’ll want to set the text of the “Final Cost”label
programmatically. Also you’ll need a way to detect when a UI component has changed, i.e. the text field's
text has changed or the slider
has moved.
UI Components are accessed in code via IBOutlets
.
Actions on those components are detected via IBActions
.
Let’s look at some ways in which we can hook up the UI.
Each screen in an app has an associated View Controller
. Currently there is only 1 View Controller
named “ViewController”. Each View Controller
has an associated Swift class. For our View Controller
this is the classViewController.swift
. The code that accesses UI elements and is responsible for the app logic will go in this file.
Go ahead an open the ViewController.swift
file. You will see something like this.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Let’s add our text field
as an IBOutlet
.
At the top of the ViewController
class add the line @IBOutlet var costTextField: UITextField!
. Let’s discuss in detail what this does.
IBOutlets
are properties on the ViewController
class that are marked with a special keyword.
All IBOutlets
have to be prefixed with the @IBOutlet
keyword. This is used to mark them as IBOutlets
and make them visible to Interface Builder
.
The type of an IBOutlet
depends on the type component you want to add. For the text field
the type isUITextField
. All UI components share the UI
prefix in their class name.
You should also notice the !
after UITextField
. This marks the property as a force unwrapped optional. This lets us access the text field without using Optional Chaining. It’s typical for IBOutlets to be forced unwrapped optionals.
Naming outlets Another thing to note is the name we gave to our outlet:
costTextField
. We could have called it whatever valid name we wanted:x
,cost
,csttf
, etc. But generally you’ll want to give your outlets and variables informative names so that you can easily tell what you’re dealing with. A good naming convention is to add a suffix to the outlet that indicates it’s type. i.e.costTextField
,nameLabel
,submitButton
etc. This is known as Hungarian Notation.
class ViewController: UIViewController {
@IBOutlet var costTextField: UITextField!
...
}
Next we’ll have to hook up the IBOutlet
in Interface Builder
. Open the Main.storyboard
file and control(⌃
) click onView Controller
in the Document Outline
. A popup will appear listing various properties of the View Controller
, this popup is used to connect IBOutlets
.
Notice that your costTextField
appear at the top of the Outlets
section.
To connect the outlet you have to drag the small circle on the right of costTextField
to the text field
in the UI.
The costTextField
outlet will change it’s background color to a lighter gray after you’ve done this.
Now we can access the text field
in the UI from code! Let’s change the text programmatically. You’ll add code in theViewController's
viewDidLoad()
method. This method is called when the ViewController's
view is loaded in memory. Essentially you can’t access UI elements before viewDidLoad
is called.
You can access a text field's
text via the text
property of type String
. Go ahead and modify the text in theviewDidLoad
method. Make sure to include your code after the super.viewDidLoad()
call.
class ViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
self.costTextField.text = "Yey! We're finally writing code"
}
...
}
If you run the app right now you’ll see that the text field's
text has changed to the provided code.
IBOutlets
are only the first half of the connections you can make via Interface Builder. The other half are IBActions
, these are used to inform you when certain UI
events are triggered in your app. Examples: the value of a textfield
changed, the value of slider
changed, a button
was pressed. etc.
Let’s create an IBAction
that informs us when the value of our slider has changed.
Open the ViewController.swift
file and add the following method above viewDidLoad
.
class ViewController {
...
@IBAction func tipSliderChanged(sender: UISlider) {
}
...
}
All IBActions
are methods prefixed by the keyword @IBAction
. IBActions
don’t return any value and always take a single parameter, the UI element that has triggered the action.
Naming Actions As with outlets it’s a good practice to give informative names to your
IBActions
and methods in general. A good naming convention is to use<<COMPONENT NAME>><<ACTION>>
or<<ACTION>><<COMPONENT NAME>>
ex:tipSliderChanged
,submitButtonWasPressed
,didTapSubmitButton
,changedTipSlider
etc.
Let’s connect an IBAction
. Open the Main.storyboard
file again. control(⌃
) click on View Controller
just like you did when adding an outlet. Now, notice the bottom section Received Action
here you can see your tipSliderChanged:
action.
To connect an action drag from the action to the slider
in the UI. A popup will appear.
Select the Value Changed
action from the popup. This will cause your tipSliderChanged
method to be called whenever the value of the slider has changed. i.e. when you drag the slider around.
The popup will update to show you that you’ve successfully connected the action.
Next let’s write some simple code that sets the costTextField's
text to the value of the slider. You access the value of an UISlider
via it’s value
property of type Float
. Go ahead and set the costTextField's
text to the slider’s value.
class ViewController {
...
@IBAction func tipSliderChanged(sender: UISlider) {
self.costTextField.text = "\(sender.value)"
}
...
}
When you run the app now you can see that the cost text field is modified whenever you drag the slider
.
More Connections
Let’s look at another way of adding connections. This method is generally faster but you’ll still have to use the previous method when modifying connections.
To get started open the Assistant Editor
by clicking the Show Assistant Editor
on the top right or using the shortcut⌥⌘↵
. The Assistant Editor
allows you to view 2 files side by side. When opening the Assistant Editor
from a storyboard it will show you the corresponding swift
file for the selected view controller.
Next you’ll have to drag UI components
from the Storyboard
to the source code on the right. Hold control(⌃
) and drag the label
to the right of “Final Cost:” below the costTextField
property.
Now a dialog will show up that will ask you for details on the connection you want to make. Set the name of the newlabel
to finalCostLabel
. Hit the Connect
button and Xcode will automatically generate the code needed to connect the label
.
Now do the same for the “20” label, name it tipPercentLabel
.
Next we’ll look at connecting IBActions
in a similar way. Let’s connect an action that tells us when the text in the cost text field changes. Control(⌃
) click on the textfield. A popup will appear listing all the possible events.
Next drag from the circle to the right of Editing Changed
to the line above the viewDidLoad
method.
A dialog will appear asking for details on the action you want to add. Set the Name
of the action tocostTextFieldChanged
and the Type
to UITextField
, generally it’s a good to set the type of an action to the actual type of the object involved instead of using the default AnyObject
.
Your code should look like this now.
import UIKit
class ViewController: UIViewController {
@IBOutlet var costTextField: UITextField!
@IBOutlet weak var finalCostLabel: UILabel!
@IBOutlet weak var tipPercentLabel: UILabel!
@IBAction func tipSliderChanged(sender: UISlider) {
self.costTextField.text = "\(sender.value)"
}
@IBAction func costTextFieldChanged(sender: UITextField) {
}
override func viewDidLoad() {
super.viewDidLoad()
self.costTextField.text = "Yey! We're finally writing code"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You’re done with hooking up the connections. Now, on to coding.
Coding
Setting up the UI
and connecting outlets is only the first part of building an iOS app. The interesting part lies in coding the various interactions and implementing the app’s logic.
To get started you’ll need a way to keep track of the data in the app. Let’s think about what data we need. We’ll need to keep track of the currently inputed cost of a meal and of the tip percent. We’ll store this data in 2 Float
variables. Declare these variables at the top of the ViewController
class below all the outlet declarations.
Models The data in our app is called the
Domain Model
or simplyModel
. Notice that this data is separate from ourUI
, it will be used to populate theUI
but it exists independently of it.
class ViewController {
...
var mealCost: Float = 0.0
var tipPercent: Float = 20.0
...
}
Next let’s write a function called finalCost
that computes the final cost of our meal. Code that operates on the model processing it or producing new data is called Buisness Logic
. Notice that the finalCost
function exists independently of the UI
of our app.
class ViewController {
...
func finalCost() -> Float {
return mealCost + tipPercent / 100.0 * mealCost
}
...
}
Next let’s hook up our UI
to our model. We’ll want to update the model whenever the UI
changes.
We’ll hook up the slider by setting the tipPercent
to the slider’s value. You’ll also want to change thetipPercentLabel's
text to reflect the new value.
class ViewController {
...
@IBAction func tipSliderChanged(sender: UISlider) {
self.tipPercent = sender.value
self.tipPercentLabel.text = "\(self.tipPercent)"
}
...
}
And we’ll hook up the textfield by setting the mealCost
whenever the text field changes. Notice that a UITextField's
text
property is declared as an optional (String?
) so it can potentially be non-existent (nil
). Note that the value nil
is different from the empty string (""
). Also converting a String
to a Float
can fail and return nil
. For example how do you convert the string hello
to a Float
?
We’ll just hand-wave these issues for now. Assume that these calls never fail* and use the !
force unwrap operator to get these values.
*this is a terrible assumption
class ViewController {
...
@IBAction func costTextFieldChanged(sender: UITextField) {
self.mealCost = Float(sender.text!)!
}
...
}
Next we’ll want to update our finalCostLabel
after UI changes. We’ll add the same code to both the tipSliderChanged
and costTextFieldChanged
methods.
class ViewController {
...
@IBAction func tipSliderChanged(sender: UISlider) {
self.tipPercent = sender.value
self.tipPercentLabel.text = "\(self.tipPercent)"
self.finalCostLabel.text = "\(finalCost())"
}
@IBAction func costTextFieldChanged(sender: UITextField) {
self.mealCost = Float(sender.text!)!
self.finalCostLabel.text = "\(finalCost())"
}
...
}
And we’re done :). Tutorial’s over, time to make some awesome apps! … or are we?
Debugging
The app is functional right now. But it’s still rough around the edges.
Here are the current issues: 1. The label under “Tip Percent” is not updated correctly, it shows “…” 2. The app displays the default iOS Keyboard making it difficult to input numbers 3. The app crashes when the textField is empty or invalid
Let’s fix each of these issues:
1. The label under “Tip Percent” is not updated correctly, it shows “…”
This happens because the slider
returns decimal numbers as it’s value
. So the value could be something like20.03456221
. The label doesn’t have enough space to display that text so it displays “…” instead. To fix this we’ll round our tipPercent
to the nearest integer. Also when setting the tipPercentLabel's
text we’ll want to cast tipPercent
to an Int
so that we get a text without a decimal dot. (20 vs 20.0)
class ViewController {
...
@IBAction func tipSliderChanged(sender: UISlider) {
self.tipPercent = round(sender.value)
self.tipPercentLabel.text = "\(Int(self.tipPercent))"
...
}
...
}
2. The app displays the default iOS Keyboard making it difficult to input numbers
To fix this we’ll change the text field’s keyboard type from interface builder.
Open the Main.storyboard
, select the cost text field, in the Attributes Inspector
expand the dropdown next toKeyboard Type
and select Decimal Pad
.
Looking better. But the app still crashes when the text field is empty. Also restricting the input to numbers and punctuation doesn’t get rid of possible invalid input: you can still write text like 1.2.3
that will cause the app to crash.
3. The app crashes when the textField is empty or invalid
We’ll fix by making the mealCost
an optional variable. If mealCost
is nil
then the data is invalid and we’ll handle it appropriately.
Go ahead and change the type of mealCost
from Float
to Float?
.
class ViewController {
...
var mealCost: Float?
...
}
Now that mealCost
is an optional we’ll have to modify some other code. The finalCost
function has to be modified to unwrap the optional. We’ll assume that the function is only called when mealCost
has a non nil value.
class ViewController {
...
func finalCost() -> Float {
return mealCost! + tipPercent / 100.0 * mealCost!
}
...
}
Next you have to modify both the tipSliderChanged
and costTextFieldChanged
functions. If mealCost
is nil
then thefinalCostLabel's
text should be set to the empty string(""
).
class ViewController {
...
@IBAction func tipSliderChanged(sender: UISlider) {
self.tipPercent = round(sender.value)
self.tipPercentLabel.text = "\(Int(self.tipPercent))"
if mealCost != nil {
self.finalCostLabel.text = "\(finalCost())"
} else {
self.finalCostLabel.text = ""
}
}
@IBAction func costTextFieldChanged(sender: UITextField) {
self.mealCost = Float(sender.text!)
if mealCost != nil {
self.finalCostLabel.text = "\(finalCost())"
} else {
self.finalCostLabel.text = ""
}
}
...
}
Duplicate Code
Notice the snippet of code:
if mealCost != nil {
self.finalCostLabel.text = "\(finalCost())"
} else {
self.finalCostLabel.text = ""
}
This snippet appears twice in our code, once in tipSliderChanged
and once in costTextFieldChanged
. This is calledduplicate code and is considered a bad practice when writing code. What if we wanted to modify this code to display “Invalid Data” whenever mealCost
is nil. We’d have to modify the code in 2 places.
We’ll extract the snippet into a new method to make the design of our code better. Restructuring code to improve its design while maintaining the same functionality is known as Refactoring.
class ViewController {
...
func updateFinalCostLabel() {
if mealCost != nil {
self.finalCostLabel.text = "\(finalCost())"
} else {
self.finalCostLabel.text = ""
}
}
...
}
Now the updateFinalCost
method will be called from both tipSliderChanged
and costTextFieldChanged
. If we want to change how the finalCostLabel
is updated we only have to make those changes once.
class ViewController {
...
@IBAction func tipSliderChanged(sender: UISlider) {
self.tipPercent = round(sender.value)
self.tipPercentLabel.text = "\(Int(self.tipPercent))"
updateFinalCostLabel()
}
@IBAction func costTextFieldChanged(sender: UITextField) {
self.mealCost = Float(sender.text!)
updateFinalCostLabel()
}
...
}
Let’s make the app display “Invalid Cost!” if the mealCost
is nil and the costTextField
is empty.
class ViewController {
...
func updateFinalCostLabel() {
if mealCost != nil {
self.finalCostLabel.text = "\(finalCost())"
} else if (self.costTextField.text!.isEmpty) {
self.finalCostLabel.text = ""
} else {
self.finalCostLabel.text = "Invalid Cost!"
}
}
...
}
Model View Controller
We’re done building the app! Now let’s discuss the structure of the app a bit further. All iOS apps share a fundamental architecture on which they are built. This Design Pattern / Architectural Pattern is called Model View Controller (MVC).
In MVC
all objects fit into one of three roles: Model, View or Controller.
Views
View objects are visible to the user. They are designed to show information to the user and get his input. These include the UI components that we used throughout this tutorial. Examples of view objects we used are: labels
, text fields
and sliders
.
Models
Model objects hold data and are unaware of the user interface. The app we built has a very simple model
: 2 Floats,mealCost
and tipPercent
. Model objects usually model data from the real world. Typically an application’s model layer consists of classes that encapsulate various related bits of data together. Let’s say you’re building an restaurant rating app, there you’ll most likely have a model object of type Restaurant
that has properties like: name
, address
,phoneNumber
, ratings
and others.
Controllers
Controller objects are the intermediaries between views and models. They update the view using data from the model and make sure that the data between the view and model stay in sync. Controller objects are usually the place where you handle user input and set up various view properties.
You can download the full source code of the app below:
This is a great tutorial. I shared it on Twitter and people liked it.
Thanks much and keep it up. I appreciate all the great info that you guys are putting together for us.
Thanks;
Gabriel
Start to finish this is an awesome tutorial for beginners. You have a great way of slowly introducing different concepts of the Xcode world along with the swift language. Fantastic work!
Very informative blog, thanks for share this, theses codes are working…
I love it! Brilliant tutorial! Thank you.
Thank you