Swift by Example

By Giordano Scalzo
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies

About this book

When Apple announced Swift at the WWDC, the iOS developer community became excited about the opportunities to improve the way in which they build iOS apps. Swift is a user-friendly language with a smooth learning curve; it is safe, robust, and flexible, and it introduces new ways to solve old problems.

Swift by Example is a fast-paced, practical guide that shows you how to develop iOS apps using Swift. Through the development of six different apps, you'll learn how to use either the right feature of the language or the right tool to solve a given problem.

By the end of the book you will be able to build well-designed apps, effectively use AutoLayout, and develop a video game.

Publication date:
June 2015
Publisher
Packt
Pages
284
ISBN
9781785284700

 

Chapter 1. Welcome to the World of Swift

Swift is a language so new that even most programming experts have barely a few months of experience in it. However, it borrows most of its features from several other programming languages, such as Ruby, Python Scala, Rust, Groovy, and even JavaScript and Haskell. So, anyone who approaches Swift will already feel at home, recognizing the patterns and features of their favorite programming languages.

Moreover, unlike Objective-C, whose learning curve is really steep for beginners, Swift is really friendly for newcomers, who can write code once they learn the basics of the language.

Nevertheless, mastering Swift when using its more advanced features, such as effectively integrating patterns of functional programming with object-oriented concepts, takes time, and most best practices still need to be discovered.

Also, Swift's language is just one part of the story. A lone language without an environment where it can build something is just a sterile exercise. Swift is not a general-purpose language, but a language with a specific goal—building apps for iOS and OS X using the Cocoa framework.

It's in this framework that the complexity resides; Cocoa is a very big framework, with thousands of APIs and different patterns and best practices. It has changed significantly over the course of its several releases, for example, moving from the delegate pattern to the use of blocks to make components interact with loose coupling.

More than knowing the language, the real challenge is in knowing the framework. I want to stress that the aim of this chapter is just to help you get the first grasp of what Swift's constructs look like, and not to be exhaustive, so expect to find a certain degree of simplification. Also, be aware that a deeper knowledge of the language can be achieved with books that specialize only in Swift learning, whereas the goal of this book is to teach you how to build apps using Swift.

 

The first look at Swift


The most obvious way to describe Swift is to compare it with Objective-C, which was the reference programming language for building Cocoa apps. Objective-C is an object-oriented programming language with similarities to dynamic languages, such as Ruby or Python. It is built on top of C, to which Apple has added features to make it modern, such as blocks, properties, and an Automatic Reference Counter (ARC) to manage the memory.

Swift is an object-oriented programming language with some functional programming characteristics. It aims to flatten the learning curve for the beginner, and to also provide more advanced features for the expert, adding more checks at runtime that could help make apps safer.

Objective-C is a loosely static-typed language; every variable must have a type, but it's possible to define a variable using the id type, reaching a sort of dynamic typing, where the type is evaluated at runtime. Thanks to its powerful runtime environment, it's possible to change the structure of a class—for example, add a method or variable—at runtime. This makes Objective-C a really flexible language, but it is also difficult to manage and prone to creating subtle bugs that are difficult to catch at runtime.

Swift is a strong static-typed language. This means that the type of a variable must be set and is evaluated at compile time. It also lacks any kind of metaprogramming at runtime, but this sternness, added to the functional patterns it supports, should help programmers eliminate an entire class of bugs, allowing apps to be more robust in a faster way.

However, the best way to learn a language is to play with it, and Xcode 6 has brought forth a really nice way to do it.

Let's go to the playground

Usually, the only way to experiment and learn a language until Xcode 5 was by creating a new app and writing code inside any method of that app. Then, you would compile and run it, reading the log or stopping using the debugger.

Xcode introduced the concept of a playground, which isn't an app or a program to be built, but a source file that is constantly compiled and evaluated every time it changes.

Xcode 6 can be downloaded for free from the Mac App Store at www.appstore.com/mac/Xcode. Once it is installed, go to File | New | Playground, as shown in this screenshot:

Without changing anything, you have created your first Swift program! The following screenshot shows our first program:

The playground is split into two: to the left is our code, and to the right is the evaluation of the code on the left-hand side.

If we change the string from "Hello, playground" to "Hello World!", as you can see in the following screenshot, the code is compiled and run without the need to select anything from the menu. This is because the compilation is triggered by the saving operation.

If we make an error, for example, by removing the closing quote from the string, the left part presents a red dot. This dot shows the error type when we click on it. Notice that the right part still presents the result of the previous run. This screenshot displays how the playground shows an error:

With the println() function, it is possible to print messages on a debug console, which can be opened by going to View | Assistant Editor | Show Assistant Editor, as shown in the following screenshot:

The console is basically another view inside the evaluation window, as you can see in this screenshot:

There is much more to learn about playground, but even with this much knowledge, we can dive into Swift without further ado.

The building blocks – variables and constants

As already said, Swift is a strong typed language, which means that every variable must be declared with the type it holds:

let name: String = "Paul"
let age: Int = 27

Using the let keyword, we define a constant—a variable that cannot change its value—and, as in math, the constant becomes the identity of the value itself. The following screenshot shows what the console looks like when we try to change the constant after we have defined it:

To define a variable, we can use the var keyword:

var name: String = "Paul"
var age: Int = 27
name = "John"
age = 29

We can change the value of a variable, paying attention to set a new value of the same kind. Otherwise, an error will be raised, as shown in this screenshot:

Speaking of type declaration, Swift is smarter than just requiring the type of a variable. If the value of the variable is set during its declaration, Swift can understand the type without the need for an explicit type. This feature is called type inference, and it allows us to create more concise code. For example, we can write code like the following:

let bassPlayer = "Paul"
let bassPlayerAge = 27
let guitarPlayer = "John"
let guitarPlayerAge = 29

Obviously, the type is mandatory if a variable is declared without being initialized:

var bassPlayer: String
var bassPlayerAge: Int
var guitarPlayer: String
var guitarPlayerAge: Int

Because it's really difficult to track all the changes made to a mutable variable, it is good practice to use constants as much as we can, and use variables only to contain the status in a small and well-defined scope in which it's easy to understand whether the code is correct or not.

Collect variables in containers

A variable is the minimum information that we can handle, but, sometimes, it is useful to group variables together. Swift provides three types of containers for this purpose: tuple, array, and dictionary.

A tuple is a limited set of heterogeneous values, like this, for example:

let bassPlayer = (name: "Paul", surname: "McCartney", age: 27)

In the declaration of a tuple, each piece of information is separated by a comma (,), each variable is a name-value pair separated by a colon (:), and all the elements are surrounded by a pair of parentheses.

To access the elements of a tuple, it is possible to use a dot notation, specifying the name of a variable:

bassPlayer.name    // Paul
bassPlayer.surname // McCartney
bassPlayer.age     // 27

A tuple can also be defined as an unnamed collection, that is, without specifying the names of the elements:

let bassPlayer = ("Paul", "McCartney", 27)

In this case, to access the elements, we must use their positions inside the tuple:

bassPlayer.0 // Paul
bassPlayer.1 // McCartney
bassPlayer.2 // 27

It is also possible to unwrap the values of a tuple and use them in simple external values, assigning each value inside the tuple to specific variables:

let bassPlayer = ("Paul", "McCartney", 27)
let (name, surname, age) = bassPlayer
println(name)
println(surname)
println(age)

An array is an unnamed list of homogeneous values:

var band = ["Paul", "John"]

An array has a number of elements. These elements can be asked for using the count property:

band.count // 2

Each element can be accessed using square brackets ([]) around the index of the value:

band[0] // Paul
band[1] // John

Just as in a tuple, the first index starts from 0.

Unlike Objective-C, where containers have mutable and immutable implementation, in Swift, it depends on the way in which the variable is declared—with let or with var.

If an array is mutable, we can change the value at a particular index, but we can also add elements using the append method:

band.append("George")
band.append("Ringo")

Moreover, using the sum operator (+), it is possible to create a new array that contains all the elements of the two previous arrays:

let theBeatles = band + ["George", "Ringo"]

The third container Swift provides is a dictionary, which is a sort of named-index array. Its syntax is similar to that of a tuple—using a name-value list separated by commas and surrounded by square brackets:

var band = ["bass": "Paul", "guitar": "John"]

Each value can be reached using the key inside the square brackets:

band["bass"]   // Optional("Paul")
band["guitar"] // Optional("John")

The value retrieved is not exactly the value we inserted during the initialization, but it is wrapped by an optional, which means that the result can be either a real value or nil. For example, if we use a key that is not present, the value returned is nil:

band["keyboard"] // nil

We'll see optionals later in this chapter. For the moment, it's enough to know that to extract the value from an optional, we must use the exclamation mark (!). Pay attention: you must do this only if you are sure that a value is inside an optional value. Otherwise, a runtime error will occur, as shown in this screenshot:

Controlling the flow

The most basic construct used to control the flow is the conditional check, which executes a piece of code if the condition provided is true:

var name = "Jim"
if name == "Paul" {
    println("Let's play the bass")
} else if name == "John" {
    println("Let's play the guitar")
} else if name == "George" {
    println("Let's play the sitar")
} else if name == "Ringo" {
    println("Let's play the drums")
} else {
    println("What do you want to play?")
}

The parentheses around the condition are optional, but the curly braces are required, even in the case of a single statement.

The switch block in Swift is more powerful than in other languages. It is a nicer way of writing a chain of if statements:

var name = "Jim"
switch name {
case "Paul":
    println("Let's play the bass")
case "John":
    println("Let's play the guitar")
case "George":
    println("Let's play the sitar")
case "Ringo":
    println("Let's play the drums")
default:
    println("What do you want to play?")
}

Whereas other languages' switch constructs handle-only integers, in Swift, we can have different types of conditional variables.

The list of possible values must be exhaustive, and, in this case, a default case must be provided.

A case block is executed until the entered variable's value matches the case. Swift is smart enough to break a case block on completion, so you don't have to explicitly break out of the switch at the end of case's code.

If you want the same behavior of case in Objective-C, which means continuing if there is no break command before the next case statement, you must add the fallthrough keyword, as shown here:

As said earlier, switches are more than this, but we'll see better when we implement the apps.

Until now, we have created only linear code, without jumping around or going back. It's now time to introduce the loop constructs provided by Swift. A loop is a statement that allows a block of code to be executed repeatedly, controlled by an exit condition.

The most basic kind is the while loop, where the loop is executed if a condition is true, as depicted in this screenshot:

To illustrate the while loop, we introduce the string format, which is a handy way to insert a part of code to evaluate inside a string—using a backslash (\) followed by parenthesis, (). The contained element is evaluated and the result replaces the expression. In other programming languages, this is called interpolation.

Another kind of loop is fast enumeration, which permits to iterate through an array without using an index, but by accessing the values straightaway, as shown in the following screenshot:

If we want to fast-enumerate through an array, and have also provided the index of the item, we can use the enumerate function. The following screenshot shows the use of enumerate, which basically returns an array of tuples containing the index and the value:

Transforming the values using functions

Swift is a multiparadigm language that mixes object-oriented programming with functional patterns.

The former organizes the code around objects, which are constructs with variables and functions in imperative way. This means telling the software how to execute the instructions one after the other. The latter defines the structures and elements of code as an evaluation of functions in a declarative way, which means defining what the elements are instead of how the elements behave.

These two paradigms—apparently opposite—give more flexibility to the developer, who can leverage one or the other, depending on the context.

In Swift, functions are first-class citizens, which means that they can be assigned to variables, or they can be passed as either parameters or return values of other functions.

A function in Swift is a named block of instructions that can be initialized, executed, passed as a parameter, or returned as a return value.

A function is declared using the func keyword and by enclosing the code to be executed around curly braces ({}):

func greet() {
    println("Hello, world!")
}
greet() // Hello, world!

In Swift, a function can be declared in anonymous way; in this case, it is called a closure:

let greet = {
    println("Hello, world!")
}
greet() // Hello, world!

A function can have a parameter, which is defined inside parentheses:

func greet(name: String) {
    println("Hello, \(name)!")
}
greet("Jim") // Hello, Jim!

When a function is defined as a closure, the parameters are inside the open curly brace and the in keyword separates them from the block of instructions:

let greet = { (name: String) in
    println("Hello, \(name)!")
}
greet("Jim") // Hello, Jim!

A function can return a value, which is defined using the arrow (->) in the declaration:

func greet(name: String) -> String {
    return "Hello, \(name)!"
}
println(greet("Jim")) // Hello, Jim!

In a consistent manner, the closure defines the return value after the parameters:

let greet = { (name: String) -> String in
    return "Hello, \(name)!"
}
println(greet("Jim")) // Hello, Jim!

A function can have more than one parameter:

func greet(name: String, greeting: String) -> String {
    return "\(greeting), \(name)!"
}
println(greet("Jim", "Hi")) // Hi, Jim!

As we can see from this example, the parameters during the call are passed in a positional way; this could be a source of confusion in certain functions where it isn't easy to understand or remember the role of the parameters:

func greeting(surname: String, firstname: String) -> String {
    return "My name is \(surname), \(firstname) \(surname)"
}

greeting("James", "Bond") //My name is James, Bond James

In this case, prepending the hash character (#) to the name of the parameter in the declaration of the function and labels during calls become mandatory:

func greeting(#surname: String, #firstname: String) -> String {
    return "My name is \(surname), \(firstname) \(surname)"
}

greeting(surname: "Bond", firstname: "James") //My name is James, Bond James

It's important to note, as we'll see when we'll discuss classes, that when a function is defined in the context of a class, all the labels apart from the first are mandatory, without the need to add the hash character (the first label is optional for compatibility with Cocoa's naming convention, where the name of the method contains the label of the first).

Structs – custom compounds type

Earlier in this chapter, we saw how to group variables using tuples; starting from this concept, Swift offers a way to create complex custom types through structs.

A struct is a container of different elements, with the possibility to add functions to manipulate them.

Starting from the example we used for the tuple, we can define a struct in this way:

struct Player{
    let name: String
    let surname: String
    let age: Int
    let instrument: String
}

let bassPlayer = Player(name: "Paul", surname: "McCartney",
    age: 27,
    instrument: "bass")
let guitarPlayer = Player(name: "John", surname: "Lennon",
    age: 29,
    instrument: "guitar")

We can access the elements of a struct using the dot notation that we used for the named tuple:

guitarPlayer.name       // John
guitarPlayer.instrument // guitar

This form doesn't seem much different from a tuple, but the structs are more powerful than this.

For example, we can add a function inside the struct:

struct Player{
    let name: String
    let surname: String
    let age: Int
    let instrument: String
    func fullname() -> String{
        return "\(name) \(surname)"
    }
}
bassPlayer.fullname()  // Paul McCartney

One of the basic principles of functional programming is to have functions that deal only with immutable elements—they receive immutable objects and return immutable objects.

In this way, the mutable state is not shared in different places of the program, adding complexity to the code because a variable can be mutated in different places.

The struct construct was created with this principle in mind: to enforce immutability. When a struct variable is assigned to another variable, it is assigned by copy. This means that a new struct is created with the same values as the previous struct. The same happens when a struct is passed as a function argument. The nature of the struct is also known as the ValueType.

On the contrary, classes (which we'll see in the next section) are passed by reference. This means that only the address of the object is copied and the variable points to the same object.

As just mentioned, although it is better to have immutable structs, it's possible to define variables inside a struct, making it possible to change their values:

struct Player{
    var name: String
    var surname: String
    var age: Int
    var instrument: String
    func fullname() -> String{
        return "\(name) \(surname)"
    }
}

var guitarPlayer = Player(name: "John", surname: "Lennon",
    age: 29,
    instrument: "guitar")
guitarPlayer.fullname()             // John Lennon
guitarPlayer.name  = "Joe"
guitarPlayer.surname  = "Satriani"
guitarPlayer.fullname()            // Joe Satriani

As already said, a struct is a container of elements; these elements are called properties. Other related properties can be created starting from already defined properties.

A struct in Swift offers the mechanism of computed properties to create related properties. These are basically functions disguised as properties:

struct Player{
    var name: String
    var surname: String
    var age: Int
    var instrument: String
    var fullname: String {
        return "\(name) \(surname)"
    }
}

var guitarPlayer = Player(name: "John", surname: "Lennon",
    age: 29,
    instrument: "guitar")

println(guitarPlayer.fullname) //John Lennon

Note that from a caller point of view, a computed property is indistinguishable from a defined property, so it's also possible to define a way to change it:

import Foundation

struct Player{
    var name: String
    var surname: String
    var age: Int
    var instrument: String
    var fullname: String {
        get { return "\(name) \(surname)" }
        set(newFullname) {
          let names = newFullname.componentsSeparatedByString(" ")
          name = names[0]
          surname = names[1]
        }
    }
}
var guitarPlayer = Player(name: "John", surname: "Lennon",
    age: 29,
    instrument: "guitar")

guitarPlayer.fullname = "Joe Satriani"
println(guitarPlayer.name)    //Joe
println(guitarPlayer.surname) //Satriani

There are a few things to talk about in this snippet.

First of all, we needed to use import Foundation to use the componentsSeparatedByString method, which creates an array of elements, splitting the string using the parameter string as a separator.

Inside the definition of the computed property, we defined two functions: a getter (get), which is the same code that we used in the previous example, and a setter (set), which accepts a string as parameter. In the function body, split the parameter in tokens, separated by an empty space, and assign the first value to name and the second to surname.

As already mentioned, a struct is a ValueType in Swift, such as an integer, a string, an array, and so on. This means that an instance of a struct is copied when assigned to a new variable or passed as a parameter:

struct Player{
    var name: String
    var surname: String
    var age: Int
    var instrument: String
}

var originalPlayer = Player(name: "John", surname: "Lennon",
    age: 29,
    instrument: "guitar")

var newPlayer = originalPlayer
newPlayer.name = "Joe"
newPlayer.surname = "Satriani"

originalPlayer.surname // Lennon
newPlayer.surname      // Satriani

A struct is also copied when it is passed a parameter in a function:

var originalPlayer = Player(name: "John", surname: "Lennon",
    age: 29,
    instrument: "guitar")

func transformPlayer(var player: Player) -> Player {
    player.name = "Joe"
    player.surname = "Satriani"
    return player
}

var newPlayer = transformPlayer(originalPlayer)

originalPlayer.surname // Lennon
newPlayer.surname      // Satriani

This knowledge of structs is enough to start using them efficiently.

Classes – common behavior objects

If you already know another object-oriented programming language, you might be wondering whether there are classes in Swift, and, if so, why we haven't them introduced earlier.

Of course there are! In the end, the main purpose of Swift is to create iOS or OS X apps using Cocoa, which is an object-oriented framework.

Nevertheless, with Swift being a multiparadigm programming language, classes are no longer the central concepts around which everything is built, as in object-oriented languages. However, they are a way to encapsulate the business logic.

Let's explore classes by altering the previous example to use classes instead of structs:

class Player{
    var name: String
    var surname: String
    var age: Int
    var instrument: String
    init(name: String, surname: String, age: Int, instrument: String){
        self.name = name
        self.surname = surname
        self.age = age
        self.instrument = instrument
    }
}

var originalPlayer = Player(name: "John", surname: "Lennon",
    age: 29,
    instrument: "guitar")

Basically, instead of the struct keyword, we used class, and we also needed to provide an initializer with all the parameters to initialize the instance (a constructor is a method called when the object is instantiated and initialized).

At first sight, it seems that class and struct are the same construct, but, in reality, the difference is substantial and relative to the nature of the two constructs.

The main difference is that an instance of a class is copied by reference. This means that the object isn't copied, but the reference of the object is copied, so when we change the new object, we are changing the original object as well.

Let's convert the example of the structs using a class:

var originalPlayer = Player(name: "John", surname: "Lennon",
    age: 29,
    instrument: "guitar")

func transformPlayer(var player: Player) -> Player {
    player.name = "Joe"
    player.surname = "Satriani"
    return player
}

var newPlayer = transformPlayer(originalPlayer)

originalPlayer.surname // Satriani
newPlayer.surname      // Satriani

We can see in the log of the playground that the function changed originalPlayer as well.

The other main difference is that a class supports inheritance. This means that we can created a specialized version of a class, which is still of the same category as the original class, but has more characteristics:

class Guitarist: Player{
    var guitarBrand: String
    init(name: String, surname: String, age: Int, guitarBrand: String){
        self.guitarBrand = guitarBrand
        super.init(name: name, surname: name, age: age, instrument: "guitar")
    }
}

var alienGuitarist = Guitarist(name: "Joe", surname: "Satriani",
    age: 31,
    guitarBrand: "guitar")

So, a guitarist is basically a player with a guitar.

Note that in the constructor, we need to initialize all the variables of our level (in our case, just one), and then call the parent initializer using the super keyword to continue the chain of initialization.

To help understand when to use a struct or a class, it is often stated to favor the use of structs over classes. When an object represents something concrete (for example, a view or a button), we must use a class. When we need to represent properties or values that don't exist as concrete real things, such as Coordinates or Rect, we must use structs.

Loose coupling with protocols

A good way to tame the complexity of code is to separate what an object does from how it does it.

This is accomplished by defining the interface of an object, namely the properties and the methods of a class or a struct.

If the class or struct adheres to a protocol, it must implement all the methods defined in the protocol:

protocol Playable {
    func play()
}

class Player: Playable{
    
    //...
    
    func play() {
        // use instrument to play
    }
}

This allows us to call the defined methods without knowing the actual value of an instance:

func concert(band: [Playable]){
    for player in band {
        player.play()
    }
}

The concept of protocols is widely used in Cocoa for loose coupling and permitting an object to interact without knowing which kind of implementation it has.

Check the existence of an optionals value

We have already seen optionals when we discussed the dictionary.

The introduction of optionals is a radical, phenomenal change from Objective-C, where it is allowed to call a method on a nil object without crashing the app, and the method call is silently discarded.

It might be handy in several occasions, but it can often create really subtle, difficult-to-track, bugs. For example, if some objects of the UI are not connected to the controller and we try to change their values, we send messages to nil and nothing happens, leaving us without a clue as to what happened.

On the other hand, when we try to insert a nil object into a collection—array or dictionary—the app crashes at runtime.

Swift forces the developer to think of the nature of an element, whether it's always present or whether it could be nil.

An optional is declared using a question mark (?), and to make the code compile, the developer must check whether an optional value is nil before using it.

Also, an optional integer or optional string is not an ordinary integer or string; it's an integer or string wrapped in a container. To extract and evaluate the value inside the container, we must use the exclamation mark (!):

var optionalInt: Int?

if optionalInt != nil {
    let realInt = optionalInt!
    println("The value is [\(realInt)]")
} else {
    println("The value is nil!")
} 

This pattern is so common that Swift allows us to create the unwrapped variable during the nil check:

var optionalInt: Int? = 3

if let realInt = optionalInt {
    println("The value is [\(realInt)]")
} else {
    println("The value is nil!")
}

As a good rule, it's recommended to use an optional as little as you can in your code, and to always check whether a variable is nil before using it.

Enumerations on steroids

Enumerations are common constructs in several programming languages, but in Swift, they are really powerful.

They are used when we have a limited and well-defined set of possible values, for example, the code responses for HTTP, or the suits of a card game.

While you can have only numeric-based enumerations in Objective-C, in Swift, enumerations can also be implemented with String:

enum Instrument: String {
    case Guitar = "guitar"
    case Bass = "bass"
    case Drums = "drums"
    case Sitar = "sitar"
    case Keyboard = "keyboard"
}

Using this enumeration, we can define a variable:

let instrument = Instrument.Drums

In this case, the constant infers the type from the initialization, but it is also possible to declare the type and using an abbreviated version of the value:

let instrument: Instrument = .Drums

Because the constant is an instrument, the compiler is expecting a value of the enumeration to assign to it, and it becomes superfluous when declaring the kind of enumerations on the right side.

We have already seen the switch construct, and it's really useful with enumeration, and in such a case, a statement contains a value of the enumeration:

let instrument: Instrument = .Drums

switch instrument {
case .Guitar:
    println("Let's play guitar")
case .Bass:
    println("Let's play bass")
case .Drums:
    println("Let's play drums")
case .Sitar:
    println("Let's play sitar")
case .Keyboard:
    println("Let's play keyboard")
}
// Let's play drums

As previously mentioned, the cases of a switch must be exhaustive, and all possible values must have a case; this enforces Swift to eliminate, as much as it can, the chances of introducing bugs because of distraction or superficiality. For every case, as in optionals, the developer is forced to pay attention and make a decision, which can be wrong of course, but at least it's not because he forgets to test a condition.

A really advanced feature of enumerations in Swift is the possibility to associate values with members. For example, we can add the number of strings for guitar and the brand for keyboard:

let keithEmersonInstrument: Instrument = .Keyboard("Hammond")
let steveVaiInstrument: Instrument = .Guitar(7)
let instrument = steveVaiInstrument

switch instrument {
case .Guitar(let numStrings):
    println("Let's play a \(numStrings) strings guitar")
case .Bass:
    println("Let's play bass")
case .Drums:
    println("Let's play drums")
case .Sitar:
    println("Let's play sitar")
case .Keyboard(let brand):
    println("Let's play a \(brand) keyboard")
}

// Let's play 7 strings guitar

Here, you can see that to extract the value from a value, we need to use the binding inside the case.

Enumerations are more powerful than what we have seen in this section, but this is enough to understand their power, which, when linked with the features of switch statements, make them one of most important additions to Swift.

 

A Guess the Number app in Swift


As mentioned in the introduction of this chapter, learning a language is just half of the difficulty in building an app; the other half is the framework. This means that learning a language is not enough. In this part of the chapter, we'll implement a simple Guess the Number app, just to become familiar with Xcode and part of the CocoaTouch framework.

The app is…

Our first complete Swift app is a Guess the Number app—a classic educational game for children where the player must guess a number generated randomly by the app.

For each guess, the app tells the player whether the guess is greater or lower than the generated number (also called the secret number).

Before diving into the code, we must define the interface of the app and the expected workflow.

This game presents only one screen, which is shown in the following screenshot:

At the top of the screen, a label reports the name of the app—Guess a Number.

In the next row, another static label with the word between, connects the title with a dynamic label that reports the current range. The text inside the label must change every time a new number is inserted. A text field at the center of the screen is where the player will insert their guess.

A big button, with OK written on it, is the command that confirms that the player has inserted the chosen number.

The last two labels give feedback to the player:

  • Your last guess was too low is displayed if the number inserted is lower than the secret number

  • Your last guess was too high is displayed if it's greater than the secret number

The last label reports the current number of guesses. The workflow is straightforward:

  1. The app selects a random number.

  2. The player inserts their guess.

  3. If the number is equal to the secret number, a popup tells the player that they have won, and shows them the number of guesses.

  4. If the number is lower than the secret number but greater than the lower bound, it becomes the new lower bound. Otherwise, it is silently discarded.

  5. If the number is greater and lower than the upper bound, it becomes the new upper bound. Otherwise, it's, again, silently discarded.

Building a skeleton app

Let's start building the app - select a new project by going to File | New | Project…, as shown in this screenshot:

The following screenshot shows Xcode asking for the type of app to be created. The app is really simple, so we choose Single View Application:

Before starting to write code, we need to complete the configuration by adding the organization identifier, using the reverse domain name notation, and Product Name. Together, they produce a Bundle Identifier, the unique identifier of the app.

Pay attention to the selected language, which must obviously be Swift. Here is a screenshot that shows you how to fill in the form:

Once done with this data, we are ready to run the app by going to Product | Run, as shown in this screenshot:

After the simulator finishes loading the app, we can see our magnificent creation—a shiny, brilliant white page!

We can stop the app by going to Product | Stop, as shown in the following screenshot:

Now we are ready to implement the app.

Adding the graphic components

When we are developing an iOS app, it is good practice to implement the app outside-in, starting from the graphics.

By taking a look at the files generated by the Xcode template, we can identify the two files that we'll use to implement Guess the Number:

  • Main.storyboard: This contains the graphics components

  • ViewController.swift: This handles all of the business logic of the app

Here is a screenshot that presents the structure of the files in an Xcode project:

Let's start selecting the storyboard file to add the labels.

The first thing we notice is that the canvas is not the same size or ratio as an iPhone and an iPad. To handle different sizes and different devices, Apple (since iOS 5) added a constraints system, called AutoLayout, as a system to connect the graphics components in relative way, regardless of the actual size of the running device.

As Autolayout is beyond the scope of this chapter, we'll implement the created app only for iPhone 6.

After deciding our target device, we need to resize the canvas as per the real size of the device. From the tree structure at the right, we select ViewController, as shown here:

After having done that, we move to the right-hand side, where there are the properties of the ViewController. There, we select the tab containing Simulated metrics, in which we can insert the requested size. The following screenshot will help you locate the correct tab:

Now the size is the expected size, we can proceed to add labels, text fields, and the buttons from the list in the bottom-right corner of the screen.

To add a component, we must choose it from the list of components. Then, we must drag it onto the screen, where we can place it at the expected coordinates.

This screenshot shows the list of UI components, called an object library:

When you add the text field, pay attention to selecting Number Pad as the value for Keyboard Type, as illustrated in the following screenshot:

After selecting values for all the components, the app should appear as shown in the mockup we had drawn earlier, which this screenshot can confirm:

Connecting the dots

If we run the app, the screen is the same as the one in the storyboard, but if we try to insert a number into the text field and then press the button, nothing happens.

This is because the storyboard is still detached from the ViewController, which handles all of the logic.

To connect the labels to the ViewController, we need to create instances of a label prepended with the @IBOutlet keyword. Using this signature, Interface Builder—the graphic editor inside Xcode—can recognize the instances available for connection to the components:

class ViewController: UIViewController {
    @IBOutlet weak var rangeLbl: UILabel!
    @IBOutlet weak var numberTxtField: UITextField!
    @IBOutlet weak var messageLbl: UILabel!
    @IBOutlet weak var numGuessesLbl: UILabel!

    @IBAction func onOkPressed(sender: AnyObject) {
    }
}

We have also added a method with the @IBAction prefix, which will be called when the button is pressed.

Now, let's move on to Interface Builder to connect the labels and outlets.

First of all, we need to select View Controller from the tree of components, as shown in this screenshot:

In the tabs to the right, select the outlet views, the last one with an arrow as a symbol. The following screenshot will help you find the correct symbol:

This shows all the possible outlets to which a component can be connected.

Upon moving the cursor onto the circle beside the rangeLbl label, we see that it changes to a cross. Now, we must click-and-drag a line to the label in the storyboard, as shown in this screenshot:

After doing the same for all the labels, the following screenshot shows the final configurations for the outlets:

For the action of the button, the process is similar: select the circle close to the onOkPressed action, and drag a line to the OK button, as shown in this screenshot:

When the button is released, a popup appears, with the list of possible events to connect the action to.

In our case, we connect the action to the Touch Up Inside event, which is triggered when we release the button without moving from its area. The following screenshot presents the list of the events raised by the UIButton component:

Now, suppose we added a log command like this one:

    @IBAction func onOkPressed(sender: AnyObject) {
        println(numberTxtField.text)
    }

Then, we can see the value of the text field we insert printed on the debug console.

Now that all the components are connected to their respective outlets, we can add the simple code required to create the app.

Adding the code

First of all, we need to add a few instance variables to handle the state:

    private var lowerBound = 0
    private var upperBound = 100
    private var numGuesses = 0
    private var secretNumber = 0

Just for the sake of clarity, and the separation of responsibilities, we create two extensions to the ViewController. An extension in Swift is similar to a category in Objective-C—a distinct data structure that adds a method to the class it extends.

Because we don't need the source of the class that the extension extends, we can use this mechanism to add features to third-party classes, or even to CocoaTouch classes.

Given this original purpose, extensions can also be used to organize the code inside a source file. This could seem a bit unorthodox, but if it doesn't hurt and is useful, why not use it?

The first extension contains the logic of the game:

private extension ViewController{
   enum Comparison{
        case Smaller
        case Greater
        case Equals
    }

   func selectedNumber(number: Int){
}
    
   func compareNumber(number: Int, otherNumber: Int) -> Comparison {
}
}

Note that the private keyword is added to the extension, making the methods inside private. This means that other classes that hold a reference to an instance of ViewController can't call these private methods.

Also, this piece of code shows that it is possible to create enumerations inside a private extension.

The second extension is for rendering all the labels:

private extension ViewController{
    func extractSecretNumber() {
    }
    
    func renderRange() {
    }
    
    func renderNumGuesses() {
}    
    func resetData() {
}    
    func resetMsg() {
}    
    func reset(){
        resetData()
        renderRange()
        renderNumGuesses()
        extractSecretNumber()
        resetMsg()
    }
}

Let's start from the beginning, which is the viewDidLoad method in the case of the ViewController:

    override func viewDidLoad() {
        super.viewDidLoad()
  numberTxtField.becomeFirstResponder()
        reset()
    }

When the becomeFirstResponder method is called, the component called—numberTxtField in our case—gets the focus, and the keyboard appears.

After that, reset() is called:

    func reset(){
        resetData()
        renderRange()
        renderNumGuesses()
        extractSecretNumber()
        resetMsg()
    }
This basically calls the reset method of each component:
    func resetData() {
        lowerBound = 0
        upperBound = 100
        numGuesses = 0
    }
    
    func resetMsg() {
        messageLbl.text = ""
    }

Then, the method is called and is used to render the two dynamic labels:

    func renderRange() {
        rangeLbl.text = "\(lowerBound) and \(upperBound)"
    }
    
    func renderNumGuesses() {
        numGuessesLbl.text = "Number of Guesses: \(numGuesses)"
    }

It also extracts the secret number using the arc4random_uniform function, and performs some typecast magic to align to the expected numeric type:

    func extractSecretNumber() {
        let diff = upperBound - lowerBound
        let randomNumber = Int(arc4random_uniform(UInt32(diff)))
        secretNumber = randomNumber + Int(lowerBound)
      }

Now, all the action is in the onOkPressed action (pun intended):

@IBAction func onOkPressed(sender: AnyObject) { 
    let number = numberTxtField.text.toInt()
  if let number = number {
      selectedNumber(number)
  } else {
   var alert = UIAlertController(title: nil, 
               message: "Enter a number", 
               preferredStyle: UIAlertControllerStyle.Alert)
   alert.addAction(UIAlertAction(title: "OK", 
               style: UIAlertActionStyle.Default, handler: nil))
   self.presentViewController(alert, 
                  animated: true, 
                  completion: nil)
        }
    }

Here, we retrieve the inserted number. Then, if it is valid (that is, it's not empty, not a word, and so on), we call the selectedNumber method. Otherwise, we present a popup asking for a number.

All the juice is in selectedNumber, where there is a switch case:

  func selectedNumber(number: Int){
        switch compareNumber(number, otherNumber: secretNumber){
  //....

The compareNumber basically transforms a compare check into an Enumeration:

    func compareNumber(number: Int, otherNumber: Int) -> Comparison{
        if number < otherNumber {
            return .Smaller
        } else if number > otherNumber {
            return .Greater
        }
        
        return .Equals
    }

Back to the switch statement of selectedNumber—it first checks whether the number inserted is the same as the secret number:

case .Equals:
var alert = UIAlertController(title: nil, 
              message: "You won in \(numGuesses) guesses!",
              preferredStyle: UIAlertControllerStyle.Alert)
            alert.addAction(UIAlertAction(title: "OK", 
              style: UIAlertActionStyle.Default, 
              handler: { cmd in
                self.reset()
                self.numberTxtField.text = ""
              }))
            self.presentViewController(alert, 
              animated: true, completion: nil)

If this is the case, a popup with the number of guesses is presented, and when it is dismissed, all of the data is cleaned and the game starts again.

If the number is smaller, we calculate the lower bound again, and then we render the feedback labels:

        case .Smaller:
            lowerBound = max(lowerBound, number)
            messageLbl.text = "Your last guess was too low"
            numberTxtField.text = ""
            numGuesses++
            renderRange()
            renderNumGuesses()

If the number is greater, the code is similar, but instead of the lower bound, we calculate the upper bound:

        case .Greater:
            upperBound = min(upperBound, number)
            messageLbl.text = "Your last guess was too high"
            numberTxtField.text = ""
            numGuesses++
            renderRange()
            renderNumGuesses()
        }

Et voilà! With this simple code, we have implemented our app.

Note

You can download the code of the app from https://github.com/gscalzo/GuessTheNumber.

Summary

This was a really dense chapter because we squeezed in content, that usually needs at least a book to explain properly, in only tens of pages.

We took a quick look at Swift and its capabilities, starting from the definitions of variables and constants, and then how to define the control flow. After that, we moved on to structs and classes, seeing how they are similar in some ways, but profoundly different as philosophies. Finally, we created a simple game app, showing all the required steps in great detail.

Of course, simply after reading this chapter, nobody can be considered an expert in Swift and Xcode. However, the information here is enough to let you understand all of the code we'll be using in the upcoming chapters to build several kinds of apps.

In the next chapter, we'll continue to explore Swift and iOS by implementing another game—a memory game that will let us make use of the power of structs. You will also learn about some new things in UIKit.

About the Author

  • Giordano Scalzo

    Giordano Scalzo is a developer with 20 years of programming experience, since the days of ZX Spectrum. He has worked in Swift, Objective-C, C/C++, Java, .Net, Ruby, Python, and in a ton of other languages that he has forgotten the names of. After years of backend development, over the past 5 years, Giordano has developed extensively for iOS, releasing more than 20 apps—apps that he wrote for clients, enterprise applications, or on his own. Currently, he is a contractor in London, where he delivers code for iOS through his company, Effective Code Ltd, with the objective being quality and reliability.

    Browse publications by this author