Swift: Banning force unwrapping optionals

Posted: June 28th, 2017 | Author: | Filed under: iOS, macOS, Programming, Swift | Tags: , , , , | No Comments »

Swift Optionals and force unwrapping

The Swift programming language supports optional types, which handle the absence of a value. An optional represents two possibilities: Either there is a value and you can unwrap the optional to access that value, or there isn’t a value at all.

Here is how you can declare an optional variable in Swift:

var myOptionalString: String?

The myOptionalString variable can contain a string value or nil. If the optional has a value, you can access the underlying value by explicitly unwrapping it using the exclamation mark operator like:

print("Underlying value of myOptionalString: \(myOptionalString!).")

Dangers of force unwrapping

Although force unwrapping looks convenient, it is extremely dangerous to use: if the optional has no value and you try to unwrap it, this will trigger a runtime error – generally causing the app to crash.

The majority of crashes I have seen in Swift code have been caused by an incorrect explicit unwrap. Why would someone unwrap a nil optional? There are multiple reasons:

  • An optional variable whose value was never nil becomes nil after a refactoring
  • Code copied and pasted
  • Edge cases where an optional variable is rarely nil

Do what I say, not what I do

Swift documentation

The Swift documentation mentions that you should use force unwrapping with caution in a note:


Force Unwrapping Note

WWDC session 407: Do not abuse forced unwrap

Similarly the session 407 “Understanding Undefined Behavior” of the WWDC 2017 tells us in the slide 125: “Do not abuse forced unwrap”.

Other WWDC sessions abuse forced unwrap

Watch any other session of the WWDC 2017 and you will see a lot (way too much) explicit unwrapping. For example look at the session 203 “Introducing Drag and Drop”. On the slide 120, you can read:

let dragView = interaction.view!

Is this line really safe? Nowhere in the documentation you can read that the UIDragInteraction’s view is guaranteed to have a value. Also if it is guaranteed to have a value, why is it an optional in the API?

Apple’s Sample Code abuse forced unwrap

Download a random Sample Code from Apple Developer website and look if and how force unwrapping is used. For this post I downloaded the latest Swift Sample Code available: Example app using Photos framework. When searching for the explicit unwrapping operator, you can find code like this:


Example app using Photos framework 1

This code assumes that the array of textFields’s alertController contain at least one object. Why not using something much safer like:

alertController.addAction(UIAlertAction(title: NSLocalizedString("Create", comment: ""), style: .default) { _ in
         if let title = alertController.textFields?.first?.text, !title.isEmpty {

Another example in the same project:


Example app using Photos framework 2

Wouldn’t it be cleaner and safer to write instead:

@IBAction func play(_ sender: AnyObject) {
        if let player = playerLayer.player {
            // An AVPlayerLayer has already been created for this asset; just play it.
            player.play()
        } else {

Xcode’s Fix-it wants you to insert the explicit unwrap operator

Xcode’s Fix-it feature proposes to insert the explicit unwrap operator… even when it doesn’t make sense:


Xcode Fix-it

How to avoid forced unwrap

There are multiple solutions that are cleaner and safer than using an explicit unwrap. I also generally find the code more readable. Below are several examples.

Optional Binding (if let)

The following code is correct but uses 2 explicit unwrapping:

if myArray.first?.object != nil && myArray.first!.object!.type == .type1 {
    // Do Something
}

This code is much more readable by using an optional binding:

if let object = myArray.first?.object, object.type == .type1 {
    // Do Something
}

Another similar example which could led to a crash:

myView.setHeight((myLabel?.frame.size.height)!)

Again an if let statement would prevent a possible crash. Note the neat trick of using the same variable name to access the unwrapped value:

if let textLabel = textLabel {
    badgeView.setHeight(textLabel.frame.size.height)
}

Guard statement and multiple optional bindings in a single if statement

Look at the following function. All the errors handling are duplicated code.
Also note the use of the as! operator which can led to a crash if theObject.value is not an NSNumber:

func( object inObject: ObjectType ) {
    if inObject.child!.childType == .childType1 {
        if let theObject = inObject.child!.object(forType: .childType2) {
            if inObject != theObject {
                if let theValue = theObject.value as! NSNumber?, theValue != true {
                    // Do something
                }
                else {
                    // Error handling
                }
            }
            else {
                // Error handling
            }
        }
        else {
            // Error handling
        }
    }
    else {
        // Error handling
    }
}

Instead use an early exit, group the if statements and use the as? operator:

func( object inObject: ObjectType ) {
    guard let child = inObject.child  else {
        return
    }

    if child.childType == .childType1,
        let theObject = child.object(forType: .childType2), inObject != theObject,
        let theValue = theObject.value as? NSNumber, theValue != true {
            // Do something
    } else {
        // Error handling
    }

Nil-Coalescing Operator

Although the following code is safe, it is not elegant:

if newState != nil {
    self.state = newState!
}
else {
    self.state = .default
}

Using the Nil-Coalescing Operator is much cleaner:

self.state = newState ?? .default

Closure expression to initialise member variables

In the following example you might get a crash if myFunc1() is called before myFunc2():

var myObject: MyObject?

func myFunc1() {
    self.myObject = MyObject.init()
}

func myFunc2() {
    self.myObject!.rotate()
}

You can use a closure expression to initialise the variable:

var myObject: MyObject = {
    return MyObject.init()
}()

func myFunc1() {
}

func myFunc2() {
    self.myObject.rotate()
}

Implicit unwrapped optionals

The case of implicit unwrapped optionals is very similar to forced unwrap. From the Swift documentation:

An implicitly unwrapped optional is a normal optional behind the scenes, but can also be used like a nonoptional value, without the need to unwrap the optional value each time it is accessed.

Similarly to force unwrapping an optional, I see a lot of code dangerously using implicit unwrapped optionals. This kind of optionals should also be avoided… except in one case where it is extremely useful: When declaring an IBOutlet, you should use an implicit unwrapped optional because once the method viewDidLoad is called, the IBOutlet will be initialized. Also using the IBOutlet before the viewDidLoad method would be a programming error.

For example you could write:

@IBOutlet weak var myLabel: UILabel!

Conclusion

Apple tells us to use force unwrapping with parsimony. However the explicit unwrap operator is frequently used in the WWDC slides as well as in the sample code. This is saddening because the repetitive usage of force unwrapping by Apple gives bad habits and you can easily forget about the dangers of force unwrapping.

By banning explicit unwrapping in your code, you can make your code safer and avoid a bunch of crashes. Furthermore your code will be more readable and cleaner.