UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

When should you force unwrap optionals in Swift?

Paul Hudson    @twostraws   

Updated for Xcode 15

Optionals provide us with safety in Swift by stopping us from accessing data that doesn’t exist. However, in a handful of circumstances you might want to consider force unwrapping the optionals rather than using if let or similar.

Remember, what you don’t want to do is risk a crash – you don’t want to force unwrap an optional only to find that it’s nil. That might save you a line or two of code, but “that should never crash” isn’t a good enough reason for a force unwrap, and we can all do better.

I’m going to go into more detail in a moment, but I want to give you some examples of code that I’m happy to force unwrap and code that should not be force unwrapped. These examples are drawn from real-world Apple APIs that you might not have met yet, but that’s OK – you’ll get the idea.

First, this code creates a URL from a string:

let url = URL(string: "https://www.apple.com")! 

That is a hand-typed URL and I have verified that it’s correct, I’m happy to force unwrap it. This cannot go wrong at runtime, and there is no scope for surprises. Compare that to using string interpolation:

let url = URL(string: "https://www.\(website)")!

That isn’t safe, and can’t be verified, and if given a malformed web address could crash.

Second, this code creates a closed range from 1 through 10, then picks a random number from that range using the randomElement() method:

let randomNumber = (1...10).randomElement()!

randomElement() returns an optional value because it’s possible the range or array we’re using it with was empty. In this case, though, you can see that I’ve hand-typed the range so I know there will always be a number coming back, and again this cannot go wrong at runtime. Compare that to using an unknown array:

var items = [Int]()

for i in 1...10 {
    if isLuckyNumber(i) {
        items.append(i)
    }
}

let randomNumber = items.randomElement()!

What does that isLuckyNumber() function do? I haven’t included it here because it doesn’t really matter, but it’s still some code that isn’t visible right here so we can’t guarantee that items will have any items by the time we call randomElement() on it.

Hopefully you can see the difference between the two: one situation is guaranteed to work 100% of the time, and the other “should” work or “will nearly always work”. That’s the key here: you should never force unwrap something that won’t succeed 100% of the time. That’s it – that’s the entire rule.

Now, some people disagree and instead argue that force unwrapping should never happen. I can see their point of view, and I’ve certainly thought that myself previous. However, I find the extra code to handle all those impossible situations to be distracting, because they add clutter to our code that literally never does anything.

More annoying are the times when there is no sensible alternative. For example, if you make an enum conform to the CaseIterable protocol, Swift will generate a special allCases array property for your enum that contains all the cases in the order they were defined. So, we could create a Direction enum and pick a random value like this:

enum Direction: CaseIterable {
    case north, south, east, west
}

let randomDirection = Direction.allCases.randomElement()!

We’ve defined an enum with four cases, and we’re asking to pick a random case. The randomElement() method returns an optional because that’s how it works, but we know it will always succeed because our enum has cases.

If we were to decide we never wanted to use force unwraps, what value could we use instead – what default value could we give randomDirection that allowed our code to continue safely?

The only way a force unwrap would be unsafe here would be if we deleted the four cases from Direction, but even then Swift shows a warning that we’re going to get unexpected behavior.

So, I think force unwrapping is sometimes a good idea, and sometimes even required. However, I am not advocating that you start scattering exclamation marks around your program, because that starts to get messy.

Instead, a better idea is to create a handful of functions and extensions that isolate your force unwraps in one place. This means your force unwrapping can be stored near to the place where its behavior is clarified, and the vast majority of your code doesn’t need to force unwrap directly.

For example, we could rewrite our Direction enum to this:

enum Direction: CaseIterable {
    case north, south, east, west

    static func random() -> Direction {
        Direction.allCases.randomElement()!
    }
}

With that in place, everywhere we need to get a random direction no longer needs a force unwrap:

let randomDirection = Direction.random()

Before moving on, I want to reiterate one key point here: if you’re only 99% certain that a force unwrap is safe, you shouldn’t use it. Heck, even if you’re 99.999% certain that it’s safe, you shouldn’t be using it – that equates to one crash in 100,000, which is really bad. Instead, force unwraps should be reserved for times when your code is absolutely guaranteed to be safe, as demonstrated above.

For a discussing on avoiding force unwrapping, check out this blog post from Alexandre Colucci: https://blog.timac.org/2017/0628-swift-banning-force-unwrapping-optionals/

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.6/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.