Swift Optionals - A Quick Guide

What are optionals?

Optionals in Swift are like “supertypes” of standard variable types which can hold any value that a variable of the standard type can hold as well as a special “nil” value, which basically means the variable has no value.

Possible Values String Optional String
"foo"
"" (empty string)
nil
Int Optional Int
42
nil

How do you define optionals?

The most common way to define a variable or constant as an optional type is to explicitly type it and to suffix the type with a question mark character.


var foo = "Hello" // foo is a String
var bar: String? = "Hello" // bar is an Optional String with an explicit
                           // String value
var baz: String? = nil // baz is an Optional String with an explicit nil value
var qux: String? // qux is an Optional String with an implicit nil value
let quux: String? = "asdf" // Of course constants can be optional too, though
                           // they're not as useful.

foo = nil // Won't work; foo does not have an optional type
bar = nil // Works; bar has an optional type
    

Because of this, Swift coders often use this question mark syntax as a shorthand when referring to optional types in text; for example, writing “Int?” instead of “optional Int.” I will use this syntax in the rest of this article.

Collection types can also hold optionals.


var arrayFoo = [1, 2, 3] // Array of Ints, or [Int]
var arrayBar: [Int?] = [1, 2, 3] // Array of Optional Ints, or [Int?]
var arrayBaz = [1, nil, 3] // This will implicitly be an [Int?] too

arrayFoo.append(nil) // Won't work
arrayBar.append(nil) // Works; arrayBar is an array of an optional type
    

And collection types can also be optionals.


var foo: [Int]? // foo is an [Int]? with an implicit nil value
foo = [1, 2, 3] // Works
foo = nil // Works
foo = [1, nil, 3] // Doesn't work; foo is an optional, but not an array of
                  // optionals
foo.append(123) // Doesn't work; optional collection types need to be unwrapped
                // before acting on them (see below)
    

Was that confusing? Here’s another table of possible values which may be useful.

Possible Values [Int] [Int?] [Int]? [Int?]?
[1, 2, 3]
[1, nil, 3]
[nil, nil, nil]
nil
[] (empty array)

An item extracted from a collection of optionals will also be an optional, even if it has a non-nil value.


let arrayBaz = [1, nil, 3] // [Int?]
let bazElement = arrayBaz[0] // Int?
    

How do you check the value of an optional?

You can check to see if an optional has a nil value by simply comparing to nil.


var foo: Int?
// Do some stuff with foo

if foo == nil {
    print("foo is nil.")
}
else {
    print("foo is not nil.")
}
    

But, more commonly, Swift developers will check if an optional has a non-nil value by unwrapping it.

What is unwrapping?

Unwrapping is the process of attempting to cast an optional variable to a non-optional one of the same type; for example, casting an Int? to an Int. The unwrapped variable can then be usefully acted upon. The unwrapping will of course fail if the optional variable has a nil value, since the non-optional one cannot have that value; however, if that happens, then we know that the original optional had a nil value.


var optionalFoo: Int?
// do some stuff with optionalFoo

if var foo = optionalFoo {
    print("If you see this, then optionalFoo is not nil and foo has a value of \(foo)")
    foo += 7 // foo is a normal Int and we can modify it as such
    print("foo now has a value of \(foo)")
}
else {
    print("If you see this, then optionalFoo is nil")
    print(foo) // Won't work; foo is not defined here
}
    

Note that the unwrapped variable is only in scope in the if clause’s code block, even if the unwrapping succeeds.


var optionalFoo: Int?
optionalFoo = 8

if var foo = optionalFoo {
    print(foo) // Works; foo is an Int
}

print(foo) // Doesn't work; foo is out of scope
    

That rule changes when a guard statement is used, however! This method of unwrapping is thus very common when you want to simply return from a function if unwrapping fails, but act upon the unwrapped variable otherwise.


var optionalFoo: Int?
optionalFoo = 8

guard var foo = optionalFoo else {
    print("Unwrapping failed. Returning now.")
    return
}

print("foo is \(foo)") // Works; foo is an Int
    

Note that simply defining a variable as the value of an optional variable will result in the new variable also being an optional; unwrapping doesn’t happen in this case.


var foo: Int? // foo is an Int?
var bar = foo // bar is an Int?, not an Int
var baz: Int = foo // Error!
    

Optional variables can be unwrapped without defining a new variable by using the ? (unwrap) or ! (force unwrap) operators. This can be useful when trying to operate on the collections “inside” an optional collection type. The major difference between using ? and ! is that your program will silently continue if unwrapping failed when ? is used, but your program will completely crash when it fails when ! is used. Thus, using ! is generally considered a bad practice, made worse by the fact that Xcode’s fixits often suggest using it.


var bar: [Int]? // bar is nil
bar?.append(123) // Unwrapping bar silently fails and bar is still nil

bar = [1, 2, 3] // bar is [1, 2, 3], but is still an optional
bar?.append(4) // bar is [1, 2, 3, 4]

if bar?.last == 4 { // If unwrapping bar fails, this evaluates to false
    print("The last element is 4")
}

var baz: [Int]? // baz is nil
baz!.append(123) // Unwrapping baz fails and the program crashes