Optional is another important concept in Swift.

Optional Values

When our variables can be missing, we use ? keyword to describe them

1
2
3
var name: String? = nil
name = "Leo"
name = nil

You can see that String? type can hold either nil or a String value. nil in Swift means no-value

Unwrapping optionals

Unwrapping with if let

When your String is an optional, you can not use String methods on it normally since it might be nil and have no String methods.

1
2
var sport: String? = "Tennis"
print(sport.count) // Error

We can use a technique called Unwrapping optional to deal with Optional values:

1
2
3
4
5
6
7
var sport: String? = "Tennis"
if let unwrapped = sport {
    // Only run if not nil
    print(unwrapped.count)
} else {
    print("sport is a nil value")
}

The syntax of Unwrapping optional is

1
2
3
4
5
if let unwrapped = var {
    // if has value do something
} else {
    // if not do something else
}

Unwrapping with guard let

Along with if let, you can use guard let to unwrap optionals. The difference between if let and guard let is, you have to use guard inside functions

1
2
3
4
5
6
7
8
9
func count(val: String?) {
    // if val does not have a value immediately return function
    guard let unwrapped = val else {
        print("value is nil")
        return
    }
    // we can use unwrapped from here until the end of the function
    print(unwrapped.count)
}

As you can see, with guard let, the statement when variable has no values comes first, while with if let the statement when variable is not nil will come first. The pros of using guard let is, we can use the unwrapped value until the end of the function which guard let is in.

Force Unwrapping

We can use the symbol ! to force unwrap optional values

1
2
var name: String? = "Leo"
print(name!.count)

Using !, we don’t have to use if let or guard let to unwrap optionals values. But this technique only works if your variable has a value. Your application will crash if the variable was nil, so be careful. When you can, always unwrap safety using if let or guard let

Implicitly unwrapped optionals

Optional values always require unwrapping before being used. The code below will crash because we did not unwrap num before using it.

1
2
3
4
var num: Int? = nil
num = 5
print(num * num)
// Error

You can implicitly unwrap optionals with ! when declaring and use them as normal variable (yes, another !. This is so confusing).

1
2
3
var num: Int! = nil // since we use Int! instead of Int?, we can use our optional as normal variable later
num = 5
print(num * num)

The point of Implicitly unwrapped optionals is, there are some cases we know that our optionals will have a value before we use them. But if you can, safely unwrapping with guard let and if let is always better.

Nil coalescing

When we want to assign value to a variable using optional, we can use ?? keyword:

1
2
var str: String = "500"
var num: Int = Int(str) ?? 0

The Int(str) function returns nil if it could not convert String to an Int. In the above example, the value of num will be 0 if str could not be converted to Int.

Optional Chaining

Imagine that we have an Array of colors:

1
var colors: [String] = ["Red", "Green", "Blue"]

We want to get the first element of the array, uppercased. The code to get first element of an array is:

1
2
var firstColor: String? = colors.first
// color.first will return an optional

Because an array can have no element, array.first always returns an optional. We can get the first color, uppercased using the code below:

1
2
3
4
5
6
7
8
var colors: [String] = ["Red", "Green", "Blue"]
var firstColor: String? = color.first
var firstColorUppercased: String? = nil
if let unwrapped = firstColor {
    firstColorUppercased = unwrapped.uppercased()
} else {
    firstColorUppercased = nil
}

There is a short hand for this problem:

1
2
var colors: [String] = ["Red", "Green", "Blue"]
var firstColorUppercased: String? = colors.first?.uppercased()

if the value before ? mark is nil, then nil will be immediately returned. If not, the code from the right hand will continue to execute.

Optional try

We can also use optional in errors handling with try?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
enum PasswordError: Error {
    case TooWeak
}

func checkPassword(password: String) throws -> Bool {
    if password.count < 6 {
        throw PasswordError.TooWeak
    }
    return true
}

if let result = try? checkPassword(password: "Hello") {
    // if an error was not threw your code will go here
    print("This password is okay")
} else {
    // if an error was threw this block will be executed
    print("Your password is too weak")
}

We can use force unwrapping:

1
let result = try! checkPassword(password: "Hello")

Note that our application will crash if the function above throw an Error

Failable initializers

In the above examples we create default type with optional values, like String? or Int?. We can also create our own structs or class that are possible to have nil value with the failable initializer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
struct Adult {
    var age: Int
    init?(age: Int) {
        if age < 18 {
            return nil
        } else {
            self.age = age
       }
    }
}
// will be nil
var alex: Adult? = Adult(age: 15)

Type casting

In the example below, both Student and Teacher inherit from Person

1
2
3
struct Person {}
struct Student: Person {}
struct Teacher: Person {}

Because a student is also a person, and a teacher is also a person, we can make an array of type Person, which has both Student and Teacher struct objects:

1
var people = [Student(), Teacher(), Student(), Teacher()]

Furthermore, while looping over our array, we can specify what a student or a teacher should do:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
for person in people {
    if let student = person as? Student {
        print("I'm a student")
    } else {
        print("I'm a teacher")
    }
}
// I'm a student
// I'm a teacher
// I'm a student
// I'm a teacher