After publishing part 1 of my article series “Thinking in Swift”, I had some great feedback and reactions on Twitter. Today I wanted to build on those comments and talk about when it could be ok to use !
and sacrifice a pony 🐴.
This post is part of an article series. You can read all the parts here: part 1, part 1 addendum, part 2, part 3, part 4
Never ever kill a pony?
So in my last article, I urged you not to use !
. Some might have read it as “never use it, like ever”. What I said was in fact more that « every time you add a ! just to please the compiler, you’re killing a pony 🐴 ». But sure, that 🐴 can keep living if you actually know what you’re doing. Just don’t do it without thinking and only to please the compiler.
So yes, in some contexts, it’s still useful to use implicitly-unwrapped optionals. But only when it’s justified and you thought about it and you know why you’re doing it.
When to sacrifice ponies?
Here are some examples where it would be ok to use !
in Swift. These are probably not the only use cases for !
, but that’s the most frequent exceptions to the pony rule I could think of.
1. IBOutlets + Dependency Injection
IBOutlets
— as well as Dependency Injected properties for those of you who do DI — are a special case as they will still be nil
at the end of the init
method (because they won’t be able to be initialized that soon), but they will get a value very quickly, right after the instance initialization (either by Dependency Injection or through the XIB-loading mechanism).
So even if they have to be optional (as they won’t be initialized during init
, only just some short moment after), you’re pretty sure that, by design, they won’t ever be nil
in the rest of your code. That’s one case where implicitly-unwrapped are convenient, because you know they’ll never be nil
by the time you use them.
2. UIImage, UIStoryboard, UITableViewCell
All the methods below are initialized using names and identifiers which are meant to be constants:
UIImage(named: …)
UIStoryboard(name:,bundle:)
UIStoryboard.instantiateViewControllerWithIdentifier(_:)
UITableView.dequeueReusableCellWithIdentifier(_:, forIndexPath:)
// and some other similar stuff…
For those use cases, you may want your code to crash if you misspelled the name/indentifier, because it would be a developer error (caused by a missing image in your bundle or a storyboard that you failed to include in your target, etc), and you probably want to detect it early at development stage to fix it ASAP.
Note that for those cases, you may also prefer sometimes to use the guard let else fatalError()
pattern, to provide a more accurate exception message in case you hit that developer error case:
guard let cell = tableView.dequeueReusableCellWithIdentifier("foo", forIndexPath:indexPath) as? MyCustomCell else {
fatalError("cell with identifier 'foo' and class 'MyCustomCell' not found. "
+ "Check that your XIB/Storyboard is configured properly.")
}
That’s also why you’d want to use tricks like the ones I discussed in enums as constants and would want to use SwiftGen to avoid those possibilities of identifier misspellings and crashes 😉.
For example SwiftGen
actually use UIImage(named: asset.rawValue)!
in its implementation, because the enum
you use is generated by the SwiftGen tool, and so it’s guaranteed that the image with that name exists in your Assets Catalog — so by design it can’t be nil
.
Should I sacrifice ponies on special occasions?
The first part of this article series was more oriented for people new to Swift, as they tend to force-unwrap all the time1 just because Xcode tells them to, or because they don’t really get why Optionals are good for them. That’s why I didn’t talk about those use cases which feel more advanced to the public I was targetting.
So yes, there are cases where it makes sense to use !
. But still, my advice in part 1 still holds: only use !
if you really understand why you use it and you thought about it first, and don’t do it just to please the compiler. Especially, if you’re new to Swift and you wanted to add that !
just because “those darn optionals are so annoying with all their errors, I want my compiler to stop complaining”, you’re doing it wrong and that’s where you start killing ponies.
See you soon for the next parts of this article series, talking about map
, flatMap
, ??
, optional chaining, and avoiding data inconsistencies.
-
Insert a “Force-Unwrap all the Things” meme here — that I was too lazy to create and include. ↩