Lesson 6 - Type system: Optionals in Swift
In the previous exercise, Solved tasks for Swift lesson 5, we've practiced our knowledge from previous lessons.
Now we're going to finally explain the concept of Optionals, which is very important in Swift. We'll talk about what all the exclamation marks in source code mean. We've already stumbled across a few, and although it may be a little complex concept to start with, it's better to understand at least a little bit what it's all about.
The nil
value concept
Programming languages must somehow deal with a situation where a variable has
no value. We often encounter this problem with functions which hasn't been
executed properly. For example, if a number couldn't be retrieved from the
console, the output shouldn't be a number but rather an "empty" value. If Swift
returned the 0
or -1
value in this case, we wouldn't
be sure whether the number indicates it wasn't possible to read it or the user
had entered 0
or` -1. For this purpose, a special `nil
value was introduced, which safely indicates that a variable is empty. In other
languages, this value is often called null
and works the same
way.
If we create a standard variable in Swift, we can't assign the
nil
value to it:
// This code won't work var number = 15 number = nil // This line will cause an error
That's because someone may not be expecting nil
there. In order
to assign nil
to a variable, we must declare the variable as
Optional
first.
Optionals
An Optional
type can be seen as kind of a box that wraps an
ordinary variable. The box always exists, but when opened, the value is
either there or it's empty.
Question mark
We create an Optional
type by writing a question
mark ?
after a variable's data type. Let's try it:
var maybeNumber: Int? = 15
maybeNumber = nil
The code was compiled successfully and the maybeNumber
variable
is now empty, even though it's a number. That sounds good, right?
There's however a problem that a lot of other programming languages failed to
deal with. We shouldn't be able to work with the
maybeNumber
variable as with a normal variable. If we
wrote:
var maybeNumber: Int? = 15 maybeNumber = nil print(maybeNumber * 2)
and the program would be compiled, it could cause a runtime crash in the case
of maybeNumber
being empty. Of course, we can't multiply "empty" by
two. If we tried to write such a program, you'd find that it won't
compile. Similarly, Swift wouldn't let us access a method or property
of an Optional
type. You can try yourself that the following code
won't compile because of printing the second string's length:
var s1 = "Hello" var s2: String? = "World" print(s1.count) print(s2.count)
You've probably guessed that Swift doesn't belong to languages that can't
deal with this problem
Null safety
The mechanism checking how we use the Optional
type during the
compilation is called null safety. There are several ways to
use an Optional
variable. Let's try them out one by one.
Exclamation mark
An Optional
can be created other way as well. We'll use an
exclamation mark !
:
var alwaysNumber : Int!
// We can write print(alwaysNumber + 1) but the application will crash, because there's no number in the alwaysNumber box yet
This way we tell Swift that we'll take care of the variable by ourselves and
ensure that it has a valid value before accessing it (in this
case any integer). In our code, we can work with it as it was a normal
initialized Int
variable, but the program can easily crash.
You surely figured out that an exclamation mark in a code means a risk and we should rather avoid it.
Opening the box
Now we'll finally learn how to access the values and why we wrote exclamation marks in our code.
The !
operator
Let's start with the most silly one which we've been already using in this
course, so that the amount of information wasn't that overwhelming. Using the
!!
operator we can degrade Swift to the level of older languages
such as e.g. Java and bypass the null safety
checking. Everything will work unless the variable is
nil
:
var maybeNumber: Int? = 15 print(maybeNumber! * 2)
The output:
30
This is called force unwrapping.
If it's empty though, the whole application will crash:
var maybeNumber: Int? = 15 maybeNumber = nil println(maybeNumber! * 2)
We wouldn't even notice this error during compilation. This approach is very uncommon and is used generally in cases when we know that there's always gonna be a value or that the absence of the value is critical enough to terminate the code.
Optional binding
We should always access Optionals carefully through optional binding. We can
use two basic structures: the traditional if
condition or the
guard
keyword. There are mainly semantic differences between them
(they have different meaning). Let's have a look at them first and then explain
what they mean.
Securing Optional values using a condition
var optionalNumber : Int? = 5 if let number = optionalNumber { print(number * 2) } else { print("The number is empty") }
The code in the condition will be executed only if there's a value
stored in the optionalNumber
variable. The value will then
be saved into the number
constant; we can work with it as if it was
a traditional Int
variable so we're not restricted anymore.
By the way, you can use if var
as well but since
the given variable is accessible only in the block, it doesn't make much sense
to modify it anyway.
Guard
In short, we could say that guard
works as the opposite and
executes the code only if the condition isn't true. In functions, which we can't
declare yet, we'll use it to exit the block using the return
keyword. We'll show it further in the course.
Opening the box using the default value
Let's show one more way to get rid of an Optional
and get the
traditional data type. We can easily specify the default value which will be
used if an Optional
is empty. It's called nil
coalescing and it has its own operator, ??
.
var maybeNumber: Int?
maybeNumber = nil
let definitellyNumber = maybeNumber ?? 4
print(definitellyNumber)
You can see that the ??
operator is binary, so it requires
values on both sides. If the left side isn't nil
(empty
Optional
), the operator just returns this value. Otherwise, if it's
nil
, it returns the second value.
Here, 4
will be printed because the maybeNumber
variable is nil
. This is useful in cases when we can easily
continue using some default value which we'll set via ??
. It's
possible to chain this operator but I suggest you don't to keep the code
readable.
In the next lesson, Arrays in Swift, we'll talk about arrays.