Creating A Simple iOS Game With Swift In Xcode

Written by: Reinder de Vries, November 8 2017, in App Development, App Projects

Creating A Simple iOS Game With Swift In Xcode

Updated for Xcode 9 and Swift 4.0

Aw yiss! It’s app makin’ time. Fire up your Xcode, get your Swift hat on and get hacking with this game called Add-1.

In this tutorial, you’ll learn how to create a game for iOS with Swift. We’ll dive in Xcode, variables, working with input and output, optionals, using Interface Builder, all you need to get started with iPhone app coding.

Let’s get started!

What Is “Add 1”?

Creating A Simple iOS Game With Swift In Xcode

The game we’ll create is called “Add 1”, and as far as I know it’s an invention of Daniel Kahneman. He’s the author of “Thinking Fast and Slow”, and used the game as an assignment in his research of cognitive strain.

The game is perfect for creating a small and simple app with enough functionality to make it a worthwhile learning experience. By the end of this mini course, you’ll know how to make a simple app and you’ll be able to challenge your friends to see who can make the most add-1’s in 60 seconds.

Prerequisites For This Course

There’s not much to setup before you can follow this mini course, but you at least need:

  1. A Mac
  2. With Xcode 8 installed
  3. An hour or so of time in total
  4. No programming experience required!

Project Template, Code And Assets

Download the Xcode project and assets with these links:

Unzip, and save in a convenient folder.

This project makes use of the following free assets:

Get 5 of my best practices

Get 5 of my best practices

Learn how to build better iOS apps

I’ll show you exactly how I built a dozen professional iOS apps,
write extensible Swift code, and turn coffee into code.
Wait, what? Yup – into Swift code.

Creating The App Project

To get started, set up Xcode for this project:

  1. Start Xcode
  2. Create a new project with File -> New -> Project... or Command-Shift-N.
  3. Pick Single-View Application
  4. Choose for Product Name: Add1
  5. Choose for Organization Name: LearnAppMaking
  6. Organization Identifier: com.learnappmaking
  7. Language: Swift, Devices: Universal or iPhone
  8. Click Next and pick a project location
  9. Click Create

Removing The Default Options

The project type Single-View Application comes with a few options we don’t need for our game, like Storyboards. Let’s remove some of those options to create an easier to work with environment.

  1. Go to the Project Navigator. It’s in the left pane, under the tab with the directory icon.
  2. Click on the big bar up top, where it says “Add 1, 2 targets”. These are the project’s settings.
  3. In the main view, pick “Add 1” under Targets. It’s right under Project.
  4. In the General tab, uncheck Landscape Left and Landscape Right for Device Orientation.
  5. Check Hide status bar
  6. For Main Interface, remove what’s in the selection input box, it must be emtpy.

Then, do this: From the Project Navigator, remove the files Main.storyboard by right-clicking and picking Delete.

Creating The Main View Controller

We’re going to create a new view controller. A view controller is a Swift component that governs what’s on the screen, it’s the code behind the interface. Usually, a view controller consists of a Swift class (based on UIViewController) and a XIB file.

Create a new view controller by doing:

  1. Right-click on the project in Project Navigator. That’s the big bar up top.
  2. Choose New File....
  3. Choose Cocoa Touch Class.
  4. For Class, type in MainViewController.
  5. Make sure it’s a subclass of UIViewController.
  6. Tick the box Also create XIB file.
  7. Make sure it’s for iPhone, and with Swift.
  8. When Xcode asks for a location, choose the default directory or your project’s base directory.

Then, click the file MainViewController.xib from the Project Navigator. Xcode will switch to Interface Builder, the program that’s used to create interfaces. It’s an integral part of Xcode and you’ll use it a lot when creating iOS apps.

Interface Builder is a sort of scaffolding tool, because you can’t create functional interfaces for apps, but you create the basic setup that is needed for the interfaces of your app. Technically, you could create apps entirely from code, without making use of Interface Builder. Using IB will often improve your app making productivity, and it’s a great visual tool for app creation.

Since iOS 7, Interface Builder has a new feature called Auto Layout. It’s a helper tool that allows you to give interface elements constraints.

Say you add a button to your iPhone app UI. What happens when the iPhone is rotated from portrait to landscape orientation? Does your button resize, center, stretch, grow bigger — you set that with Auto Layout. The same goes for making a UI that works well on both iPhone and iPad, with different screen resolutions.

Creating The UI In Interface Builder

We’ll now create the interface for our app. Or better said: we’ll set up the structure. It’s a “dead” interface, it doesn’t do anything yet. Such a foundation is made with Interface Builder.

Go to MainViewController.xib in Interface Builder, by clicking on the file in the Project navigator. Then, have a look at the screenshot below.

This is Interface Builder. On the left you see the list of items you’ve added to the UI of your app. For you, it’s empty of course: we haven’t added anything yet. Whenever you add an interface element, you drag it from the bottom-right list to the middle main part of Interface Builder. You can then change the elements (buttons, labels, pickers, etc.) with the gizmos on the right. These gizmos are called inspectors, and they govern the properties of each of the elements.

The list on the left (called Document Outline) is a hierarchical list of all elements on the screen, and 2 special items: File’s Owner and First Responder. We’ll be using the File’s Owner later on.

We’ll now set up the views and layout of the game. You need the following metrics to properly set everything up — make sure you watch the video!


  • Background Image View
    • Full Size, Aspect Fill
  • Score Image
    • Top Left, 149x50
  • Score Label
    • On Score Image, “1234”, Font: 22, Regular, HVD Comic Serif Pro
  • Time Image
    • Top Right, 149x50
  • Time Label
    • On Time Image, “01:00”, Font: 22, Regular, HVD Comic Serif Pro
  • Number Image
    • Top Center, 302x129
  • Number Label
    • Center at Number Image, “1234”, Font: 70, Regular, HVD Comic Serif Pro, Alignment Center
  • Text Field
    • Center Below Number, input Background, Font: 70, Regular, HVD Comic Serif Pro, Border Style “None”, Keyboard Type “Number Pad”, Alignment Center
  • Explanation Label:
    • Center Bottom, Font: 70, Regular, HVD Comic Serif Pro, 2 Lines, text: “Add 1 to each of the digits. So, 1357 becomes 2468.”


  • White
  • Brown (RGB): 135, 79, 33
  • Blue (RGB): 105, 168, 255

Watch the video to see how to create the interfaces of the app.

Setting Up The AppDelegate

When an app starts, the function application(_:didFinishLaunchingWithOptions:) is called. It sets up the application and gives it a starting point for the first interface. When using Storyboards, you don’t really need this function, because Xcode already knows the first storyboard it should use at app launch. We however don’t use Storyboards, so we need to tell the app what it should show when it’s launched.

First, add this instance variable to the AppDelegate class:

var mainVC:MainViewController?

You write that at the top of the class, so right after the first { of the file. It’s inside the AppDelegate class. We’ve just told the compiler: we want to use a variable called mainVC of type UIViewController (optional) and it should be available in the scope of the entire class. Telling the compiler what variables to use, and which type and which name, is called declaring or declaration.

Then, replace the code inside the function application(_:didFinishLaunchingWithOptions:) , inside the squiggly braces { and }, with this code:

mainVC = MainViewController(nibName: "MainViewController", bundle:nil)

let frame = UIScreen.main.bounds
window = UIWindow(frame: frame)

window!.rootViewController = mainVC

return true

This is what our code does:

  1. Initialize the instance variable mainVC by calling constructor MainViewController() with 2 parameters: “MainViewController” for the nibName and nil for the bundle. We just said: set up variable mainVC and fill it with the contents of our MainViewController.xib file.
  2. Then, create a constant frame with value UIScreen.main.bounds. We store the screen resolution of the app inside constant frame. A constant is a variable with a fixed value, so it never changes. Just like the dimensions of the iPhone screen never changes, because it’s a physical object.
  3. Then, initialize the instance variable window by calling constructor UIWindow() on it. This function takes one parameter: frame, the constant we set up earlier. It’s a named parameter, because you write frame: frame. The first frame is the name of the parameter, the second frame is the variable (a constant in this case) we supply as the parameter.
  4. Then, we set mainVC to be the rootViewController of window. We use the exclamation mark to force unwrap the variable window. The instance variable window is an optional, which means it could be nil. In order to use it as a definitive variable (a variable that is not nil), we need to unwrap it. In this case, we can be certain that it’s not nil because we’ve just initialized it. So, we force it to be a definitive non-optional variable by putting an exclamation mark at the end of the variable name.
  5. Then, we call a function makeKeyAndVisible() (no parameters) on the force unwrapped instance variable window.
  6. Then, we return true. This is required in the function application(_:didFinishLaunchingWithOptions:).

Try It: Run The App!

Alright, let’s run the app. Either:

  1. Click the Play button in the top-left corner of Xcode.
  2. Hit Command-R

When nothing happens, you might have made an error somewhere. Feel free to ask a question in the comments of this post. Also, check if you set the right Build Target (iPhone Simulator). Pick an iPhone model to use in the Simulator by using the dropdown list on the right of the Play button.

When the app runs, you should see your interface pop up on the screen. It doesn’t really do anything though!

If you don’t see anything or encounter an error, check whether you’ve set up the rootViewController the right way and whether you removed the Storyboard from both Xcode and the project settings.

Get 5 of my best practices

Get 5 of my best practices

Learn how to build better iOS apps

I’ll show you exactly how I built a dozen professional iOS apps,
write extensible Swift code, and turn coffee into code.
Wait, what? Yup – into Swift code.

Writing The MainViewController Code

Alright, let’s do some more coding. Switch to MainViewController.swift. Do you notice the structure of the file?

  • First, there’s an import statement. It tells the compiler to use code from UIKit.
  • Then, there’s a class declaration. It tells the compiler: this is the class we’re creating and we want it to extend UIViewController. When developing apps for iOS, everything you do is object-oriented. All functionality of an app resides in one or more classes. This class called MainViewController holds the code for our interface, and it’s connected to the .xib file. And the class extends UIViewController, which means that we’ll make use of the code Apple wrote for the superclass UIViewController and add our own code to it. Our code, the UIViewController superclass, and the MainViewController.xib file make up the single interface of our app.
  • Inside the squiggly brackets, we write the contents of this class.
  • Then inside the class, there’s functions like viewDidLoad(). This function is called when the MainViewController.xib file is loaded into memory. We use it to set up the view.

Writing The First Function “generateRandomNumber()”

Let’s create our first function. It’s called generateRandomNumber and it returns a random number of 4 digits, just what we need for our little game!

This is the entire function:

func generateRandomNumber() -> String
    var result:String = ""

    for _ in 1...4
        var digit:Int = Int(arc4random_uniform(8) + 1)

        result += "\(digit)"

    return result

And go over it line by line:

  1. First, we write func, and then generateRandomNumber, and then (), and then -> String. We declare our function by writing func, and then set its name to generateRandomNumber. Then, we say that this function has no parameters by writing (). If our function had parameters, we would have written (parameter1:String) for example. Then we set the return value of this function, by writing a single arrow -> and writing the return type String. It means that, whatever may happen, this function must return a String.
  2. Then, we declare and initialize a new variable called result with type String and value "" (empty string).
  3. Then, we write a for-loop. Our loop is supposed to repeat itself 4 times, hence the 1...4. The underscore _ means that we don’t want to keep track of how many times this loop has run, inside the loop.
  4. Then, inside the loops squiggly brackets { and } we write the contents of the loop. We declare and initialize a variable called digit of type Int, that has the value of Int(arc4random_uniform(8) + 1). The function arc4random returns a random value from 0 to 7. That’s 0 or 1 or 2 etc. up to 7 (not 8!). Then, we add 1 to it to always have a value from 1 to 8 (0-7 -> 1-8).
  5. Then, we append the string value of digit to result. Do you notice that result is a String while digit is an Int? We can’t add an integer to a string, that’s why we interpolate the Int inside a string. It’s a quick and dirty way to turn just about anything into a String, by using the "\(...)" notation.
  6. In the end, we return result. Like we declared, the function must always return a String value.

Connecting Code With UI: IBOutlet

In order to use the interface elements we created with Interface Builder inside the MainViewController.xib, we must create a connection between the controller class file and the XIB. Such a connection is called an outlet, and the statement to create an outlet goes like this:

@IBOutlet weak var numbersLabel:UILabel?
@IBOutlet weak var scoreLabel:UILabel?
@IBOutlet weak var inputField:UITextField?

This is what happens:

  1. First, we start with @IBOutlet this is a special declaration statement that indicates that the property we’re creating is in fact an outlet. A property is just an instance variable (like we created earlier), except that it’s also available for use outside of a class. For now, let’s leave it at that.
  2. Then, we write weak var. That means that this property is weak, as opposed to strong. Weak means that whenever our controller class is removed from memory, the property can be removed at the same time.
  3. Then, we write the property name and type, ex. numbersLabel of type UILabel.
  4. Finally, we indicate that the property is an optional, so it can be nil, written with the question mark ? at the end of the declaration.

Connecting The Outlet In Interface Builder

Now, go to MainViewController.xib in Interface Builder. Here’s how you connect the property to the outlet.

  1. Click the first UILabel, the one in the middle of the screen that says “1234”.
  2. Hold the Control-key.
  3. Click-and-drag from File’s Owner (Document Outline, under Placeholders) to the UILabel with “1234”. A blue line should be visible.
  4. Release the mouse when at the label, then a black gizmo should appear.
  5. Select the right outlet: numbersLabel.

Repeat the same step for the score and the textfield. Now you’ve made a connection between the code and the interface, for specific elements and properties.

Watch the video to see how to create the outlets.

Coding Functions “updateScoreLabel()” And “setRandomNumberLabel()”

Switch back to the MainViewController.swift class file and add these two functions, right above the generateRandomNumber function we wrote earlier.

func updateScoreLabel()
    scoreLabel?.text = "\(score)"

func setRandomNumberLabel()
    numbersLabel?.text = generateRandomNumber()

What do these two functions do?

  1. updateScoreLabel first checks if the property scoreLabel, connected to the outlet for the label with the score, is nil. It’s an optional property, so it could be empty. The code does that with a technique that’s called optional chaining, denoted by the question mark: ?. When scoreLabel is nil, any code behind the question mark isn’t executed! Then, it sets the text property of the label to "Score: \(score)", which is another string interpolation of the score instance variable.
  2. setRandomNumberLabel sets the numbersLabel with a random number, by first checking whether numbersLabel is not nil (using optional chaining) and then setting numbersLabel!.text to the return value of function generateRandomNumber().

Keeping The Score

Then, create another instance variable just below the outlets you created earlier.

Like this:

var score:Int = 0

See how this is different from a property or an outlet? It’s just a variable that’s declared in the scope of the class, and not in the scope of a function.

Setting Up “viewDidLoad()”

Now, let’s quickly fill the viewDidLoad function with some code. The viewDidLoad function is overridden with the override statement, just before the func statement. Do you see it? It means that the superclass UIViewController of our class MainViewController has a function with the same name, and we overwrite it with new functionality. Whenever this function is called in the app lifecycle, our function is called, and not the function of the superclass. This allows us to customize the behaviour of the controller and the interface, and hooking into the view lifecycle that the UIViewController has.

This is the code for the function viewDidLoad:



inputField?.addTarget(self, action: #selector(textFieldDidChange(textField:)), for:UIControlEvents.editingChanged)

And this is what happens:

  1. First, we call super.viewDidLoad(). This is needed to ensure that the view lifecycle is not interrupted. It’s a call to the viewDidLoad superclass function, and it’s required.
  2. Then, we call setRandomNumberLabel() and updateScoreLabel(). Can you find out what this does? It sets the label in the interface to a random number, and updates the score with the initial value of 0. We set that initial value when declaring the instance variable score, at the top of the class.
  3. Then, we check if optional inputField not is nil using optional chaining. The property inputField is connected to the outlet of the big textfield in the middle of the screen, in our interface. We add a target for an event, that calls an action.

The target, MainViewController, is indicated by self. It’s self-explanatory: self means the currect context, so it means this current class.

The action that’s called is denoted by #selector(textFieldDidChange(textField:)). It’s the name of the function that should be called when the event UIControlEvents.editingChanged happens. We’ll write that function later on.

The event will be fired when the text inside the text field changes, so when you type something in it. For each character you type, it fires an event, and so it executes the function we just gave it.

What Happens When We Input Text?

Take a look at this function, and then add it to your code:

@objc func textFieldDidChange(textField:UITextField)
    if inputField?.text?.characters.count ?? 0 < 4

    if  let numbers_text    = numbersLabel?.text,
        let input_text      = inputField?.text,
        let numbers = Int(numbers_text),
        let input   = Int(input_text)
        print("Comparing: \(input_text) minus \(numbers_text) == \(input - numbers)")

        if(input - numbers == 1111)

            score += 1                

            score -= 1                


Can you figure out what happens here?

  1. First, we’re checking if the number of characters in inputField is smaller than 4. When that’s the case, we return the function: execution halts, so the code below return is not executed when the if-statement is true. (When inputField or inputField.text is nil, the character count is set to 0 thanks to the ?? operator.)
  2. Then, we check whether the inputField.text and numbersLabel.text are nil using optional binding with if let, because they’re optionals. Optional binding statements can be combined together with a comma. Each statement is executed consecutively, and when one of them fails (i.e. the variable involved is nil), execution of the if-statement is stopped and the code continues below the end of the statement. Once the text inside the text field is checked, the code attempts to convert these String variables to Int. If that succeeds, the code inside the if-statement (between the squiggly brackets) is executed.
  3. Inside the if-statement, we compare the input value to the random value. Remember the point of our game? You were supposed to add one to each of the digits. Since we don’t include digits 0 and 9, you can easily figure out that the exercise is correct when the difference between the two values numbers and input is 1111.
  4. If the input is correct, up the score one point by writing score += 1, which means “add 1 to score“. If it’s wrong, subtract one point by writing score -= 1.
  5. Then, update the random number with a new value, update the score label, and empty the textfield value.

And there you have it, a running game! Run the app by hitting Command-R and see if you can play it. Encounter any errors? Ask for help in the comments, and backtrace your steps in case you missed something.

Let’s see where this got us. Run the app, and try out the functionality. If you followed closely, your app should do this:

  • Not remove the input code when 4 characters have been entered
  • Still check the code for validity
  • Allow no more than 4 characters

All thanks to delegation! Lets move on and see how we can make this game nicer.

Get 5 of my best practices

Get 5 of my best practices

Learn how to build better iOS apps

I’ll show you exactly how I built a dozen professional iOS apps,
write extensible Swift code, and turn coffee into code.
Wait, what? Yup – into Swift code.

MBProgressHUD And Cocoapods

Alright, we’re going to use a library for the next part of this tutorial. In order to show a notification to the user, to inform them whether their answer was right or wrong, we’ll use a progress HUD.

It’s one of those little black squares that pop up when you do something on your iPhone, to inform you that stuff is happening.

A very fine framework as been built for such HUDs and it’s called MBProgressHUD. We can download the source code for the framework on GitHub and inspect it ourselves. While GitHub is great for finding and making use of someone elses code, there’s a better alternative for getting that code inside our project. You see, in order to work with third-party code, we need to import it in our project as a library.

And we’re gonna do that with Cocoapods. Cocoapods is a dependency manager, a quick way to import and maintain third-party code that we use in our projects. In order to work with it, we just declare a file, write down the names of the libraries we want to use, and Cocoapods takes care of the rest.

We install Cocoapods on a Mac by going to the Terminal app, and writing this on the command-line (without the $ sign):

$ sudo gem install cocoapods

After a while (it’s downloading), we should see this:

Successfully installed cocoapods-0.35.0
Parsing documentation for cocoapods-0.35.0
1 gem installed

Now, get back to Xcode and create a new empty file. You do that by right-clicking on the project in the Project navigator on the left, then choosing New file... and picking the Empty file type from the iOS -> Other category. Create the file in the root (topmost) directory of the project. Name the file Podfile, without extension.

Paste the following text in the new file:

source ''
platform :ios, '9.0'

target "Add 1" do
    pod 'MBProgressHUD', '1.0.0'

Then, go back to Terminal and cd into the directory of your app. If you don’t know how to do that, just do this:

  1. Write cd (with an ending space) on the command-line.
  2. Open Finder, and go to the root directory of your project.
  3. Drag the project folder onto the Terminal window. It should write out the directory path on the command-line.
  4. Hit Enter.

Now, when in the right directory, type this in Terminal:

$ pod install

In case you get a No such file or directory error, run this command before running pod install:

$ pod setup

Then, an important step:

  1. Close Xcode.
  2. Go in Finder to your projects root directory.
  3. Click on the Add1.xcworkspace (or any other .xcworkspace project) file.

Cocoapods adds a new project to our project, so we can’t simply work with our old project anymore. We need to work within the Workspace, that contains both projects. Cocoapods has added our dependent library (MBProgressHUD).

Watch the video to see how to install CocoaPods and work with Terminal.

Adding A Bridging Header For Objective-C

The MBProgressHUD library is written in Objective-C, and that’s not immediately compatible with Swift code. Fortunately, we can create a so-called bridging header to use the library from within our Swift code.

Creating a Bridging Header goes like this:

  1. Creata a new file in the Add1 project, by right-clicking on the project and choosing New file....
  2. Then, pick Header file from the iOS -> Source category.
  3. Name the file Add1-Bridging-Header.h and save it in the project root directory.

Then, do this:

  1. Click on the project in the Project navigator.
  2. Up top, click on the Build Settings tab. (Don’t see it? Make sure Add1 is selected, under Targets on the left.)
  3. In the search box (top right), type in bridging. The list below should filter, and show a field called Objective-C Bridging Header.
  4. Double-click on the empty field contents (the column right of the bold-typed Objective-C Bridging Header).
  5. Input in the field: Add1-Bridging-Header.h.

Finally, edit the Add1-Bridging-Header.h file and write the following line in it:

#import <MBProgressHUD/MBProgressHUD.h>

Check whether you’ve implemented the Bridging Header right, by building the project with Command-B. If all goes well, you don’t see any errors.

What did we just do?

Swift and Objective-C can be integrated with one another by creating a connection, a Bridging Header. Both languages work with header files, a certain type of file that describes the functions of a class to the compiler. But, both Swift and Objective-C have different syntax for describing classes and functions. A Bridging Header mediates between the two, it translates from one language to the other by creating a “bridge”.

Importing Image Assets

Alright, lets integrate MBProgressHUD with our project. We’ll use it to show the user a thumbs up, or thumbs down, when they’ve answered a question, and hide it after 3 seconds.

We’ll use 2 different files:

  1. Image file [email protected]
  2. Image file [email protected]

Import like this:

  1. In Xcode, click on the Images.xcassets file in Project navigator. The asset library should open now.
  2. From Finder, drag the 2 files to the left part of the asset library.

Two items now should be created, thumbs-down and thumbs-up.

Watch the video to see how to work with image assets.

Using MBProgressHUD

We’ll now incorporate MBProgressHUD in our code.

First, add the following line to the top of MainViewController.swift right below import UIKit:

import MBProgressHUD

Then, add an MBProgessHUD instance variable to the MainViewController class, by adding this file to the top of the class, just under the score variable declaration.

var hud:MBProgressHUD?

Note that it is an optional!

Then, add in viewDidLoad the following code, just under super.viewDidLoad().

hud = MBProgressHUD(view:self.view)

if(hud != nil)

What does this code do? It’s this:

  1. Initialize an instance of MBProgressHUD and assign it to instance variable hud. The constructor MBProgressHUD() has one named parameter: view. We put self.view in there. It tells the new instance: create yourself, and you’ll be added to self.view later! The HUD now knows where it is. The variable self.view (or: property view on variable self) is a reference to the current view.
  2. If hud is not empty, add it as a subview to the current view. Notice the exclamation mark? We can force unwrap the optional hud, because we verified that it is not nil.

Then, add this function to the MainViewController class, right under the closing squiggly bracket } of viewDidLoad.

func showHUDWithAnswer(isRight:Bool)
    var imageView:UIImageView?

    if isRight
        imageView = UIImageView(image: UIImage(named:"thumbs-up"))
        imageView = UIImageView(image: UIImage(named:"thumbs-down"))

    if(imageView != nil)
        hud?.mode = MBProgressHUDMode.customView
        hud?.customView = imageView

        hud?.show(animated: true)

        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            self.hud?.hide(animated: true)
            self.inputField?.text = ""

Whoah! That’s an entirely new function!

What does it do?

First, these are the characteristics of the function. Can you spot:

  1. That it’s name is showHUDWithAnswer?
  2. That it has one unnamed parameter: isRight?
  3. That the type of that parameter is boolean: Bool, so it only can be true or false?

So, we can deduct that this function must show the HUD, considering the correctness of the answer. Right?

Then, from top to bottom, this is what happens inside the function:

  1. First, we create an empty variable called imageView of type UIImageView?. It doesn’t have a value, because we don’t initialize it. Initialization happens according to function parameter isRight: when the answer is right, we load up an image view containing the “thumbs-up” image, if it’s wrong, the image will show “thumbs-down”.
  2. Then, if hudImageView is not nil (remember, it could be nil because the images didn’t load!) the HUD is configured and put on screen.
    • First, we assign the property mode the value of MBProgressHUDMode.CustomView. That’s just a configuration code, like saying it’s type 1 or 2 or 3, but then with code.
    • Second, we assign our image view to the customView property. It will override MBProgressHUDs standard image with our image view, of either the “right” image, or the “wrong” image.
    • Third, we show the HUD by calling function show(). The parameter true means that the showing will be animated.
    • Finally, we instruct the HUD to hide after 3 seconds with the DispatchQueue.main.asyncAfter(... code.

That last function is kinda special! In short, it makes use of Grand Central Dispatch. It takes 3 parameters: the execution time, the dispatch queue, and a closure with the actual code. It executes the code inside the closure with a delay.

Finally, we need to put the new function to use.

In the textFieldDidChange: function, add the following two codes somewhere:

showHUDWithAnswer(isRight: true)


showHUDWithAnswer(isRight: false)

Where? Well, the first one is a response to a right answer, and the second to a wrong answer.

Let’s add them inside the if-statement, right under score += 1 and score -= 1. Can you figure out which one you need to add where?

See if what you just made is working! You should be able to enter a number, and then see the HUD briefly appear with either a thumbs up or down.

Well done!

Adding A Timer

A game is not a game if there’s no time pressure. Let’s add a timer to our game! These are its “rules”:

  1. The timer counts down from 1 minute to 0 seconds.
  2. When it reaches zero, the game stops.
  3. The timer has to start once the first answer is given.

Of course, the goal of the game is to get as many points as possible before the timer runs out.

Coding The Timer

First, let’s create 2 new instance variables at the top of MainViewController.swift. Open up Xcode if you have not done so already, and locate the file. Add these two lines at the top of the class, just below the declaration of hud.

var timer:Timer?
var seconds:Int = 60

First, you create an object that is called timer and is of type Timer. It’s a complex timer that allows you to schedule tasks, execution of code, within an interval of time. Perfect for counting down from 1 minute to zero. Note that the variable is an optional.

Second, you create a simple variable called seconds of type Int and directly set it to 60, or 1 minute in seconds.

Now, let’s set up the timer itself. We want it to start counting once the first answer is given. The user can start the game at their own pace, but once they’ve started, the clock starts ticking.

Locate the textFieldDidChange: function of our class, and input the following code at the very end, just below updateScoreLabel(), but before the squiggly closing bracket.

if(timer == nil)
    timer = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector:#selector(onUpdateTimer), userInfo:nil, repeats:true)

Alright! Note the if-statement — what does it do? It checks whether timer is nil, whether it’s empty and not initialized. The code inside the if-block, sets the timer. If we wouldn’t use the if-statement, the timer would reset itself each time a game answer is given! We only want to set it once, and then let it run out. Later on, we set the timer to nil when it reaches zero. So, a perfect loop is made: create the timer if it is nil, let it run until zero, set it to nil, and restart.

The inside of the if-block does this:

  1. Assign to timer with the =-sign.
  2. Call class function scheduledTimer(timeInterval:target:selector:userInfo:repeats:), which returns a timer instance that is assigned to timer.
  3. First parameter: 1 second, the interval of the timer, so it calls once every second.
  4. Second and third parameter: the target and the selector. It indicates a function on the target that should be run when the timer fires (once every second). So, when the timer fires, execute function onUpdateTimer of self, and self is our current class MainViewController. We’ll create that function later.
  5. Fourth and fifth: userInfo allows us to give the timer some extra data, but we won’t use it, so it’s set to nil. Then, repeats is set to true: we want to repeat the timer until we reset it.

Coding The Firing Of The Timer

Now, add this function to the class:

@objc func onUpdateTimer() -> Void
    if(seconds > 0 && seconds <= 60)
        seconds -= 1

    else if(seconds == 0)
        if(timer != nil)
            timer = nil

Can you figure out what the code is for the following features?

  1. When the second variable is between 0 and 60 (60 included), decrease it with 1 step and execute updateTimeLabel().
  2. When second is zero (and point 1 is false, i.e. not between 0 and 60) and timer not is nil, invalidate the timer and erase it by setting it to nil.

Got it?

We either downstep the timer, or reset it.

Creating Another Outlet And Writing The Seconds Left

Now, create a new outlet. Do you remember how?

First, add a property to MainViewController like this:

@IBOutlet weak var timeLabel:UILabel?

We designate it to be an outlet by using the @IBOutlet statement. We now can connect to it with Interface Builder. Switch to the XIB file and Control-draw a line from File’s Owner to the time label top-right. Remember: creating an outlet means you connect an interface element from IB to your code, by first writing the outlet declaration in a class, and then drawing a line in Interface Builder.

You might want to change the timeLabel text to 01:00 in Interface Builder.

Alright, next function! Add the function below to class MainViewController, just like before.

func updateTimeLabel()
    if(timeLabel != nil)
        let min:Int = (seconds / 60) % 60
        let sec:Int = seconds % 60

        let min_p:String = String(format: "%02d", min)
        let sec_p:String = String(format: "%02d", sec)

        timeLabel!.text = "\(min_p):\(sec_p)"

What happens here? Well, we want to update the time label with a 00:00 minute-second format. First, we only attempt to set the text on timeLabel when the outlet exists and is not nil.

Then, we create 4 constants. let declares constants, var declares variables, and a constant is just a variable that never changes during the course of our program. Constants are bound to a scope, just like any other variable, so they remain constant during the course of the function updateTimeLabel.

  1. min, type Int, cleverly extracts the number of minutes from an amount of seconds. See, 60 seconds is 1 minute, or 01:00 and not 00:60. How do we write that? First, we divide seconds by 60, and then take the remainder of the result (with the modulo sign, %).
  2. sec, type Int, simply takes the remainder of seconds divided by 60 — the number of seconds that remain, once all the minutes are taken “out”.
  3. min_p and sec_p, type String, are a formatting of min and sec into zero-padded strings. The format indicator %02d means: always write me with two digits, and when there’s initially only one digit, prefix me with a zero.

Finally, we assign a concatenated string literal "\(min_p):\(sec_p)" to property text of force-unwrapped optional timeLabel.

Score Not Lower Than Zero

In order to make the gaming experience more fun and less strict, lets make sure you can’t have a score below zero. How?

In the code, there’s one line where we add 1 or subtract 1 from the score. We want to change the subtraction, and only allow it when the score is above zero. Can you figure out where?

When the score is 1 (1 > 0 == true) then subtract one from it, making the score 0. When the score is 0 (0 > 0 == false), don’t subtract. Do you understand that the if-statement conditional must be score > 0 (bigger-than) and not score >= (bigger-than-or-equal)?

Locate the right line, and make sure it resembles this:

if(score > 0) {
    score -= 1

Just for fun. Can you rewrite the scoring mechanism so that you can’t have a score greater than 100?

Uppacing The Tempo

Remember the hud we used? It’s a modal view that shows you whether your input is wrong or right. It’s set to remain on-screen for 3 seconds, which greatly influences the maximum score by eating up game time. Let’s adjust it.

Find the line that uses Grand Central Dispatch in the code, and change the 3 on that line into 1. It should then look like this:

DispatchQueue.main.asyncAfter(deadline: .now() + 1) {

Aw yiss! We got the timer added. Can you build and run the game, and figure out whether you’ve implemented the above code right?

Let’s see.

Resetting After The Timer Reaches Zero

Alright, we’re missing just one thing: a reset! When the timer reaches zero, we gotta tell the user their score and set the game up for another round.

Let’s do just that. Take a look at the code below.

let alertController = UIAlertController(title: "Time Up!", message: "Your time is up! You got a score of: \(score) points. Very good!", preferredStyle: .alert)

let restartAction = UIAlertAction(title: "Restart", style: .default, handler: nil)

self.present(alertController, animated: true, completion: nil)

score = 0
seconds = 60


What happens here?

  1. First, we create another constant with name alertController and type UIAlertController. It’s a complex type that puts a modal alert box on screen. Check out the parameters: the title, a message, then a delegate (we won’t use that) and a button that dismisses the modal alert box.
  2. Then, we put it on screen with the function presentViewController:animated:completion:.
  3. Then, we set score and seconds to zero, a reset. Setting those to zero doesn’t update the screen though, only the internal variables!
  4. Finally, we call our functions to update the time and score label, and create a new first number for the next round.

Where do you add this code?

In onUpdateTimer maybe? Yes! Why? That update function holds the perfect game context, because a game ends when the timer reaches zero: else if(seconds == 0). Change its contents (not the original if-statement itself!) so it resembles:

if(timer != nil)
    timer = nil

    let alertController = UIAlertController(title: "Time Up!", message: "Your time is up! You got a score of: \(score) points. Very good!", preferredStyle: .alert)
    let restartAction = UIAlertAction(title: "Restart", style: .default, handler: nil)

    self.presentViewController(alertController, animated: true, completion: nil)

    score = 0
    seconds = 60


Build and play your app again, to see if all went well. When the timer reaches zero, does it show the alert popup and reset the game? Awesome!

Get 5 of my best practices

Get 5 of my best practices

Learn how to build better iOS apps

I’ll show you exactly how I built a dozen professional iOS apps,
write extensible Swift code, and turn coffee into code.
Wait, what? Yup – into Swift code.


That’s it for now folks! You did a great job creating this Add 1 game, very well done! Bear with LearnAppMaking to stay updated on future tutorials, and read up on app publishing tactics on the blog in the meantime.

What did we learn?

  1. Using timers with Timer, invoking selectors on a target.
  2. Updating code by inserting new lines, without knowing what the code should look like (did you notice the different with last week?).
  3. Working with UIAlertController.
  4. More complex conditionals (if-statements), introducing if-then-elseif-else and bigger-than and bigger-or-equal-than.
  5. String formatting, zero-padding and the modulo operator.
  6. How to be an awesome app maker! (Hint: you already are!)

It wouldn’t be a course without a little challenge? See if you can change the game and it’s mechanics to solve the challenges below.

  1. Give users 2 minutes, or 120 seconds, of game time. (You do need to change an if-statement, too.)
  2. Adjust the scoring mechanism: the time decreases twice as fast when your score is above 10.
  3. Allow zeroes to be included in the random number. To do so, you must zero-pad the digit variable, to allow it to be 0123 for instance.
  4. Allow nines to be included in the random number. The “check if this answer is right” then needs a complete overhaul, because the difference won’t be 1111 anymore. Note: 0397 becomes 1408.

Good luck!

Reinder de Vries

Reinder de Vries is an indie app developer who teaches aspiring app developers and marketers how to build their own apps at Since 2009 he has developed over 50 apps for iOS, Android and the web, and his code is used by millions of users all over the globe. When Reinder isn't building apps, he enjoys strong espresso and traveling.

Comments & Questions

Got a comment or question? Let me know! I read and respond to every one. Thanks!

  • Jup! Good catch :-). This is because the text field is still active “behind” the modal dialog. When you input text, textFieldDidChange is executed. It’ll compare the input and the random number, and mark it as wrong because it’ll never match. You can either change the first conditional in textFieldDidChange to return when the input > 4 characters, or change showHUDWithAnswer as follows:

    Add the following line right below hud?.show(animated: true)

    inputField?.isEnabled = false

    Then, inside the async closure, add this at the end:

    self.inputField?.isEnabled = true

    This will disable the input field when the dialog hud shows, and then put the focus back into the input field when the hud disappears. The entire last conditional now looks like this:

    if(imageView != nil)
        hud?.mode = MBProgressHUDMode.customView
        hud?.customView = imageView
        hud?.show(animated: true)
        inputField?.isEnabled = false
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            self.hud?.hide(animated: true)
            self.inputField?.text = ""
            self.inputField?.isEnabled = true
  • Jon Perrault

    I noticed that after a new random number appears and the thumbs up is still there if you type anything into the box at that exact point, it skips to another number and marks it wrong. Is there anyway to fix this?

  • OK! If you put “print(#function)” (no quotes) at the top of viewDidLoad() in MainViewController.swift, do you see the name of the method show in the debugger window when you run your app? If not, you should check whether you removed “Main” from the project settings. It’s explained in the first part of the video.

  • Lucas Dickson

    Thanks! that fixed the error but when ever i run the app its showing a white screen

  • Gotcha! You’re missing a “-> Bool” at the end of application(didFinishLaunchingWithOptions:) method. Also, the “@nonobjc” keyword isn’t necessary. Change it to this:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
  • Lucas Dickson
  • Lucas Dickson

    After typing return true it says its an unexpected non void return value in void function

  • Yes Justin, they were! Check out the 3-series links right below the chapter “Project Code, Assets and This Series” (at the top of the page). Happy coding!

  • Justin

    Were subsequent videos ever published (Weeks 2 and 3)?

  • Hi Martin,

    OK. Could be the constraints, yes. Are you seeing any errors or constraint warnings when editing the XIB in Interface Builder? Errors and warnings show up at the top right corner of the list of UI elements. Also, make sure you’ve connected the outlet correctly, and that you’re setting a value with code in the Swift file.

    Are you using the stable Xcode 7 or a beta of Xcode 8?

  • Martin

    Hey, I’m running the app and for some reason the 1234 in brown is not displaying when I run the simulator. would this be an issue with the constraints? It shows in the MainViewController, just not when I run the sim. Help please :-)

  • That error message isn’t an error, it usually appears in the Simulator for no good reason. The reason the keyboard doesn’t show up is because you probably haven’t called becomeFirstResponder on the input field. Make sure you follow the video by the letter, otherwise you’ll run into issues like these.

  • Jye Disisto

    I fixed previous issues. But now I’ve noticed that when i launch the app. The iPhone numeric pad doesn’t pop up…. and below I’ve pasted the error message.

    2016-07-09 11:55:57.299 Add1[5686:468215] Can’t find keyplane that supports type 4 for keyboard iPhone-PortraitChoco-NumberPad; using 1336863583_PortraitChoco_iPhone-Simple-Pad_Default

  • OK, that most definitely means you either have the outlets set up wrong or there’s not a proper connection between the Interface Builder file and the Swift file. This is explained in the video, so make sure you double check that!

  • Max Cordell

    Ok so i think that fixed that problem but now i’m getting this.
    2016-07-03 16:47:40.752 add1[1994:42691] *** Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key view.’
    Thanks again!

  • Alright, got it. You need to go to your project’s settings by clicking on the top item in the Project Navigator (the blue one), and then go the General tab, and then remove the text from “Main Interface”. The app now thinks you want to start with the Main Storyboard, when in fact you’ve coded it to start with the first view controller in the AppDelegate. This is all explained in the beginning of the video, so make sure you watch that too.

  • Max Cordell

    I think this is it.
    2016-07-03 08:10:40.657 add1[22262:939997] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘Could not find a storyboard named ‘Main’ in bundle NSBundle (loaded)’
    Thanks so much!

  • In the bottom right area, the one with the bold black text (the Debug Area), what does the exception say? It’s usually at the top of all the text, like NSInconsistencyException bla bla bla. Let me know what that is, and we’ll figure it out!

  • Max Cordell

    help me i got a sigabrt error.

  • Thanks very much David! Your questions about programming and interest in coding apps helps fuel LearnAppMaking, so if you do have a question about coding — help me help you ;-)

  • David Pham

    Thank you SO much for this! I am new to programming and this helped me understand the overwhelming Xcode UI. How can I help support you?! :) Thank you again!!

  • Could you compare your code of AppDelegate.swift with the code from week 1? Perhaps you’ve missed something. A SIGABRT is a pretty generic error, so it could be just about anything.

  • Mari Vijil

    I need help really Bad!!!! I did week 1 two TIMES
    and I get the same error: Thread 1: signal SIGABRT

  • James Ikeler

    I’m been working on my own app(Not using your code at all).And I used your assets for my app.Can I use them if I were to publish them to the app store I would leave credit to the website and stuff.But if I can’t can you tell me who made them.

  • Hiya! Leuk om van je te horen :-)

    You’re gonna wanna keep an eye on the Swift and Xcode version numbers, then. Right now the most recent version is Swift 2.2 and Xcode 7.3, but they’re getting ready (3-4 months) to release Swift 3. All of our paid courses are kept up-to-date the minute a new version or a change comes out.

    As it so happens, the “Add 1” guide is updated for Swift 2.2. I know how frustrating it can be to check out an answer on StackOverflow, and then realize it only works on Swift 1.2… You’ll get the hang of it with practice :-).

    Keep coding!

  • Reinder!!!! nice tutorial man, ik ben een Nederlander maar woon nu in Florida, i’m just starting to learn app development. Its really hard because every tutorial i find online seems to be out of date overtime there is an Xcode update

  • The Project Navigator should show a file that’s called “Assets.xcassets”. It’s icon is a blue folder. Open it, then drag all the files from Finder to the list of files in the assets window.

  • Thanks. Could you share a screenshot with the File’s Owner selected, and then the Connections Inspector open? First click File’s Owner, then click the icon on the far right, the one with the circle and an arrow in it. It’ll show what you set the outlets to.

  • dylan

    How do you import the background and other images i can’t seem to find them i have Xcode 7.3

  • Noah1955

    very cool!

  • That’s a really weird error! Where did you put the extra semi colon? Glad it’s solved now :-)

  • Arian Nasser

    Just fixed my issue by removing the UIwindow semi colon , :)

  • If you could send a screenshot of the outlets, that’d be awesome. First click File’s Owner, then the Connections Inspector on the far right (circle with the arrow).

  • Arian Nasser

    Screen shots

  • Arian Nasser

    just finished the first part , and then when i tried to run it this happens. i’ve checked everything honestly everything was ok

  • At what point in the code guide are you?

    My guess is you should check the outlets on ViewController, if you set any. Click on “First Responder” in Interface Builder, then go to the Connections Inspector, and check that every UI element has an outlet assigned to it and no connections are “dangling” / orphaned. This can happen when you make an outlet connection and then change the name of the property, or remove it.

    Good luck :-)

  • Arian Nasser

    I am getting this error , What should i do? Every time i try to run it happens , please help

  • Does it say anything in the Debug Window? It’s pointing towards AppDelegate.swift because SIGABRT is a runtime crash, but the cause could be pretty much anywhere. Did you check all the optionals, i.e. if they’re unwrapped properly and not accidentally nil when force-unwrapped?

  • Edmond Hobeika

    Everything is solved now.

    Just the app is not running. Seems from the AppDelegate.swift. is says on the class name “Thread 1 – signal SIGABRT”

  • Edmond Hobeika

    I get this when i run the app

    “libc++abi.dylib: terminating with uncaught exception of type NSException”

  • Edmond Hobeika

    I just solved the “inputField!.text.characters.count < 4 || numbersLabel!.text.characters.count < 4"

    It should be
    "inputField!.text?.characters.count < 4 || numbersLabel!.text?.characters.count < 4"

    But i am still having issues with "textFieldDidChange:"

    The app is not running
    it crashes

  • Edmond Hobeika

    I’m still getting errors on “textFieldDidChange:” and “inputField!.text.characters.count < 4 || numbersLabel!.text.characters.count < 4"

  • Yeah, my bad, inputField is an optional, so it must be unwrapped. You can use force unwrapping, because you’re already checking whether inputField is accidentally nil. Change it for: inputField!.text.characters.count.

    If you’re unfamiliar with optionals, make sure to do a little reading on them or check out our Pro Course — that’s got it covered in great detail. See:

  • You’re missing a colon, it is: “textFieldDidChange:”

  • Edmond Hobeika

    I even get an error here

  • Edmond Hobeika


  • Edmond Hobeika

    the inputField.text.characters.count < 4 is not giving me an error..

  • Replace “println” with “print”, “score–” with “score -= 1”, “score++” with “score += 1”, and “count” doesn’t exist any more, so replace it with “inputField.text.characters.count < 4".

    By the way, you might want to change your font to a monospace one, it's way easier to read than the font you're using now.

  • Edmond Hobeika

    Screenshot below

  • Edmond Hobeika

    I am getting this error.

    Please help.


  • Did you create both the Swift class file and the XIB? I can only see the XIB file in the screenshot you sent, so most likely the class “MainViewController” is missing. See if you can delete the XIB file and then follow the steps from chapter “Creating MainViewController” to create both the XIB and the Swift class file.

    Good luck!

  • Daniel Qeli

    Like this

  • Daniel Qeli

    What should I do if it says MainViewController is an unresolved identifier while testing the app near the middle?

  • You’re absolutely right! That’s Swift < 1.2, thanks for pointing it out :-).

    This should help you:

    if(inputField == nil || numbersLabel == nil || count(inputField!.text) < 4 || count(numbersLabel!.text) < 4)