When I was first learning Swift, optionals were one of the hardest things to wrap my head around. Forced unwrapping?? WTH! Or maybe you’ve seen “fatal error: unexpectedly found nil while unwrapping an Optional value”. From talking to other developers it sounds like I’m not alone in this. But, now that I’ve been writing Swift for over a year I’ve come to love optionals!
Optionals provide a very elegant solution for working with `nil` or unset variables. One of the biggest issues I originally had when trying to understand them was the difference between “?” and “!” and when to use one over the other. Now that I’ve been using them for some time I’ve come to think about the difference this way.
“?” means that you, the developer, expect the value to sometimes be “nil”, and will handle that case.
“!” is a promise to yourself, other developers using your code, and your users that the value should never be “nil”.
If you try to access a variable with “!” that’s unset, your app is going to crash. Where as “?” means a variable can be “nil”, but that may not always be the best thing. As a developer you want to know when something unexpected happen so that during development you can catch problems before they become problems for your users.
I think there are two tendencies when it comes to not fully leveraging optionals. One is to use “!” on everything so you don’t have to use “if let” or “guard”. But this can lead to unexpected crashes for you and your users when values are “nil”, even if it’s acceptable for them to be. The other tendency is to use “?” on everything so that your swift code works more like how Objective-C handles “nil” by gracefully doing nothing.
Swift’s handling of “nil” with “!” and “?” is one of the reasons I think it’s more powerful and produces more stable code than Objective-C does. Because it allows you to specify when a value can be “nil”, and when it should never be “nil” so you can better catch issues before they get to your users.
Don’t take any of these examples as gospel :) Deciding when you’re app should gracefully handle an unexpected state and when it should go nuclear and crash in a unexpected state will be different for every app.
Lets assume you have a Config.plist file that has to exist with specific values for your app to function. Using “!” to “force unwrap” the file path and values within could be used to enforce that the file and expected values exist when the app starts up.
This code promises that Config.plist should exist in your app bundle, if it doesn’t the app will crash immediately. Also if Config.plist does exist but can’t be loaded as a dictionary the app will crash. It should be pretty clear to you, or any other developers working with the codebase, what the issues is if the app crashes here.
as! vs as?
Lets then say there is a “copyright” value that MUST be in the config file.
Using “as!” means that the value returned from the “copyright” key MUST exist and MUST be a string.
For “as?”, lets assume there is a value in the config data that’s not required. Like an optional note to the user.
This will set the text of the “userNoteLabel” to the value with the “user_note” key if it exists and is a “String”. If this is not the case then the text will gracefully be set to “nil”.
Next, lets assume there MAY be a tint color in the config data defined with “r”, “g” and “b” values.
["tint_color": ["r": 0.4, "g": 0.6, "b": 0.2]]
Lets break this down. Line 1 unwraps the value from the “tint_color” key only if it exists and is a dictionary of “String” keys and “CGFloat” values.
If this is true, “tintColor” is set and we move to the inner “if let” statement on line 2 which makes sure that all three RGB values exist. This comma syntax was introduced in Swift 1.2 and is a nice way to unwrap multiple values in one “if let” block.
If all of the RGB values exist then the window tint color is set to a new color created with the RGB values.
The guard statement was introduced in Swift 2 and was a welcome addition to help unwind the often nested “if let” statements that occured in Swift 1.
In the above example we only have two levels of “if let” statements which isn’t too bad. But lets see what this could look like with “guard”. We’ll turn the above code into a function that takes the config data and returns the optional tint color.
Lets break this down. Like the “if let” example above, the first “guard” statement checks if the value from the “tint_color” key exists and is a dictionary of “String” keys and “CGFloat” values. If it’s not the function will early exit and return “nil”.
Next we move onto the second “guard” statement on line 6 to make sure we have all the RGB values. Again, if any of them are missing the function will early exit and return “nil”.
Only if both guard statements pass and we have all the RGB values we need do we finally make the tint color and return it.
Using “guard” helps keep your code readable and understandable because you can read it in chunks, in this example each “guard” statement must pass before you move on.
Last tip, the double “??” is a nice little trick to provide fall-through values. Now that we have our “tintColor” function we could write something like this to set the window tint color.
If we get a color back from our “tintColor” function then we’ll use it, if not we’ll fall-through to the system red color.
I’ve found this especially useful for collection and table view data source counts.
Optionals provide an elegant balance when handling “nil”, either by doing nothing or crashing the app. Both have their place, I hope this overview helps you get more comfortable with them.
Thanks to Aubrey Johnson for the original idea for this post and for helping me make sure it all made sense ;)