Swift From Scratch: Optionals and Control Flow

In the previous articles, you learned some of the basic concepts of the Swift programming language. If you've programmed before, I'm sure you saw a few similarities with other programming languages, such as Ruby, JavaScript, and Objective-C.

In this article, we zoom in on control flow in Swift. Before we can discuss control flow in more detail, we need to take a look at a concept that is new to most of you, optionals. Optionals are another safety feature of Swift. At first, it may look like a hassle to use optionals, but you'll quickly learn that optionals will make your code much safer.

1. Optionals

We've already seen that a variable must be initialized before it can be used. Take a look at the following example to better understand what this means.

If you're used to working with strings in Objective-C, then you may be surprised that Swift shows you an error. Let's see what that error tells us.

An error

In many languages, variables have an initial default value. In Objective-C, for example, the string in the following code snippet is equal to nil.

However, the concept of nil differs in Swift and Objective-C. We'll discuss nil in more detail a bit later.

What Is an Optional?

Swift uses optionals to encapsulate an important concept, that is, a variable or constant has a value or it hasn't. It's that simple in Swift. To declare a variable or constant as optional, we append a question mark to the type of the variable or constant.

The variable str is no longer of type String. It is now of type optional String. This is important to understand. The result or side effect is that we can no longer directly interact with the value of the str variable. The value is safely stored in the optional, and we need to ask the optional for the value it encapsulates.

Forced Unwrapping

One way to access the value of an optional is through forced unwrapping. We can access the value of the variable str by appending an ! to the variable's name.

It's important that you are sure that the optional contains a value when you force unwrap it. If the optional doesn't have a value and you force unwrap it, Swift will throw an error at you.

Forced unwrapping of an optional

Optional Binding

There is a safer way to access the value of an optional. We'll take a closer look at if statements in a few minutes, but the following example shows how we can safely access the value stored in the variable str, which is of type optional String.

We first check if the variable str is equal to nil before we print its contents. In this example, str doesn't have a value, which means it won't be forced unwrapped by accident.

There's a more elegant approach called optional binding. In the following example, we assign the value stored in the optional to a temporary constant, which is used in the if statement. The value of the optional str is bound to the constant strConst and used in the if statement. This approach also works for while statements.

What Is nil?

If you're coming from Objective-C, then you most certainly know what nil is. In Objective-C, nil is a pointer to an object that doesn't exist. Swift defines nil a bit differently, and it's important that you understand the difference.

In Swift, nil means the absence of a value, any value. While nil is only applicable to objects in Objective-C, in Swift nil can be used for any type. It's therefore important to understand that an optional isn't the equivalent of nil in Objective-C. These concepts are very different.

2. Control Flow

Swift offers a number of common constructs to control the flow of the code you write. If you have any experience programming, then you'll have no problems getting up to speed with Swift's control flow constructs, conditional if and switch statements, and for and while loops.

However, Swift wouldn't be Swift if its control flow didn't slightly differ from, for example, Objective-C's control flow constructs. While the details are important, I'm sure they won't hinder you from getting up to speed with Swift. Let's start with the most common conditional construct, the if statement.

if

Swift's if statements are very similar to those found in Objective-C. The main difference is that there's no need to wrap the condition in parentheses. Curly braces, however, are mandatory. The latter prevents developers from introducing common bugs that are related to writing if statements without curly braces. This is what an if statement looks like in Swift.

It should come as no surprise that Swift also defines an else clause. The code in the else clause is executed if the condition is equal to false. It's also possible to chain if statements as shown in the next example.

There is one important note to make, that is, the condition of an if statement needs to return true or false. This isn't true for if statements in Objective-C. Take a look at the following if statement in Objective-C.

If we were to port the above code snippet to Swift, we would run into an error. The error isn't very informative, but Swift does tell us that we need to ensure the result of the condition evaluates to true or false.

Control flow error

The correct way to translate the above Objective-C snippet to Swift is by making sure the condition of the if statement evaluates to true or false, as in the following snippet.

switch

Swift's switch statement is more powerful than its Objective-C equivalent. It's also safer, as you'll learn in a moment. While there are some differences, switch statements in Swift adhere to the same concept as those in other programming languages; a value is passed to the switch statement, and it is compared against possible matching patterns.

That's right, patterns. As I said, a switch statement in Swift has a few tricks up its sleeve. We'll take a look at those tricks in a moment. Let's talk about safety first.

Exhaustive

A switch statement in Swift needs to be exhaustive, meaning that every possible value of the type that's handed to the switch statement needs to be handled by the switch statement. As in Objective-C, this is easily solved by adding a default case, as shown in the following example.

Fallthrough

An important difference with Objective-C's implementation of switch statements is the lack of implicit fallthrough. The following example doesn't work in Swift for a few reasons.

The first case in which a is compared against 0 doesn't implicitly fall through to the second case in which a is compared against 1. If you add the above example to your playground, you'll notice that Swift throws an error at you. The error says that every case needs to include at least one executable statement.

Notice that the cases of the switch statement don't include break statements to break out of the switch statement. This isn't required in Swift since implicit fallthrough doesn't exist in Swift. This will eliminate a range of common bugs caused by unintentional fallthrough.

Patterns

The power of a switch statement in Swift lies in pattern matching. Take a look at the following example in which I've used ranges to compare the considered value against.

The ..< operator or half-open range operator defines a range from the first value to the second value, excluding the second value. The ... operator or closed range operator defines a range from the first value to the second value, including the second value. These operators are very useful in a wide range of situations.

You can also compare the considered value of a switch statement to tuples. Take a look at the following example to see how this works.

As you can see in the above example, it is possible that the value matches more than one case. When this happens, the first matching case is chosen. The above example also illustrates the use of the underscore. As we saw in the previous article, we can use an underscore, _, to tell Swift which values we're not interested in.

Value Binding

Value binding is also possible with switch statements, as the following example demonstrates. The second value of the tuple is temporarily bound to the constant description for use in the first and second case.

for

The for loop is the first loop construct we'll take a look at. It behaves very similarly to for loops in other languages. There used to be two flavors, the for loop and the for-in loop. As of Swift 3, however, C-style for loops are no longer available. The following snippet is not possible in Swift 3.

If you paste this snippet in a playground, you'll also notice that the ++ and -- operators are no longer available in Swift 3.

The for-in loop is ideal for looping over the contents of a range or collection. In the following example, we loop over the elements of an array.

We can also use for-in loops to loop over the key-value pairs of a dictionary. In the following example, we declare a dictionary and print its contents to the console. As we saw earlier in this series, the sequence of the key-value pairs is undefined since a dictionary is an unordered set of key-value pairs.

Each key-value pair of the dictionary is available in the for-in loop as a tuple of named constants. The for-in loop is also great in combination with ranges. I'm sure you agree that the below snippet is easy to read and understand thanks to the use of a closed range.

while

The while loop comes in two flavors, while and repeat-while. The main difference is that the set of statements of a repeat-while loop is always executed at least once, because the condition of the repeat-while is evaluated at the end of each iteration. The following example illustrates this difference.

The print statement of the while loop is never executed, while that of the repeat-while loop is executed once.

In many cases, for loops can be rewritten as while loops, and it's often up to the developer to determine which type of loop to use in a particular situation. The following for and while loops result in the same output.

Conclusion

There's much more to control flow in Swift than what we've covered in this article, but you now have a basic understanding to continue your journey into Swift. I hope this tutorial has shown you how Swift's control flow implementation is very similar to that of other programming languages—but with a twist.

In the rest of this series, we'll make more use of Swift's control flow constructs, and you'll gradually get a better understanding of the subtle differences between Swift and languages like Objective-C. In the next installment of this series, we'll start exploring functions.

If you want a quick way to get started building apps with the Swift language, we've got a course for that!

Or check out some of our other tutorials and courses on Swift and iOS development!

Tags:

Comments

Related Articles