Swift Optional

Filed Under: Swift

Swift Optional is a very important underlying concept in Swift programming. Optional is something that you’d be dealing a lot in Swift development. Let’s see what it has in-store for us. If you aren’t aware of the basics of Swift, you can breeze through this article before proceeding.

Need for Swift Optional

Typically in Swift we define a variable or constant as:


var str = "String type inferred"
var string:String = "Type defined"
let const = "I'm a constant. I can't change"
let i:Int = 0  

Note: let constants can’t be changed once defined.

It’s all good with the above declarations until we try setting it as below.


string = nil //Compile Time Error: error: nil cannot be assigned to type 'String'
var currentValue:String = nil //Error

The above snippet shows that Swift variables, constants always require a value. Setting no value would lead to compile-time error. This is where Swift gives us Optional, a very powerful feature. We can use optional in situations where a value may be absent (like storing nil values in the above snippet).

Swift Optional

Swift Optional variable or constant can contain a value or a nil value. An optional variable/constant declaration requires a ? after the type. A declaration of an optional variable/constant is given below.


var currentValue:Int? = nil
var x :String? = "Optional variable"
print(currentValue)  //prints nil
print(x) //prints Optional("Optional variable")

currentValue = 1
x = nil
print(currentValue)//prints Optional(1)
print(x) //prints nil

Swift Optionals are just a type like normal types. Optional types act as a wrapper over the normal types with the added functionality to allow the variable/constant to be nil.

Hence printing an optional would wrap the value inside Optional().

Any optional type Int? or String? is not the same as Int or String. Therefore an Optional type cannot interact with a normal type. Neither can an optional type interact with another optional type as shown in the below image.

swift optional type

To use the value of an Optional type we need to unwrap it using !. That’s what XCode recommends. But is it ideal? We’ll see all this shortly.

Swift Optional Unwrapping

Swift Optional variable unwrapping is used to convert an optional type to a normal type (Int? to Int).

There are two types of unwrapping:

Force Unwrapping of Swift Optional Variable

This is what XCode suggests (check previous image) by adding a ! at the end of optional variable. An example is given below.


var a:Int? = 2
var c:Int? = 3
var b:Int = 4

print(a!) // prints 2
print(a! + b) //prints 6
print(a! + c!) //prints 5
var unwrapThis:String? = "Unwrap me please"
print(unwrapThis) //prints Optional("Unwrap me please")
print(unwrapThis!)//prints Unwrap me please

It can get pretty redundant while adding a ! to each optional variable/constant.

We can define an optional variable that’ll implicitly unwrap when called as shown below.


var a:Int! = 2
var c:Int! = 3
var b:Int = 4
print(a) // prints 2
print(a + b) //prints 6
print(a + c) //prints 5

A variable with ! instead of ? after the type is known as Implicitly Unwrapped Optional. This would be able to interact with normal variables whenever called since it’ll be implicitly unwrapped to a normal type.

Good to know, but defining this way can be risky when the variable/constant is nil (it’ll cause a crash since normal type can’t have a nil value).

It’s noteworthy to mention that Force Unwrapping tells the Swift compiler that you’re sure that the variable won’t be nil. This can’t always be the case. This is where the second type of unwrapping comes to the rescue.

Optional Unwrapping of Swift Optional Variable

Here we conditionally check if the optional variable has a nil value or not. Only if it doesn’t have we’ll unwrap it. One way to unwrap an optional is to check if the value is nil before force unwrapping as shown below.


if name != nil
{
    if desc != nil
    {
        print(name! + ": " + desc!) //prints Swift Tutorial: Optionals
    }
    else{
        print(name! + ": Undefined")
    }
}
else{
    
    print("Nothing is defined")
}

In the above approach, we’ll only unwrap when we’re sure the value is not nil.

Note: Swift compiler is sensitive to white spacing.

Optional unwrapping using ternary operator is given below.


var i:Int? = nil
let ternary = i != nil ? i! : -1

print("Value of i is \(ternary)") //prints -1

Swift has an even clever approach to unwrap an optional. Let’s see that in the next section.

Swift Optional Binding

Swift Optional Binding is used to find out whether an optional contains a value or not. If it does then make that value available as a temporary constant or variable. All this is performed in a single action. An example is given below.


var desc: String? = "Optionals"

if let temp = desc{
  print(temp)
}
else{
 print("No description")
}

In the above code, if the value inside desc is not nil we save it in a temporary variable or constant and use it.
Optional binding is the recommended way to unwrap an optional.

An example using multiple if let is given below.


var first:String? = "first"
var second:String? = "second"
var third:String? = "third"

if let f = first {
    
    if let s = second{
        
        if let t = third{
            
            print(f + " " + s + " " + t  )
        }
        else{
            
            print(f + " " + s + " NA")
        }
        
    }
    else{
        print(f + " NA")    
    }
    
}
else{   
    print("NA")
}

Now, this has too much of nesting and would require too many if-let checks so that the temporary variables are available in each scope. Surely we can improve it.

There’s a super short form to do an optional binding if-let as shown below.


var first:String? = "first"
var second:String? = nil
let f = first ?? "NA"
print(f) //prints first
let s = second ?? "NA"
print(s) //prints NA

The ?? unwraps and stores the optional value in the variable/constant. If the optional value is nil, it uses the default value present on the right hand side.

The above concept can be applied to the nested if let code shown previously.


var first:String? = "first"
var second:String? = nil
var third:String? = "third"

print ("\(first ?? "NA")" + " \(second ?? "NA")" + " \(third ?? "NA")") // prints first NA third

Multiple Optional Binding In Single Statement

We can unwrap multiple optionals in a single if statement separated by commas as shown below:


var optionalX :String? = "x"
var optionalY :String? = "y"
if let x = optionalX, let y = optionalY
{
print(x) //prints "x"
print(y) //prints"y"
}
else{
print("One or more Optional can't be unwrapped")
}

However there’s one pitfall in the above approach. If any of the optionals can’t be unwrapped, none of them can be used in the if statement as shown below:


var optionalX :String? = "x"
var optionalY :String? = "y"
var optionalZ :String?

if let x = optionalX, let y = optionalY, let z = optionalZ
{
print(x)
print(y)
print(z)
}
else{
print("One or more Optional can't be unwrapped") //this gets printed. x and y are skipped too.
}

Hence the above approach is recommended only when you need the values from all optionals or none.

Swift Guard

guard is another powerful feature in Swift that can be used in place of nested if let.

guard is similar to if condition except the fact that guard checks for all the bad cases only. The structure of a guard statement is in the form:


func functionName()
{
guard <condition> else{ //break or return since the condition has failed }

//Do something since your condition has passed the bad checks.
}

A guard is in a way an inverted if condition. Besides guard works only inside functions. An example of guard is given below.



func exampleGuard() {

    var didWeMeet = false
    
    guard !didWeMeet else { return }
    
    print("Did we meet? " + String(didWeMeet))
    
}
exampleGuard() //prints "Did we meet? false\n"

//Example 2
func exampleGuard2() {

    var didWeMeet = false
    
    guard didWeMeet else { return }
    
    print("Did we meet? " + String(didWeMeet))
    
}

exampleGuard2() //else statement executed and function returns void

In the above code if the condition entered after guard keyword fails the function would execute the else case and return. An example of guard with optional binding is given below:


var second:String? = nil
func guardStatements()
{
guard let sa = second else{
print("nil value")
return
}
print(sa)
}

guardStatements() //prints nil value

In the above code since the optional value of second is nil, the else statement of the guard runs and the function exits there itself.

If the value was successfully unwrapped the print(sa) statement would have run.

Let’s finally convert the nested if let statements into guard statements and see how easily we can get rid of nested conditions.


func guardStatements()
{
guard let f = first else{
print("nil value")
return
}
    
    guard let s = second else{
        print("nil value")
        return
    }
    
    guard let t = third else{
        print("nil value")
        return
    }


    print(f + " " + s + " " + t)

}


guardStatements() //prints first second third

// Isn't the above code simpler than the below one?

var first:String? = "first"
var second:String? = "second"
var third:String? = "third"

if let f = first {
    
    if let s = second{
        
        if let t = third{
            
            print(f + " " + s + " " + t  )
        }
        else{
            
            print(f + " " + s + " NA")
        }
        
    }
    else{
        print(f + " NA")    
    }
    
}
else{   
    print("NA")
}

Swift guard checks for bad cases only. If in any of the guard let, the condition (bad case) is met, the function would exit there itself.

Note: Difference between guard let and if let

  • The guard statement makes early exits possible with an emphasis on negative cases whereas the if conditions waits for all the nested conditions to pass.
  • Unwrapped values in guard statements would be available for the rest of the code block without the need to force/optionally unwrap again.

Hence guard let is preferred over if let!

This brings an end Swift Optional tutorial.

Comments

  1. Sumir Kapur says:

    Great post. Really informational 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages