Create an iOS Game with Swift and Xcode

Written by Reinder de Vries on May 7 2020 in App Development

Create an iOS Game with Swift and Xcode

Let’s build a fun iOS game with Swift and Xcode! Fire up your Xcode, get your Swift hat on and get hacking with this fun game called Add 1. BOOYAH!

In this tutorial, you’ll learn how to create a game for iOS with Swift. We’ll dive into Xcode, and you’ll learn how to work with variables, optionals, Interface Builder, Auto Layout, outlets, timers, and much more. It’s everything you need to get started with iOS development!

Let’s get started. Here’s a quick overview of this tutorial’s chapters:

  1. What’s The “Add 1” Game?
  2. What You Need For This iOS Tutorial
  3. Required: Download The Game App’s Assets
  4. Set Up The iOS App Project In Xcode
  5. Configuring The Game View Controller
  6. Getting Started With Interface Builder
  7. Building The UI In Interface Builder
  8. Configuring Auto Layout Constraints
  9. Outlets: Connecting Code With UI
  10. Generating Random Number Strings
  11. Updating The Score And Number Labels
  12. Coding The “viewDidLoad()” Function
  13. Run The Numbers: Working With Text Input
  14. Coding The Countdown Timer
  15. Finishing The Game
  16. Wrapping Up

Updated for Xcode 11.4, iOS 13 and Swift 5.2.

What’s The “Add 1” Game?

The iOS game we’e going to create is called “Add 1”. It’s inspired by Daniel Kahneman, a renowned psychologist and economist, and author of Thinking, Fast And Slow. Kahneman used a game, that lets people “add 1” to an arbitrary sequence of numbers, to test a concept called cognitive strain.

The goal of the game we’re building is to “add one” to each of the numbers of a 4-digit sequence, as many times as you can in 60 seconds. You get one point for each correct answer. How many points can you score?

It’s a simple and fun iOS game project – perfect for a Thursday night or Sunday afternoon of playing with Swift. By the end of this tutorial, you’ll know how to make a simple game app for iOS and you’ll be able to challenge your friends, to see who can make the most “add ones” in one minute.

What You Need For This iOS Tutorial

You don’t need much to get started with this iOS game tutorial, but you need at least:

  1. A computer that runs macOS
  2. Xcode 11 with Swift 5
  3. An hour or so of time in total
  4. A few graphics images and assets (see below)
  5. No programming experience is required!

Learn how to build iOS apps

Get started with iOS 13 and Swift 5

Sign up for my iOS development course, and learn how to build great iOS 13 apps with Swift 5 and Xcode 11.

Required: Download The Game App’s Assets

You’ll need to download the following assets to complete the app project:

This iOS app project makes use of the following free assets:

Let’s get started!

Set Up The iOS App Project In Xcode

The first step to build the Add 1 app project, is to set up Xcode. Here’s how:

  1. Start Xcode on your Mac
  2. Create a new project by choosing File -> New -> Project… from the menu
  3. Choose the Single View App template, from the iOS category, and click Next
  4. Name the project Add 1, choose Storyboard for User Interface, leave the other fields to their defaults, and click Next
  5. Choose a convenient location for the project, such as your Desktop folder
  6. Finally, click Create

Set Up The iOS App Project

Next up, we’re going to change some of the project’s settings.

  1. Click on the Add 1 project in the Project Navigator on the left
  2. Click on Add 1 below Targets, in the sidebar
  3. Make sure to select the General tab, at the top (it’s the default)
  4. Scroll down to the Deployment Info section
  5. Make sure that only Portrait is selected, for Device Orientation
  6. Make sure that the Hide status bar setting is enabled

These changes are minimal, but essential. Setting the device orientation to Portrait means that you won’t be able to rotate the game’s User Interface (UI), which is exactly what we need. And hiding the status bar is just a nicety, so it won’t interfere with the game UI. Neat!

Change project settings

Want to get a tour of Xcode? Check out this tutorial: Xcode 11 Tutorial for Beginners

Configuring The Game View Controller

When you set up your Xcode project, Xcode automatically added a view controller and Storyboard to the project.

A view controller is a common component in iOS apps. It controls the way a User Interface (UI) looks, and how a user interacts with it. A view controller typically consists of a Swift class and a Storyboard (or XIB). The Storyboard contains layout and UI information, and the Swift class contains the code that governs the view controller.

The project’s default view controller is just named ViewController. We’re now going to rename the view controller to GameViewController, because that better describes what the view controller does.

Here’s how:

  1. Open the ViewController.swift file, by clicking on it in the Project Navigator
  2. Locate the text “ViewController” on the line that says class ViewController: UIViewController {
  3. Right-click on the text “ViewController” and choose Refactor -> Rename... from the menu
  4. Rename the “ViewController” to GameViewController, and then click Rename in the top-right corner

Rename ViewController

With the refactoring tool, we’ve now renamed the ViewController class to GameViewController. The tool also renames references to ViewController in the Storyboard, and the Swift filename itself. That’s super useful, because it saves us from having to manually change these references!

Refactoring is the practice of rewriting programming code to make it better structured, easier to extend, more concise, and easier to read. It’s important in practical iOS development, because improves how your app works “under the hood.”

Getting Started With Interface Builder

Before we can build the app’s User Interface (UI) in Interface Builder, we’ll need to do some setting up, such as importing graphics assets and fonts.

The game UI consists of an input field, a few text labels, and a bunch of images. Building your app’s UI is an important part of the app development workflow. You build UIs with Interface Builder (or SwiftUI). Interface Builder is a UI editor that’s built into Xcode.

Here’s a quick overview:

Xcode Interace Builder overview

You can see Interface Builder if you open Main.storyboard in Xcode, by clicking on it in the Project Navigator.

Interface Builder is a scaffolding tool. You don’t use it to create interactive UIs, but it’s used to set up the UIs you need for your app. A XIB or Storyboard, the file types of Interface Builder files, merely contain information about the layout and views of a UI and their attributes.

A view controller contains a main view, which consists of subviews. Subviews can be text labels, buttons, image views, input fields, table views, and so on. Views have attributes, such as a background color.

For this game app we’re working with a Storyboard. A Storyboard is an Interface Builder file that contains multiple UIs, and some settings that govern the flow and transitions between these UIs. The game app we’re building uses a Storyboard that contains just one UI.

Quick Tip: In Xcode, you can add new User Interface elements to your Storyboard or XIB by using the Library button, in the top-right corner of Interface Builder.

  • On Xcode 10, this button looks like a square in a circle.
  • On Xcode 11, this button has a plus + icon.

After clicking it, a gizmo that shows all available UI elements pops out. Use the search field to find the UI element you need, and then drag-and-drop it onto the editor canvas.

Interface Builder Library

Here’s what the complete User Interface looks like in Interface Builder:

The User Interface of the Add 1 app has 9 different components:

  1. Background Image of type UIImageView. This image shows a blue/green full screen background graphic.
  2. Score Image of type UIImageView. This image is displayed behind the Score Label – it’s a graphic UI element for the user’s score.
  3. Score Label of type UILabel. This label displays the user’s score as text. We’ll update it dynamically if the user scores a point in the game.
  4. Time Image of type UIImageView. Again, this is a graphic for the countdown timer.
  5. Time Label of type UILabel. This label displays the user’s remaining game time, and it will count down to zero.
  6. Number Image of type UIImageView. This is another UI graphic that puts a nice border around the Number Label.
  7. Number Label of type UILabel. This label displays the random 4-digit code that’s at the center of the Add 1 game.
  8. Input Text Field of type UITextField. It’s used to input an answer to the game’s Add 1 challenge. We’ll also give this text field a nice background image.
  9. Explanation Label of type UILabel. This label will read: “Add 1 to each of the digits. So, 1357 becomes 2468”, to explain the game to the user.

We’re going to use 3 different colors. Their RGB color codes are:

  • White: 255, 255, 255 or #FFFFFF
  • Brown: 135, 79, 33 or #874F21
  • Blue: 105, 168, 255 or #69A8FF

You need a few graphics assets, and a custom font called HVD Comic Serif Pro, to build the game’s User Interface. Here’s how you can get them:

  1. Download the project assets, a font and some images (see section above)
  2. Unzip the .zip files in a convenient location
  3. Open the unzipped folders in a Finder window

First, we’re going to add these image files to Xcode. Here’s how:

  1. Open the Assets.xcassets library file in Xcode, by clicking on it in the Project Navigator
  2. Switch to Finder and select all image files
  3. Drag-and-drop the files from Finder into Xcode (into the left sidebar, right below AppIcon)

Interface Builder Library

Then, the next step is to add the custom font to Xcode. Adding custom fonts in your app is a bit tricky. In short, the steps we need to take are:

  1. Install the custom font in macOS
  2. Add the .otf font file to Xcode
  3. Add the font filename to Info.plist
  4. Select the font in Interface Builder, when building the UI

First, make sure that the HVD Comic Serif Pro font is installed on your Mac. You can do this by double clicking the HVD_Comic_Serif_Pro.otf file in Finder. In the dialog that appears, click Install Font.

Then, with the font’s folder open in Finder, drag-and-drop the .otf font file from Finder into Xcode. Make sure to drop it onto the Project Navigator!

In the dialog that appears, select the checkbox before Add to Target. This will add the font file to the app project, as an asset.

Interface Builder Library

Then, open the Info.plist file in Xcode by clicking on it in the Project Navigator. This is a property list file, and it includes basic information about our app project.

We’re going to add the filename HVD_Comic_Serif_Pro.otf to the Fonts provided by application field in Info.plist. Here’s how:

  1. Right-click somewhere in the editor and choose Add Row. A new blank row appears.
  2. In the Key field, start typing Fonts provided by application. You don’t have to type all of this – once you start typing the text auto-completes.
  3. Click the small arrow to the left of this new field to expand it. You see a subrow that’s called Item 0. Double-click on the empty space to the right of this field, in the Value column.
  4. In the text field that appears, type HVD_Comic_Serif_Pro.otf, the filename of the font.

The font is now added to Xcode! Later on in this tutorial, we’ll use the font to build the game’s User Interface.

Custom font in Plist in Xcode

Working with a different font, or does your font have a different filename? Just complete the above steps, but use the different filename.

Building The UI In Interface Builder

Alright, let’s get a move on! It’s time to build that game UI… Make sure to open Main.storyboard in Xcode first.

Before you continue, we’ll need to adjust a setting in the Storyboard. Here’s how:

  • First, select the Game View Controller Scene in the Document Outline on the left. It’s the item at the top.
  • Then, open the File Inspector on the right of Xcode. It’s the left most icon.
  • Finally, uncheck the checkbox for Use Safe Area Layout Guides

This setting will enable us to create a full screen layout. You use the Safe Area Layout Guides to constrain the UI within an iPhone’s top and bottom edge. This is especially important on iPhone X, iPhone 11 Pro, etc., because those edges can prevent a user from seeing the complete UI. We don’t have to take that into account, because we’re going to create a full screen UI.

How to change Use Safe Area Layout Guides

Next, we’re going to start with the Background Image.

1. Background Image

First, open the Library by clicking on its button in the top-right corner of Interface Builder. The button looks like a + plus sign. Find the UIImageView in the list – you can search for image view – and then drag-and-drop it from the list onto the editor’s canvas.

Then, resize the image view so it takes up the entire view controller, including the status bar. It’s OK to position the image view behind the iPhone’s top notch.

Next, we’ll have to change the image view’s settings. Here’s what you do:

  1. With the image view selected, open the Attributes Inspector on the right of Xcode. It’s icon looks like a down-pointing arrow, but it’s actually a slider icon.
  2. Then, find the Image field at the top, and use the dropdown to change the image to background. This is the background image you imported earlier.
  3. Finally, find the setting for Content Mode and set it to Aspect Fill

Neat! The Content Mode setting ensures that the image inside the image view resizes to take up as much space as it can, effectively “filling” the image view. It’s super helpful for differently sized screens, such as the iPhone 11 or iPhone 8 Plus.

Add Image View In Xcode

The names of image assets, as found in the Image attribute, are exactly the same as the asset names you find in Assets.xcassets. Good to know!

2. Score Image

Next up is the image that’s shown behind the score label. It’s a simple graphic. Here’s how you add it:

  1. First, drag-and-drop another image view from the Library onto the canvas
  2. Go to the Attributes Inspector and change the Image setting to score
  3. Switch to the Size Inspector and change the size of the image view to 120 x 50 points
  4. Finally, move the image view to the top-left corner of the view. On iPhone 11, the x and y coordinates are set to something like 20 and 50.

Add Image View to Xcode

As you’re dragging a UI element over the canvas, blue helper lines appear. These guides can help you position the UI more easily. Along the edges of the screen, they take into account a safe margin for viewing.

3. Score Label

We’re now going to place a label on top of the score image. Here’s what you do:

  1. First, find the Label element (class name UILabel) in the Library and drag-and-drop it onto the canvas
  2. Move the label, by drag-and-drop, so it’s centered on top of the score image (a few helper lines usually appear)
  3. Change the text of the label to something like 1234 – you can do this by double-clicking on the label itself
  4. Switch to the Attributes Inspector, find the Color attribute, and change it to white (you can use the dropdown)

We’re now going to change the font of the label. Here’s how you do that:

  1. First, with the Attributes Inspector open, find the Font attribute
  2. Click on the small T button to select a different font
  3. In the gizmo that pops up, set the Font field to Custom
  4. Set Family to HVD Comic Serif Pro
  5. Set the Size to 22
  6. Finally, click Done

Now that you’ve changed the font size, you may need to reposition the label once more, so it’s centered nicely on top of the score image view.

Change the font in Interface Builder

Quick Note: If you can’t select find the right font in the dropdown, you’ve probably missed a step in the previous section, when adding the custom font to Xcode.

4. Time Image

The time image and label are exactly the same as the score image and label, except that they’re at the top-right of the view.

First, let’s add the time image. Here’s how:

  1. First, drag-and-drop an image view from the Library onto the canvas.
  2. Go to the Attributes Inspector and change the Image setting to time.
  3. Switch to the Size Inspector and change the size of the image view to 150 x 50 points.
  4. Finally, move the image view to the top-right corner of the view. On an iPhone 11, this roughly corresponds to x = 244 and y = 50.

The position coordinate system in Interface Builder, and on iOS, works in such a way that the top-left corner of a view has X, Y coordinate (0, 0). A view’s origin point is also its top-left corner.

5. Time Label

Adding the new time label is exactly the same as adding that previous score label. Here’s what you do:

  1. Find the Label in the Library and drag-and-drop it onto the canvas
  2. Move the label, so it’s centered on top of the score image
  3. Change the text of the label to 00:00
  4. In the Attributes Inspector, change the Color to white
  5. Finally, change the label’s font to HVD Comic Serif Pro size 22

6. Number Image

The number image is the background for the number label. We’re going to add it to the top-center of the view. Inside the label (see below), the game shows the generated numbers for the Add 1 game.

Here’s how you add the number image:

  1. You’ve guessed it – add another image view to the editor’s canvas!
  2. Set its Image to number, in the Attributes inspector
  3. Set its size to 300 x 130 points, in the Size inspector
  4. Move the image view so it’s centered horizontally, and positioned at the top of the view controller, a bit below the score and time image views

7. Number Label

The number label is centered within the number image. The label itself shows a 4-digit number that’s randomly generated.

Here’s how you add the number label:

  1. Add a label to the view controller, by using the Library
  2. Set the label’s text to something like 1234
  3. Set the label’s Color to brown (RGB: 135, 79, 33, see below)
  4. Set its font to HVD Comic Serif Pro size 70
  5. Set the text Alignment to Center
  6. Move the label so it’s centered in the middle of the number image

It’s easiest to change the color like this:

  1. Select the label, and open the Attributes Inspector
  2. Click the dropdown for Color, scroll down, click Custom…
  3. In the gizmo that appears, manually input the RGB color code 135, 79, 33

If you don’t see input fields for RGB, make sure you’ve opened the Color Sliders tab, and then select the RGB Slider from the dropdown.

Interface Builder

On iOS, one way to work with different colors is by using RGB, which stands for Red, Green and Blue. We can either express RGB colors in 3 values between 0 and 255, or by using a hexadecimal code like 874F21. This hex code uses 3 pairs of 2 values between 0 and F. Every pair corresponds to a value on the 0 to 255 scale, which adds up to an RGB code again.

8. Input Text Field

The input for the game is a text field (class UITextField), that the user uses to input a 4-digit number. When the text field gets selected, the on-screen keyboard pops up. And unlike the other UI elements, this input UI has an actual background image itself!

Here’s how you add the text field to the UI:

  1. First, find the Text Field in the Library and drag-and-drop it onto the editor’s canvas. Pick the Text Field, don’t accidentally pick the Text View.
  2. Then, change the following attributes in the Attributes Inspector
    • Set the font Color to blue (RGB: 105, 168, 255, hex: 69A8FF)
    • Set Font to HVD Comic Serif Pro size 70
    • Set text Alignment to Center
    • Set Background to input
    • Set Border Style to None (the first icon)
    • Set Keyboard Type to Number Pad
  3. Then, with the Size Inspector, change the size of the text field to 256 x 110 points.
  4. Finally, position the text field horizontally centered, slightly below the number label and image.

Position your views in such a way that the text field isn’t (partially) hidden by the on-screen keyboard. If they’re both above the midline across the screen, you’re good!

Text field in Interface Builder

9. Explanation Label

And last but not least – the explanation label! This label briefly explains how the game works. And it’s nicely hidden from view when the on-screen keyboard pops up.

Here’s how to add it to the UI:

  1. First, add a label to the canvas, by using the Library
  2. Change the following attributes:
    • Set the label’s text to: “Add 1 to each of the digits. So, 1357 becomes 2468.”
    • Set the text Color to white
    • Set the Font to HVD Comic Serif Pro size 24
    • Set text Alignment to Center
    • Set Lines to 2
  3. Change the size of the label to about 375 x 65 points
  4. Finally, position the label centered horizontally, just above the “treeline” at the bottom of the view

Awesome! You’ve finished setting up the UI elements. Let’s continue with Auto Layout constraints…

Finished UI in Xcode!

If you “lose” the label off-screen when you change the label’s text, and you can’t resize it, simply set its width manually with the Size Inspector to get it back on screen.

Configuring Auto Layout Constraints

Oh yes, Auto Layout. This is going to be fun! We’re now going to add some rules to the User Interface, to make it resize itself nicely on differently sized screens. These rules are called constraints, and the layout system they’re a part of is called Auto Layout.

With constraints, we can determine what happens when a User Interface changes size, or is shown on a differently sized iPhone. Based on these constraints, the Add 1 game app can be used on iPhone models with different screen sizes, such as the iPhone 8, 8 Plus, X or 11 Pro.

The constraints we’re about to add aren’t complex, but if you’re new to Auto Layout, working with constraints can surely seem challenging. Just take it slow, read carefully what to do, and you’ll have a working UI in no time.

In short, here’s what the constraints are going to do:

  1. Background Image: Sticks to the top, right, bottom and left edges. This makes the background image full screen.
  2. Score Image: Sticks to the top left of the UI.
  3. Score Label: Centered in the middle of the score image.
  4. Time Image: Sticks to the top right of the UI.
  5. Time Label: Centered in the middle of the time image.
  6. Number Image: Sticks below the score and time, center horizontally.
  7. Number Label: Centered in the middle of the number image.
  8. Input Text Field: Sticks below the number image, center horizontally.
  9. Explanation Label: Sticks to the bottom of the UI.

To add a constraint, first select the UI element you want to add the constraint to. Then, click the Add New Constraints button, or the Align button, in the bottom-right of the Interface Builder editor. The Add New Constraints button looks a bit like a Star Wars TIE Fighter. Like this:

Add New Constraints icon

Let’s get started!

1. Background Image

First, we’re going to add constraints to the background image. Here’s how:

  • First, select the background image view in the Document Outline
  • Then, click the Add New Constraints button in the bottom-right of Interface Builder
  • Then, click the 4 red struts, and make sure to set their input fields to 0
  • Finally, click Add 4 constraints

The 4 red struts represent the Leading, Trailing, Top and Bottom edges of the image view. You can use them to determine the distance between one UI element, and the next.

Right now, we’ve set distance between the 4 edges of the image view and the edges of the screen to 0. When the view resizes, the image view sticks to the edges of the screen, essentially creating a full screen background image.

A constraint, such as the 4 we created, connects two edges of two UI elements with each other, optionally adding some space between them. This can either be the edge of the next UI element, or the edge of the superview.

2. Score Image

Next up, the score image. We’re going to affix that to the top-left edge of the screen. We’ll also set its width and height with constraints.

Here’s how:

  • First, select the score image view in the Document Outline
  • Then, click the Add New Constraints button in the bottom-right of Interface Builder
  • Then, make sure to untick the Constrain to margins checkbox
  • Then, click the left and top struts, and leave the input fields as-is
  • Then, tick the checkboxes for width and height
  • Finally, click Add 4 constraints

If you’ve looked carefully, you saw that the top and left constraint values were already set to the distance between the UI element and the edges of the screen.

In the screenshot below, the top constraint is set to 6, and the left constraint to 20. This corresponds to the x and y coordinates we set for the image view.

With the width and height constraints you can fix a UI element to a certain size, of course. You can also set either one of them, to allow a UI element to grow in size.

3. Score Label

OK, now let’s add constraints for the score label. We’re going to center this label on top of the score image. Here’s how:

  • First, select both the score label and the score image. You can hold Shift, while clicking, to select them both.
  • Then, click the Align button at the bottom-right of Interface Builder. It’s on the left of the Add New Constraints button.
  • Then, tick the checkboxes for Horizontal Centers and Vertical Centers.
  • Finally, click Add 2 constraints

These two constraints, who determine the alignment between 2 UI elements, will center the label on top of the image. Auto Layout will first determine the position for the image, and then center the label on top of it.

An important principle of Auto Layout constraints, is that the constraint resolver will attempt to satisfy all constraints. It doesn’t apply constraints in any given order, but instead will attempt to find a position and size for a UI element that fits all of its constraints. This is efficient, but sometimes challenging to comprehend, and it can lead to constraints breaking.

4. Time Image

Next up, the time image. We’re going to repeat what we did for the score label and image, and apply it to the time label and image.

Here’s how:

  • First, select the time image view in the Document Outline
  • Then, click the Add New Constraints button in the bottom-right of Interface Builder
  • Then, make sure to untick the Constrain to margins checkbox
  • Then, click the right and top struts, and leave the input fields as-is
  • Then, tick the checkboxes for width and height
  • Finally, click Add 4 constraints

This will affix the time image to the top-right of the screen. Neat!

5. Time Label

Just like the score label, the time label is centered on top of the time image. Here’s how:

  • First, select both the time label and the time image
  • Then, click the Align button at the bottom-right of Interface Builder
  • Then, tick the checkboxes for Horizontal Centers and Vertical Centers
  • Finally, click Add 2 constraints

You may notice that the time label is slightly off center. Let’s fix that! We’re going to change the offset of the Center X Alignment constraint of the time label.

Doing so consists of 2 steps:

  1. Select the right constraint, the Center X Alignment constraint
  2. Set its constant value to -20

You can select the constraint in no less than 3 ways! Here’s how:

  1. First, select the time label. Finally, click the small blue vertical line in the middle of the label.
  2. First, select the time label. Then, go to its Size Inspector. Finally, double click the Align Center X to: time constraint, below Horizontal.
  3. First, find and expand the Constraints item in the Document Outline. It’s a sub-item of View. Select the 00:00.centerX = time.centerX constraint.

Constraint Center

Which approach you use, is up to you! With the right constraint selected, go to its Size Inspector. Finally, set the input field for Constant to -20. The time label should now shift slightly to the left of the center of the time image. Nice!

Constraints

Take a moment to study the Size Inspector for this constraint. If you look closely, you’ll see that a constraint is actually a “relation” between two items. To center the time label and time image onto each other, horizontally, we’re saying that both their centerX properties are equal. We’ve also added an offset of -20. Feel free to inspect other constraints too!

6. Number Image

OK, let’s move on to the number image. That’s the image behind the number label. We’re going to center it horizontally on screen, and position it below the score and time images.

Here’s how:

  • First, select the number image view via the Document Outline
  • Then, to center horizontally, do this:
    • First, click the Align button at the bottom-right of Interface Builder
    • Then, tick the checkbox for Horizontally in Container
    • Finally, click Add 1 constraint
  • Then, to position the image view, do this:
    • First, click the Add New Constraints button
    • Then, click the top strut and set its value to about 60
    • Then, tick the checkboxes for width and height
    • Finally, click Add 3 constraints

If you look carefully, you’ll see a blue line appear between the number image and the score image (left). This is the constraint that puts about 60 points of distance between those UI elements.

That’s the core principle of working with constraints: putting space between UI elements, to determine how their positions relate to each other.

Can’t keep horizontal and vertical centering apart?

  • The horizontal axis (X) lies flat. Horizontal centering happens by placing something in the middle of the horizontal axis. You can draw an imaginary vertical centerline straight through the horizon. Rotating along the horizontal axis flips top and bottom.
  • The vertical axis (Y) goes straight up. Vertical centering happens by placing something in the middle of the Y axis. You can draw an imaginary horizontal centerline along the horizon. Rotating along the vertical axis flips left and right.

7. Number Label

Moving on! Next up: the number label. You’ve guessed it – this one is centered on top of the number image, both horizontally and vertically.

Here’s what you do:

  • First, select both the number image and number label. Hold Shift to select them both.
  • Then, click the Align button in the bottom-right of Interface Builder, and tick the checkboxes for Horizontal Centers and Vertical Centers
  • Finally, click Add 2 constraints

If you want, you can slightly adjust the number label inside the number image. Select the Center Y constraint by clicking the blue horizontal line across the image. You can set its constant to about 5.

8. Input Text Field

Almost there! We’re now going to set the constraints on the input text field. That’s the one users use to input numbers for the Add 1 game. Here’s how:

  • First, select the input text field in the Document Outline
  • Then, to position the text field, do this:
    • First, click the Add New Constraint button
    • Then, click the top strut and set its value to about 20
    • Then, tick the checkboxes for width and height, about 250 x 110
    • Finally, click Add 3 constraints
  • Then, to center it horizontally, do this:
    • First, click the Align button
    • Then, tick the checkbox for Horizontally in Container
    • Finally, click Add 1 constraint

Awesome!

9. Explanation Label

And, last but not least, the explanation label at the bottom of the UI. Do you really need step-by-step instructions for that one? Of course not!

Here’s what needs to happen:

  1. Set the height constraint of the label to 65
  2. Set the bottom constraint to about 50
  3. Center the label horizontally in its container
  4. Set its width to about 375

If the above constraints causes problems on smaller screens, remove the width constraint, and add some space by setting the leading and trailing constraints to about 20.

At this point, you should be able to change the device model you’re viewing this UI as, in Interface Builder. You can do so by clicking View as: at the bottom of Interface Builder. Then, select a different Device. The UI should now update and show you the different device, with different dimensions.

And if you run the app right now, by clicking Play or pressing Command + R, the game should show right up in iPhone Simulator. Awesome!

Outlets: Connecting Code With UI

Before we can create the game with Swift code, we need a way to change the User Interface (UI) with code. For example, the score label needs to get updated with the user’s score, and the time label needs to show the remaining time.

All that happens with Swift code, but how do we connect that code to the UI in Interface Builder? That’s where outlets come in.

First, open the GameViewController.swift in Xcode by clicking on it in the File Navigator on the left. You’ll see an empty Swift view controller. It looks a bit like this:

class GameViewController: UIViewController
{
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

The GameViewController is the class that will govern what happens in the Add 1 game, and specifically its User Interface. It’s the code equivalent of the UI we just created in Interface Builder.

Connecting a UI component, from Interface Builder, to code in the view controller class, takes 2 steps:

  1. Creating outlet properties in the view controller class
  2. Creating outlet connections in Interface Builder

It’s as simple as creating a property in the class and connecting that to a UI component. The property will then hold a reference to the UI component, when the view controller loads.

First, add the following lines of code to the top of the GameViewController class:

@IBOutlet weak var scoreLabel:UILabel?
@IBOutlet weak var timeLabel:UILabel?
@IBOutlet weak var numberLabel:UILabel?
@IBOutlet weak var inputField:UITextField?

Make sure to add them within the class, so inside the first opening squiggly bracket {. Here’s what happens on those lines:

  1. First, that @IBOutlet keyword means that the property we’re creating is an outlet. A property is a reference (a variable) that’s part of the class.
  2. Then, we write weak var. That means that this property is weak, as opposed to strong. Without going too deeply into memory management; using weak as a default will potentially prevent a strong reference cycle later on.
  3. Then, we write the property name and type, ex. numberLabel 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.

What’s most important here are the names of the properties and their types. You see we’ve created 4 properties, with the names scoreLabel, timeLabel, numberLabel and inputField. The labels have type UILabel, and the input field has type UITextField. All properties are optionals.

If you want to learn more about view controllers, outlets and properties, check out this article: View Controllers Explained: Ultimate Guide For iOS & Swift.

The next step is connecting the UI elements to the outlet properties in Interface Builder. Here’s how:

  • First, open the Main.storyboard in Interface Builder
  • Then, select the Game View Controller in the Document Outline
  • Then, open the Connections Inspector on the right
  • Finally, see if you can spot the properties we created below Outlets

Next up, we’re going to connect the outlet properties to their respective UI elements. First, make sure you’ve found the properties below Outlets. Do you see those small circles next to them? We’re going to draw lines from those circles to the UI elements in the editor.

Here’s how:

  1. Find the scoreLabel property below Outlets
  2. Click-and-drag from the circle on the right of the property …
  3. … to the score label UI element in the editor (and let go)

You should now see Score Label next to the scoreLabel property. Make sure to repeat the steps for the other 3 outlets!

  • timeLabel to the time label (the right one)
  • numberLabel to the number label (the middle brown one)
  • inputField to the input text field (the bottom blue one)

You should end up with 4 outlet connections. Note that a default outlet connection, for view, is already present. Here’s a helpful screenshot, that shows making the outlet connection:

Quick Tip: You can find a UI element’s type via the Identity Inspector. Select the UI element in Interface Builder, go to the Identity Inspector, and see what’s next to Class. It’s important that the type of an outlet property matches the type of the UI element.

Generating Random Number Strings

We’re now going to write some code to generate a string of random numbers. This string, such as "9163", consists of 4 characters. It’s the basis of the Add 1 game, because a user needs to “add 1” to each of the number’s digits.

The code we’re about to write is an extension. We’re extending the String type with a new function, called randomNumber(length:).

Here’s how:

  • First, right-click on the Add 1 folder in the Project Navigator and choose New File…
  • Then, choose the Swift File template from the iOS category and click Next
  • Then, in the dialog that appears, name the file String.swift and save it alongside the other Swift files of the project
  • Finally, click Create

A new Swift file appears! We’re going to add some code to it. First, add the following statement to the file. You can add it right below import Foundation.

extension String
{

}

The extension block tells Swift we want to extend the String type. Next, add the following code within the squiggly brackets { and }:

static func randomNumber(length: Int) -> String
{

}

The above code is called a function declaration. We’re declaring a new function called randomNumber(length:). It has one parameter length of type Int, and its return type is String. Differently said, the function accepts an integer as input and produces a string as output.

The static keyword is used to add this function to the class String, rather than an instance of String. This is often called a class method, as opposed to an instance method. At a later point, we’re going to call the function like this:

let number = String.randomNumber(length: 4)

Next up, we’re going to add the following block of code within the randomNumber(length:) function:

var result = ""

for _ in 0..<length {
    let digit = Int.random(in: 0...9)
    result += "\(digit)"
}

return result

What’s going on? This is:

  • First, we’re creating a variable result of type String. It’s initialized with an empty string.
  • Then, we’re using a for loop to iterate from zero to length. When length is 4, the loop will repeat 4 times.
  • Then, within the loop, we’re generating a random integer between 0 and 9. On the second line of the loop, this integer is appended to the result string.
  • Finally, on the last line, we’re returning result. This is the output string of the function: a string with random numbers, of a given length.

We now have a function to generate random numbers with. Neat!

What’s that "\(digit)" bit? That’s called string interpolation. You can use it to include a variable in a string. In this case, we’re using it to convert the integer to a string, so it can be appended to result.

Updating The Score And Number Labels

We need a way to keep track of a user’s score. To that end, we’re going to add another property to the GameViewController class.

First, switch over to the GameViewController.swift file. Then, add the following property to the top of the class, below the outlet properties:

var score = 0

Easy, right? But what is the type of the score property? In the above code, the 0 is an integer literal of type Int. Based on that, we can infer that score has type Int. In fact, that’s exactly what Swift figures out – with type inference – if we don’t provide a type annotation in our code.

OK, we’re going to code 2 functions next. These functions will help us manage the game, later on.

First, add this function to the class:

func updateScoreLabel() {
    scoreLabel?.text = String(score)
}

What’s the function do? It merely updates the score label, with the current score. We’re using the text property of the scoreLabel property, and set it to "\(score)". Two things stand out here, though!

  1. The question mark ? right after scoreLabel is used for optional chaining. When scoreLabel is nil, the code graciously and silently stops at that point, and continues to the next line. The effect? We’re unwrapping the optional, and only continue when it’s not nil.
  2. The String(···) code is called an initializer. We’re using the score property as the parameter for this string, effectively converting the integer to a string. After all, the type of the text property is String.

OK, now add the following function to the class:

func updateNumberLabel() {
    numberLabel?.text = String.randomNumber(length: 4)
}

What’s going on in there? Here’s what:

  • The text in the numberLabel is set to the result of the randomNumber(length:) function. In other words, we’re generating a random number and assign it to the number label.
  • Again, we’re using optional chaining. And that randomNumber(length:) function? That’s coming from our extension of the String type! Now that it’s added to the type, we can use it everywhere. Neat!

Quick question: Why are we adding such simple functions to the class? They’re just one line of code long!

Well, what we’re doing right here is creating abstractions. We’re abstracting away some block of code, so we can reuse it later. Whenever we need to update the score or number labels, we can just call that function. We don’t have to bother with the text properties, or optional chaining.

And if we use the functions more than once, we’ve also saved some lines of code. You don’t have to maintain or debug the code you don’t write!

Coding The “viewDidLoad()” Function

The viewDidLoad() function is the starting point of any view controller. At that point, the view is loaded but not yet shown on screen. It’s called once in the lifetime of a view controller, which makes it the perfect setup point for a view controller.

At the start of the Add 1 game app, the score needs to be set to zero, and we also want to show the first random number to the user. Guess what? We just coded the perfect functions for that purpose.

Add the calls to the updateScoreLabel() and updateNumberLabel() functions to the viewDidLoad() function. Your function should now look like this:

override func viewDidLoad()
{
    super.viewDidLoad()

    updateScoreLabel()
    updateNumberLabel()
}

What does override mean? The viewDidLoad() function is defined in UIViewController. Every view controller subclasses that UIViewController class, and inherits its functionality. When we want to customize some of its functionality, we can override the function and customize its implementation.

The super.viewDidLoad() function call gives us access to the parent of the GameViewController class instance, which is UIViewController. In other words, we’re calling the super implementation of viewDidLoad(). We’re overriding the function, but still call its “super” implementation prior to overwriting it.

Quick Question: Can you assert what the text inside the score label and number label is, at the start of the game?

Run The Numbers: Working With Text Input

We’re ready to code the most important part of the Add 1 game app: validating a user’s input, and increasing the game score! In short, we’ll need to respond to user input in the text field, check if the input is OK, and increase the score.

Here’s what we’re going to do:

  1. Connect a “editing changed” event from the input field, to a function
  2. In the function, grab the input text and the number from the label
  3. Validate that the input field has 4 characters of text in it
  4. Compare each of the digits from the input field against the digits in the number label, and see if all 4 are correct
  5. Respond to a correct or incorrect number, by adding or subtracting 1 from the user’s score

Let’s get started!

Responding To Text Input

The first step is to respond when the user has edited the text of the input field. We’re going to call a function when the inputField emits an .editingChanged event.

First, make sure to add the following function to the GameViewController class:

@IBAction func inputFieldDidChange()
{

}

This function is going to be called when the user edits the text in the input field. We’ve designated the function with @IBAction, which means that we can connect this function to an action in Interface Builder. This is similar to an outlet.

Here’s how you can make that connection:

  • First, open Main.storyboard in Interface Builder and select the input field
  • Then, go to the Connections Inspector and find the Editing Changed item under Sent Events
  • Then, drag-and-drop from the circle next to Editing Changed to the Game View Controller item in the Document Outline
  • Finally, when a gizmo appears, select the inputFieldDidChange() function

You can also connect actions to UI elements with code. This mechanism is called target-action. The steps we’ve just taken in Interface Builder, can also be coded like this:

inputField?.addTarget(self, action: #selector(inputFieldDidChange), for: .editingChanged)

You don’t have to add this to your code, but if you choose to do so, make sure to remove the Interface Builder-based action first.

Getting The Input Field Text

Next up, we want to grab hold of the text inside the input field. We’ll also get the text in the number label, so we can compare both.

First, add the following code inside the inputFieldDidChange() function:

guard let numberText = numberLabel?.text, let inputText = inputField?.text else {
    return
}

In the above code, we’re combining the guard statement with optional binding. What you get, is a guard let block that evaluates whether an expression is nil. When it isn’t nil, the non-optional values are assigned to their respective constants. When a guard let expression is nil, the else block is invoked, which will return and exit the function.

In other words, 2 things can happen with the above code:

  1. Either numberLabel?.text or inputField?.text is nil, and the function returns and exits
  2. Both values are not nil, and they get assigned to numberText and inputText, and the function continues

With guard let, the constant you defined can be used throughout the function scope. We can use numberText and inputText constants in the rest of the function. This is counter-intuitive compared to if let, whose constants are only available inside the conditional. However, guard exits the function, and scope, which (hopefully) makes up for the confusion…

Validating The Input Field Text

The inputFieldDidChange() function will be called for every change in the input field, which means for every character the user types in there. We only want to respond to complete input, i.e. the 4 digits that the user enters as the answer to the Add 1 challenge. In other words, we only need to check a result if it’s 4 characters long.

Here’s how. Add the following code to the inputFieldDidChange() function, below the previous code.

guard inputText.count == 4 else {
    return
}

That’s reasonably simple, right? We’re using the guard statement again to “guard that inputText.count equals 4, or else exit. The code will return and exit the function when the number of characters in the inputText string is not 4.

By the way, we could have used an if statement here, but a guard statement is often more descriptive. Here, check out these two:

// Option 1: With guard...
guard inputText.count == 4 else {
    return
}

// Option 2: With if...
if inputText.count != 4 {
    return
}

Both have the same effect, but the “guard that, or else” is much more descriptive. It’s intuitive, even.

Moreover, the guard statement forces you to “transfer control out of scope”, or differently said, to exit the function. A mere conditional doesn’t have this rule, which makes using guard clearer, and more explicit. You can’t accidentally alter the flow of the code, without also changing the guard statement.

Here’s what we got so far:

Getting A Number From A String

In the Add 1 game app, a user needs to “+1” to each of the digits in a number string. We now have a handle on both the number string, and the input string. Both have type String.

If we want to calculate whether the input string is correct, we’ll have to compare each of the digits from the number string agains the input string. If each of the digits is equal to the other, plus one, we have a correct result.

To that end, we need a way to access the individual digits in the number strings. In many programming languages, you can just treat the string as an array of characters, and get, for example, the third character like this: inputText[2]. We’d be able to check whether a pair of digits is correct with something like this:

if inputText[n] == numberText[n] + 1 {
    // This pair of digits is correct, it's "+1"
}

Unfortunately, dealing with strings in Swift is not that easy. For a number of reasons, most notably that strings have a variable-width encoding, you can’t randomly access any character in a given string in Swift, without some more work.

To make the GameViewController code as simple and clear as possible, we’re going to add another function to our extension of String. This function will accept an integer as input, and it’ll produce the number at that position in the string.

First, make sure you’ve opened String.swift in Xcode, via the Project Navigator, and then add the following code to the String extension:

func integer(at n: Int) -> Int
{
    let index = self.index(self.startIndex, offsetBy: n)

    return self[index].wholeNumberValue ?? 0
}

What’s going on here?

  • First, it’s important to understand that the function integer(at:) accepts an integer as input, and produces an integer as output.
  • Then, the self in the function refers to the current string. We can call the function integer(at:) on any String value, after which the function will use that string we call it on.
  • Then, we use the index(_:offsetBy:) function to get an index of a position in the string. Swift will count from the beginning of the string to n, and create an index for the position under n.
  • Then, we use that index to get the character from the string. We use the wholeNumberValue property of this character to get the integer number this character represents, such as integer 1 for character "1".
  • Finally, that integer is returned. When no integer is represented, we return 0.

Pfew! That’s quite some complex code, for something so simple. There are 2 things you need to understand here:

  • We need to work with that index, because Swift needs an index to get a character from a string. Because of the variable-width strings, Swift needs to “count” which number is the nth character in a string. We can then use the index to get that character.
  • From the context of our game, we can assert that wholeNumberValue will always return an integer number. After all, we’re working with strings of numbers! When the user accidentally inputs a text character, like "a", we regard that as 0, which is always wrong.

Let’s move on!

Note: The integer(at:) function is only meant for the Add 1 game app! Don’t add it to your own, other app project, unless you know how it works and what its consequences for your code are.

Checking If The Input Is Correct

Finally, we’re getting to the hairy part of the Add 1 game app: checking if a user’s input is correct. How are we going to figure out if their +1’s are right?

Here’s the basic algorithm:

  • The starting point is going to be that the input is correct, unless we find one incorrect digit. That’s a digit for which the input is not the same as the given number, plus one.
  • We’re going to iterate over each of the 4 digits, grab the digit from the input number and the given number, and compare ’em.
  • We’ll need to use a quick trick to make sure that 9 is compared to 0, and not 10.
  • When we find that a digit is incorrect, we quit the comparison and subtract one from the score. When we find that all digits are correct, we add one to the score. Easy!

This approach makes sense, because the user only needs to make one mistake for the entire input to be incorrect. That’s why we’re starting out with a “correct” result, until we find otherwise.

First, add the following code below the last guard statement:

var isCorrect = true

Simple, right? This variable is going to keep track of whether the result is correct. It’ll start out at true.

Below that, add the following for loop:

for n in 0..<4
{

}

This loop iterates 4 times. For each of the iterations, we get access to a constant n which contains the index of that iteration. Based on the 0..<4 range, we can assert that the loop runs over indices 0, 1, 2 and 3. We’ll use the index n to grab the right digit from inputText and numberText.

Add the following code inside the for loop:

var input = inputText.integer(at: n)
let number = numberText.integer(at: n)

This code uses the String extension we created earlier. Thanks to the integer(at:) function, we can now get the integer number at a position in the strings. Internally, that function uses string indices and the wholeNumberValue property to grab the integer from the string.

It’s important to keep track of variable and constant types in Swift. Can you figure out that, in the above code, input has type Int, and inputText has type String?

At this point we can compare the digits input with number, but there’s one special case we need to take in account. What happens when number, i.e. a digit from the given number, is 9?

Based on the principle of Add 1, the input digit then needs to be 10. However, you can’t type double digit numbers into the input field. On top of that, we expect the user to input 0 for 9, in a circular fashion. How do we deal with that?

Add the following code below the previous code, inside the for loop:

if input == 0 {
    input = 10
}

What’s going on here?

  • We know that the input digit 0 is the actual right answer for the given number digit 9.
  • We also know that input number 10 is the right answer for given number digit 9, based on the Add 1 principle that input == digit + 1 is a correct answer.
  • In other words, when input is 0, we’re deliberately changing input to 10, so that 10 = 9 + 1 results in a correct digit, because 0 is the right answer for 9!

Moving on! Add the following code below the previous code, inside the for loop:

if input != number + 1 {
    isCorrect = false
    break
}

What does this code do? It’s an if statement that checks if input is not equal to number + 1. When it’s not equal, we know that the entire input is incorrect. And so we set isCorrect to false.

The break statement is a small optimization that’ll stop the for loop. There’s no need to continue with the next digits, because the complete answer is already incorrect.

Why are we checking whether input is wrong, instead of right? Remember that we started out with isCorrect = true. We’re assuming that an answer is right, until we prove it’s not. We iterate over the digit pairs, and set isCorrect to false when we encounter an incorrect digit. When we don’t find any wrong digits at all, isCorrect will remain true.

Finally, add the following code below the for loop, so outside of it:

if isCorrect {
    score += 1
} else {
    score -= 1
}

updateNumberLabel()
updateScoreLabel()
inputField?.text = ""

The above block of code will first evaluathe the isCorrect boolean. Like this:

  • If isCorrect is true, we add 1 to the score variable, with +=
  • If isCorrect is false, we subtract 1 from the score variable, with -=

Then, in the second part of the above code, we’re calling functions to update the number label with a new randomly generated string, and we update the score label with the new score. Finally, we’re emptying the input field, so the user can add a new answer in it.

The a += b operation is the same as a = a + b. The a -= b operation is the same as a = a - b. In the above code, we’re increasing or decreasing score by 1. In Swift, the a++ and a-- operators don’t exist!

Learn how to build iOS apps

Get started with iOS 13 and Swift 5

Sign up for my iOS development course, and learn how to build great iOS 13 apps with Swift 5 and Xcode 11.

Coding The Countdown Timer

You can just keep playing the Add 1 game as it is right now, which is no fun. We need some boundaries! Who can score the most points in 60 seconds? The app needs a timer.

In Swift, you can use the Timer component to take some action at a given interval. We’re going to write some code that counts down from 60 to 0, by subtracting 1 every second. Let’s get started!

First, we’re going to add 2 new properties to the top of the GameViewController class. Like this:

var timer:Timer?
var seconds = 60

Awesome. As you’ve guessed, the timer property is going to hold on to a reference to the Timer object, and we’ll use seconds to keep track of the remaining time for the game. Note that the timer property is an optional, so it can be nil or contain a Timer object.

Next up, we’re going to add a new function called updateTimeLabel(). Add the following function to the GameViewController class:

func updateTimeLabel() {

    let min = (seconds / 60) % 60
    let sec = seconds % 60

    timeLabel?.text = String(format: "%02d", min) + ":" + String(format: "%02d", sec)
}

What’s up with that?

  • First, we’re calculating how many minutes and seconds are remaining, and assign that to the min and sec constants.
  • Then, with the String(format:) initializer, we’re using the special string format %02d to format the min and sec integer values as zero-padded strings. That way, 7 seconds is written as 07, with a 0 in front.
  • Finally, the strings are concatenated with + and assigned to the timeLabel.

Before you move on, quickly add a call to the updateTimeLabel() function to the viewDidLoad() function. That way, the time label is updated at the start of the game. Like this:

override func viewDidLoad()
{
    super.viewDidLoad()

    updateScoreLabel()
    updateNumberLabel()
    updateTimeLabel()
}

Then, last but not least, add the following code to the end of the inputFieldDidChange() function. So, before the closing squiggly bracket }, but after inputField?.text ···.

if timer == nil {
    timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
        if self.seconds <= 60 {
            self.seconds -= 1
            self.updateTimeLabel()
        }
    }
}

Let’s take a closer look at that bit of code. Here’s what’s going on:

  • The Timer.scheduledTimer( ··· ) call creates and starts a timer. Its time interval is 1.0 second, and it will repeat (every second). The Timer object that’s created, is assigned to the timer property.
  • The last argument of the scheduledTimer( ··· ) is a closure. It’s a block of code that is executed when the timer fires, so every second.
  • Inside the closure, we’re checking if the seconds property is less than or equal to 60. When it is, we decrement score by 1, and call updateTimeLabel(). This will make the time label count down to zero.

But, there’s more… On the first line, on the outermost level of the code, we’re checking if timer equals nil. This is on purpose. Keep in mind that the above code, because of where it’s written, is called when the user’s input is evaluated, in inputFieldDidChange().

When the app starts, timer is nil. The first time a user inputs an answer, and inputFieldDidChange() is called, the above code block executes, because timer is nil. That means the timer will only start counting after the user has entered their first answer!

On subsequent answers, unless timer is nil again, nothing happens, because timer isn’t nil anymore. The timer will keep firing every second though, because the timer’s closure runs independently from the inputFieldDidChange() code. Before you continue, make sure you understand how the code works!

The self in the closure is a reference to the current instance of the GameViewController. Closures that use properties from the class they’re created in, need to use self to make explicit that they’re strongly referencing the current class. You can read more about that in this article.

Finishing The Game

We’re almost ready! The last thing we’ll need to code, is what happens when the game finishes. When the timer reaches zero, we want to show the score to the user, and reset the game for another match. Let’s work on that!

First, we’re going to write another function. Add the following code to the class:

func finishGame()
{

}

The first thing this function needs to do, is to reset the timer. After all, the game is done, so the countdown can stop. We do that like this:

timer?.invalidate()
timer = nil

Make sure to add the above code to the function. It’ll first invalidate the timer, which means to stop it firing, and to release its closure. Then, the timer property is set to nil.

We already know from the code in inputFieldDidChange(), that when timer is nil, it’ll start again when the user inputs their first answer. So, this is exactly how we need to “reset the board” to its initial state.

Next, add the following code to the function, below the previous lines of code:

let alert = UIAlertController(title: "Time's Up!", message: "Your time is up! You got a score of \(score) points. Awesome!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK, start new game", style: .default, handler: nil))

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

The above code creates a UIAlertController, which is a popup dialog that shows a bit of text and a button. It’ll tell the user their score, and prompts them to start another game. Finally, the alert object is shown on screen with the present(_:animated:completion:) function call.

Then, add the following code to the function, below the other lines:

score = 0
seconds = 60

Familiar lines of code, right? We’re resetting the score and seconds properties to their initial states. Because the score is shown to the user in the alert dialog, we can safely reset it to 0.

And finally, add these lines of code to the end of the function:

updateTimeLabel()
updateScoreLabel()
updateNumberLabel()

These are the 3 functions we coded earlier. They’ll show the time and score in their respective labels. The updateNumberLabel() call will generate a new random 4 digit number – and we’re done!

The last step we’ll need to take, is putting the finishGame() function to use. Where do we need to add a call to it? We already know that the game is finished when the timer reaches zero, so… We’ll need to adjust the code of the timer!

First, make sure you’ve located the following block in your code:

if self.seconds <= 60 {
    self.seconds -= 1
    self.updateTimeLabel()
}

Next, what we want, is to merge the above code with something like this:

if self.seconds == 0 {
    self.updateTimeLabel()
}

How do we do that? Well, first off, it’s important to understand that there’s overlap between the 2 conditionals. When seconds is 0, it matches both “less than or equal to 60” and “is zero”. Like this:

let seconds = 0

if seconds <= 60 {
    // This is true
}

if seconds == 0 {
    // This is also true
}

Can we just add an else if clause to the conditional? Like this:

if seconds <= 60 {
    // Is this true?
} else if seconds == 0 {
    // Or is this true?
}

This won’t work. When we add if seconds == 0 below the other conditional, it’ll never execute. Because conditionals are evaluated top-to-bottom, if seconds is zero, it’ll always match the first clause in the conditional! And our seconds == 0 code wouldn’t execute.

What’s the solution? We add it on top! That may seem counter-intuitive, but it’s a great solution. Change the timer’s closure to reflect the following:

timer = ··· { timer in

    if self.seconds == 0 {
        self.finishGame()
    } else if self.seconds <= 60 {
        self.seconds -= 1
        self.updateTimeLabel()
    }
}

Here’s how that works:

  • When seconds is 0, the finishGame() function is called
  • When seconds is less than or equal to 60, seconds is decreased by 1, and updateTimeLabel() is called

Neat! There’s no conflict. And of course, if you’re curious, try both approaches, and see for yourself which one works OK.

You’ll have to ignore those self. references for a bit – they don’t make the code easier to read. A future version of Swift may remove the requirement for explicit calls to self, because in the above code block, it doesn’t have any benefits.

Learn how to build iOS apps

Get started with iOS 13 and Swift 5

Sign up for my iOS development course, and learn how to build great iOS 13 apps with Swift 5 and Xcode 11.

Wrapping Up

We’re done with the app! AWESOME! Run your app with Command + R or by pressing the Play button in Xcode. Does it run OK? Well done!

This wouldn’t be a tutorial, without a challenge. See if you can change the game and its mechanics to solve these challenges:

  1. Give users 2 minutes, or 120 seconds, of game time. (You do need to change a conditional somewhere, too.)
  2. Adjust the scoring mechanism: the time decreases twice as fast when your score is above 10. (Is that even possible?)
  3. Show a thumbs up or down graphic when a user inputs a number, to show whether they got it right or not. (You get extra points for fireworks!)
  4. Incorporate something like GameKit into the app, for score leaderboards. Who’s got the highest score?

Want to learn more? Check out the following articles and tutorials:

Reinder de Vries

Reinder de Vries

Reinder de Vries is a professional iOS developer. He teaches app developers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed a few dozen apps for iOS, worked for global brands and lead development at several startups. When he’s not coding, he enjoys strong espresso and traveling.

×

Build great iOS apps
Learn how in my free 7-day course

  • This field is for validation purposes and should be left unchanged.

No spam, ever. Unsubscribe anytime. Privacy Policy