What is Swift's Yolo
Why "Yolo"?
When an optional variable is force-unwrapped "unsafely", we are telling the compiler "Hey bro, don't worry about this code -- I know better than you do. Yolo. ¯\(ツ)/¯" This is the programming equivalent of "here, hold my beer -- watch this". You will get to know this line of code shortly because there is an excellent chance you will see it again in a crash report with the name EXC_BREAKPOINT
.
Types of Yolo❗️
I can see those of you reaching around for your exclamation-shaped pitchforks already. Calm down. As with all things in life, there are exceptions in addition to best practices.
💥 Unsafe Yolo
Unsafe yolo is force-unwrapping optionals because you "need a non-optional" or "this will never be nil". If it will "never be nil", then make it a non-optional -- it's an optional for a reason. Using yolo unsafely should be met with the assumption that when something goes wrong, the "expected behavior" is to crash. Good thing you know every single failure path in your application, because you've just opted the compiler out of caring about it.
⚠️ Safe Yolo
"Safe" yolo is using force-unwrap in places where the only reason it should crash your application is because of a programming or "developer" error. It is a contractual agreement you are entering into with the compiler that you know what's up. A calculated risk.
✅ Mitigated Yolo
Mitigated yolo is yolo that has been removed in favor of proper use of either formally optional/non-optional storage. This allows the compiler to completely fact-check your work automatically, at least for expectations around object initialization. Normally these cases require explicit error handling around "failure" cases when optionals are nil.
😱 Silent Yolo
Mitigated yolo cases in which a formal optional has been used, but your nil failure cases are left entirely un-handled.
Yolo❗️ by Example
Storyboard Unpacking
Let's get this one out of the way since it's going to come up.
@IBOutlet weak var MyButton: UIButton❗️
When you create an IBOutlet from a storyboard or a xib, Xcode will create this little gem.
- You are accessing them before
awakeFromNib
has been called - You didn't connect that
IBOutlet
to your storyboard
These are both programmer errors and should crash your application.
Rogue Optionals
func completionCallback(optionalObject: Any?, error: ErrorType?) {
guard error == nil else {
// handle the error
return
}
// do some processing
self.nonOptionalFunc(optional❗️)
}
Someone (of course not you) has indicated that some variable is "optional" and you want to pass it into a function that requires a non-optional parameter.
if let
and explicitly handling both logical branches:
func completionCallback(optionalObject: Any?, error: ErrorType?) {
guard error == nil else {
// handle the error
return
}
if let unwrappedOptional = optional {
// do some processing
self.nonOptionalFunc(unwrappedOptional)
} else {
// explicitly handle this case since something clearly went wrong
}
}
Inline optionals
let width: CGFloat = self.imageCache.imageForKey(imageKey)❗️.size.width
This is likely the most common occurrence of yolo in your codebase. Something will break here at some point in the future. Go on, do a quick audit of your codebase for !.
. I'll wait.
Unfortunately, you're probably not going to like this one very much. Depending on how important or "required" the result of the work you are performing, you'll have to handle it in a couple of different ways:
let width: CGFloat
if let cachedImage = self.imageCache.imageForKey(imageKey) {
width = cachedImage.size.width
} else {
// Handle "failure" here
}
var width: CGFloat = 0.0
if let cachedImage = self.imageCache.imageForKey(imageKey) {
width = cachedImage.size.width
}
Weak Storage
weak var delegate: UITableViewDelegate❗️
When defining storage properties using weak storage, you should never* yolo these properties. weak
storage are auto-zeroing properties that can become nil at any time when their owners release them.
You are entering into a contract with the compiler that you cannot in any way fulfill without intimate knowledge of the owner of delegate
.
weak var delegate: UITableViewDelegate?
Type Casting
let superObject = (givenObject as❗️ MyObject)
if let
the type cast and handle the "failure" depending on your architecture and requirements.
if let superObject = givenObject as? MyObject {
// guaranteed superObject is MyObject and non-optional
}
else if superObject == nil {
// superObject was nil -- but we didn't crash!
} else {
// givenObject was non-nil , but failed a cast to MyObject -- handle failure depending on requirements
}
Throwing Errors
try❗️ errorThrowingProcessing(ofObject: obj)
Even if this is "temporary" code -- both you and I know this will make it into production. You're entering into a contract here where failures that result from this function choking will be met with an application crash.
do {
try errorThrowingProcessing(ofObject: obj)
}
catch {
// Handle error or rethrow -- it was thrown for a reason
}
let resource = try❗️ unpackCriticalBundledResourceWithName(resourceName)
* unless you seriously know what you're doing and have written a lot of documentation around this code. judicious use of emoji is recommended with this type of documentation