DEV Community

Evan Deaubl
Evan Deaubl

Posted on • Originally published at appsdissected.com

Force unwrapping is not just “not Swift-like.” It’s wrong.

Swift comes with a number of features that help developers write correct bug-free programs: optionals, safe casting, and built-in error handling. But for every one of these, there is an escape hatch: !, the force unwrapping operator.

You may know another developer or team member who plays a little fast and loose with force unwrapping. Or you don’t feel like doing the additional boilerplate to unwrap an optional, so you do it when “nobody’s looking.”

You may not have a good argument for why safe unwrapping is good and force unwrapping is bad, other than that the Swift documentation says so, or “that’s how it’s done.”

The reason behind safe unwrapping comes from a long history of languages that didn’t provide strong safeties such as these. This often resulted in trying to interpret random memory locations as objects, resulting in crashes at best, hard to debug app behavior at worst. Not using all of Swift’s safety mechanisms at your disposal is not just not Swift-like, it is bad programming.


Swift does a lot to make safe unwrapping easy to use, but sometimes the draw to use ! is very attractive. Just drop it in your code and you can ignore a bunch of nil checking or error catching or type casting you would normally need to. That is the wrong way to think about it; you shouldn’t think of ! as a way out.

Using ! is saying that you know better than your compiler; that you can reason through all the paths and uses of your code, not just now, but every possible use in the future, and decide that your force unwrapping is safe. NEWS FLASH: Compilers reason about code; interpreting code to produce executable output is their sole reason for existing. They will do a better job than you will at keeping you safe.

The ! operator is a good choice of character — okay, maybe 🚨 would have been better, or 🔥 — because you should be alarmed anywhere you see it in your code. Everywhere you see the ! operator, either defining an implicitly unwrapped property, after an Optional, as!, or try!, you shouldn’t think “this is going to be fine, so I’m force unwrapping.” You should think, “This is a place where I have specified that my code can crash.” When you frame it in those terms, you will feel much more motivated to code in ways that avoid that situation in the first place.

You might be saying right now, “But I need to interface with Objective-C NSDictionarys or NSArrays, and I know they contain all Strings, or all Integers, so I want to just force unwrap it and move on.” You can — and should — still use safe unwrapping and casting on NSDictionarys and NSArrays.

Consider this:

var foo: NSArray = NSArray(arrayLiteral: 5, 6, 7)

if let bar = foo as? [Int] {
  print(bar.reduce(0, +))
}

if let bar2 = foo as? [String] {
  print(bar2)
}
// Will not print

The safe cast checks all of the elements immediately, so the first if let prints the sum of the elements of the NSArray, while the second if let falls through because the elements are not Ints. Do this as soon as you can after receiving an NSArray, NSDictionary, or other loose Objective-C type, then your Swift code will work with the stricter Swift types exclusively.

If you have a force unwrapping, casting, or try! in your code, there is almost always some way you can restructure your code to make it safe. Get rid of your !s; your future self will thank you for it.


Did you like this tip? The next tip on a compile-time error you may run into using Objective-C BOOLs in Swift is already waiting for you. Or sign up to get every tip straight to your inbox.

This post originally published at Apps Dissected.

Discussion (11)

Collapse
halileohalilei profile image
Halil Ibrahim Kayim

The points made in this post are very real. What we did to enforce these rules was to use SwiftLint so that people were forced to refactor their code and not use any force unwrapping, otherwise the build process would fail. If the developer was %100 sure that using force unwrapping was the only way to write that piece of code, then they could ignore the error with a specific SwiftLint comment, and we would decide whether it was absolutely necessary to use force unwrapping or not, during code review.

Collapse
evandeaubl profile image
Evan Deaubl Author

That's a great combination of several different techniques! Adding review layers where somebody needs to justify their use of an unsafe language function adds that mindfulness. And Lint+code review generalizes nicely to catch this general kind of code smell.

Collapse
striderhnd profile image
Erick Gonzales • Edited

I really agreed with all the statement about the using of unwrapping optionals. but sometimes on the development it's a necessary evil, if the developer is pretty confident to justify the use of ! in an object and make sure when trying accessing the object value there will be no problem with it, I've seen a lot of usage of the ! but is not a practice I really support for me even I prefer to deal with the boiler plate or make something that deals with that using the minimal code required for that operation than deal with bugs.

Collapse
evandeaubl profile image
Evan Deaubl Author

Yes, I love the conversation this has inspired about what others have done to use ! as safely as you can when it is necessary. A strong bias against it is a good default, and being very mindful when you do need to use it (with supporting documentation!).

Collapse
ben profile image
Ben Halpern

Thanks for this, as someone newer to Swift, I knew this, but now I feel like I really know this. I have definitely snuck in some ! already. 😳

Collapse
evandeaubl profile image
Evan Deaubl Author

I'll admit, it's easy in a weak moment to drop a ! and run away. 😅 I think that's one of the main problems; they made it too easy to do just that, without thinking.

Collapse
ben profile image
Ben Halpern

Escape hatches are such a funny element of computer programming in general. So terrible yet fairly awesome in some senses. All in all it’s “better” that you “can” ship a quick and dirty solution, but you just shouldn’t do it.

Thread Thread
evandeaubl profile image
Evan Deaubl Author

Yeah, re: the discussion with Theodore below, IB outlets are one place where the escape hatch is useful, because the contract that this value will be set when you use it is being fulfilled another way. It probably shouldn't be an absolute 100% never do it, but training a strong bias against it is wise. And maybe some additional guardrails and mindfulness for when you do need to use it (code comments asserting why you believe it's safe, for example).

Collapse
evandeaubl profile image
Evan Deaubl Author

Outlets are an interesting case. I can mostly get behind those (I'm at about 95% 😁), because even though it is implicitly unwrapped, you are relying on the platform to populate the outlet, and so there is a guarantee of sorts. You could even do static analysis on the code and know that an IBOutlet would be populated before you would ever use it if you wanted stricter enforcement. It isn't perfect, but it's probably the safest place it could be used.

I think it's a healthy stance to strongly bias towards not force unwrapping in general, though. The minute I need to deeply reason about the interactions in the code is where it gets dangerous, and it is much safer if the assurance is enforced in the code, rather than a promise that exists outside of code. I don't want that IOU to be bad debt when I come to collect! 💸