Update (26th January 2015) See Beyond `if let` for video of me presentating of much of this content.
Dealing with optionals is unavoidable in any Swift program that interfaces with the Cocoa or CocoaTouch libraries. Realistically that is all of them with a very few limited exceptions. Handling them is often annoying a although I am very glad of the rigour that they enforce on me. In fact I like the rigour so much that I never use implicitly unwrapped optionals (variable declared with !) and I use force unwrapping only in very limited circumstances.
In almost all cases I use combinations of optional chaining and nil coalescing operators with `if let` used where they don't do the job. In this post I'm going to explain how and where I use the different options and give some examples.
Note that this is very much a "How I do it" post and not a "How you should do it" post. It is also what I do this week which may evolve further in future. I take a somewhat radical approach and others may prefer approaches that fail earlier rather keep running in error conditions to the choices I take which are intended to keep running in most circumstances. Do note that there are definitely cases where it is better to stop than continue in an error condition especially where there is risk of corrupting data, losing money or even more serious consequences. Please choose a domain appropriate response. My recent domain has been a consumer app handling network data and not complicated and corruptible user documents or financial or safety critical and the choices you make about operating in unexpected state are different in each one.
The expectation in this post is generally that you both understand what optionals are and that you are aware of the operators and essentially how they work. If you are not comfortable with them the Swift book from Apple is excellent and there is also this post that explains them and the options around them. This post is about the when to choose which approach issue rather than documentation of what the operators are and what they are and do (although I may cover that in places).
Why I Like Optionals - Conversion from Objective-C to SwiftThis is some Objective-C code from a 3rd party library I recently encountered an error in. It compiles without warnings even with the static analyzer and looks OK at a first glance but the Swift equivalent would not compile without explicit force unwrapping and would have revealed the bug.
If you haven't spotted it the crash comes if there is a key in the error codes dictionary that isn't in the fieldNames dictionary. `[fieldNames objectForKey:key]` returns nil and the `[message appendString: nil]` crashes.
Now you may say that you wouldn't make that mistake or that your testing would catch something like that and maybe it usually would are you sure you will NEVER make that mistake or that someone on your team wouldn't change something that meant your assumed pre-conditions weren't true or.... Because the compiler's type check is a proof of the type safety of your program and it won't get it wrong (in favour of danger) unless you explicitly tell it to.
It is very far from a proof of correctness and isn't a proof against crashing (try indexing beyond an array) but it is a valuable tool and it is full proof of that aspect in a way that test short of exhaustive testing of all possible inputs can achieve. If your test coverage is excellent optionals can reduce the amount of tests you need as you only need to test for nil cases if function takes an optional.
This is a fairly straight translation of the original Objective-C code and there are some minor tweaks I would make if I was writing it for production but as it is allows easier comparison to see the particular changes that have been made without having to discuss, map, reduce and string formatting.
There are two places in this method where optionals are handled in this short method. In the first the handling is exactly the same as the original code which accomplished the same thing by using Objective-C's nil messaging behaviour where a message sent to nil simply returns nil. In the second where the Objective-C version passed a potentially nil value as an argument (and crashed on nil) the Swift version substitutes a default value.
The first optional handling is for `result` which is an optional (because the userInfo property is optional and there is a conditional cast either would be enough for it to be optional) and I use optional chaining to unpack the whole key path in the `if let` line. `valueForKeyPath` isn't available from Swift so optional chaining is combined with optional casting to assign to codes. As this is an `if let` the code following it will only be executed if the result of the whole chain (`(result?["error"]? as? [String:AnyObject])["codes"] as? [String:AnyObject]`) isn't nil. There are three conditions that could lead to a nil result and each is indicated by a question mark. The `result` itself could be nil, there could be no value for the `error` key or the cast might not be possible (because of type mismatch including nil result). There probably should be an else clause on the `if let` so that in the event of one of these things happening we don't return "Please enter the following fields:" with no following fields but I have not done so to keep the similarity with the Objective-C version. Also this is a possible outcome if the codes dictionary exists but is empty so that should probably be answered too.
The other optional handling is when the the field name is looked up for the key. In this case I have used nil coalescing to provide a default value. I've chosen to use the key itself because this is at least somewhat likely to recognisable to the user (in the production crash the key was actually "email"). Because fieldNames[key] returns an optional some handling of optionals is required by the compiler. For the same crashing behaviour as the original you could force unwrap like this: `message += fieldNames[key]!` but at least you have the ! as a marker that something dangerous is happening. This is actually also a good case for the !! operator if you expect fieldNames to have complete coverage as then you will be alerted in development if you hit a case you haven't covered while still providing a reasonable result in production if you hit this case.
Note that in the absence of some handling the Swift code will not compile so you must do something.
In this example the error argument is non-optional so if that could be nil (and it probably could be in the Objective-C version) it must be dealt with upstream before this method is called. This is a sensible approach because this method doesn't really make sense when there is no error argument so it is better to refuse to be called in such circumstances. However if we wanted it to be optional it would only need a `?` after the argument type (`NSError?`) and one where the `error` is used in the first line to optionally chain on that too.
If you want more information about the benefits of Swift's strictness this article is a good read.
Optional Tools and How I Use ThemThe initial example actually gives a good short example of the use most of the tools for dealing with optionals with examples of optional chaining, nil coalescing and `if let` but I'm going to through my uses of each separately here for a good look. It is important to really consider whenever dealing with an optional but what will happen if it is nil not just to make it compile. Even if it is unlikely to be nil you should try to choose a safe option whether that is using a safe alternative default value, doing nothing or taking special error handling code paths. Sometimes crashing (rather than continuing with risk of corrupting data or losing user work a crash or a clean closedown, error log/report and then abort may be the correct approach).
Implicitly Unwrapped OptionalsI don't use these as I feel that they hide but do not remove danger. The most likely places that you have these are where you have created outlets from Interface Builder or in places where Apple's nullability audit was not complete when you did an autocompletion of a method to implement. In either case I delete the `!` and replace it with a `?` and then just handle as a normal optional.
Even in the case of an IBOutlet there a numerous things that can go wrong. Firstly the outlet will only be valid after `viewDidLoad` has been called and it is possible that the variable could become nil at a later stage (the compiler won't complain if you assign nil to it). Secondly there are risks if the associated view or the outlet are removed in InterfaceBuilder or another view is laid out InterfaceBuilder and attached to the same class there is a risk that one or both views won't have all the outlets properly connected (and at times this may be fine). I've done this sometimes with tableView cell prototypes where I can have a couple of variants with different identifiers and different layouts but with similar contents.
Force Unwrapping - !I almost never do this at all. The only time I use it is when writing tweet size functions or in circumstances where it immediately follows a nil check and even where I do that I would wrap it in a function rather than have it embedded in the main code. I the only use of force unwrapping in the Giftstar app that I have recently developed is in the array extension method shown below (actually this isn't quite true, when I started the project just a few weeks ago I wasn't quite as strict and there were a few cases although all followed explicit nil checks and I've just refactored them now).
This is a utility function to perform a operation that results in an optional return on each item in an array and to return an array of all the results which aren't nil. It actually meets both criteria for using ! because it is tweet-able (if you remove the comment) and a wrapped up utility function that does a nil check before force unwrapping.
I use the above utility in places where I'm loading data and I want to gather the good/valid data. I'm using it both in JSON parsing and CoreData loading. The called function (the transform) logs and reports errors but the result I need is only the good and valid data so the transform returns nil on an error so is just excluded from this result.
Optional ChainingThis probably the technique that I use most frequently. In Swift beta 5 (early August 2014) Apple made this substantially more powerful than the first betas and included the ability to use optional chaining on the lvalue in an assignment (they also added nil coalescing in the same release). What this allows is to have a chain of operations that will only be performed if the result to the left of the operator is non-nil. It allows the same things that could be done with Objective-C's nil messaging behaviour (only made explicit) and also a few additional tricks. Where it can't be used is within arguments (unless combined with nil coalescing).
What needs to be remembered is that where an rvalue result is nil with optional chaining the return value will be nil so you may just be passing the issue further along. Where an lvalue is nil it will be as if the operation didn't take place. In many situations this is fine but you do need to think about it.
I make heavy use of optional chaining where possible as it is far tidier than making `if let` pyramid's of doom. The downside of this approach is that there is a significant amount of code that may not be executed and doesn't give any warnings but at least it avoids any nil access crashes that would caused. I'm currently thinking about adding an asserting optional chaining operator to the asserting nil coalescing operator that I developed last week (maybe using `#` and changing the `!!` to `##`) but I don't think it could support lvalue’s.
Nil Coalescing ??The nil coalescing operator ?? allows an alternative value to be provided if the argument on the left hand side is nil. This is particularly useful in two contexts. Firstly it allows safe assignment where you want to assign from an optional value where you can set a safe alternative. Secondly it allows handling passing optionals as arguments without needing `if let` again provided there is a safe alternative or fallback. It can also be used where you know that the main result will not be nil but you can provide a safe alternative if that knowledge ever proved wrong.
This is definitely an operator to be careful of and it should probably only be used when you really expect nil may be possible and it is handled reasonably. Consider !! if it should never fail. Also especially for the function arguments you should really think about whether it is clear and obvious or you should really be using `if let`.
Comparison == !=You can directly compare an optional value with a non-optional value or a literal without unwrapping it first. Maybe this is obvious but I had overlooked this for a while. Obviously this can work with optional chaining so that you can easily make comparisons even when data is nested though several optionals. In testing you can’t use XCTAssertEqual on an optional and a non optional but you can do XCTAssert(optional == nonOptional) without unwrapping.
This is actually a special case of the implicit conversion of non-optionals to optionals which means that you can directly pass a non-optional to function expecting an optional but it is worth calling out as an example of where unwrapping is unnecessary.
map over an optional - (this section added as an update)
This is an approach that I haven't yet used but I plan to. I'm not sure if I came across it late on Natasha the Robot's blog or somewhere else (did anyone else blog about it in late December - let me know and I'll add the additional credit) but it is a nice way of handling cases where you need to use the optional as an argument or not call the method and it also covers the cases where the value needs to be unwrapped multiple times in the expression.
This is close to an `if let` statement without the else except that it returns a value as an optional which can be very useful. This is definitely going in my toolbox.
if letSometimes this is right way to go. It gives you a explicit test and opportunity to handle the nil case properly as you want in the `else` branch. The downsides are the verbosity and the additional nesting of code that it introduces. I use it when the variable is essential (often it needs to be an argument or is needed multiple times in an expression [Update - See above map approach]) or I want to take particular action when the value is nil rather than safe inaction. In many cases the optional unwrapping is combined with a conditional cast using `as?`. I actually use this quite a bit when I am expecting nil values to be something that will happen frequently or at least in cases where it is not an error in the software (such as when I'm parsing external data).
One thing that I try to do is do a single `if let` on the result of an optional chaining sequence and conditional casts rather than doing it at every level unless lots of different items are needed at an intermediate level.
NamingHaving tried a few approaches such as `if let variableName = variableNameOpt` I am now leaning towards the approach of having the non-optional alias the optional as in `if let variableName = variableName` where the right hand variableName is of optional type. This approach has since been recommended in the RayWenderlich Swift Style guide.
You can use the pattern matching of the switch statement to define cases for multiple variables in a tuple. This allows you to handle multiple optionals without nesting `if let` statements and also handle other conditions at the right time. I'm not going to go into details here as it may be worth a small blog post on its own and it isn't actually a technique that I have used heavily because in most cases I haven't needed nested `if let` statements anyway.
Other `if let` alternative
Colin Eberhardt's pyramid's of doom (mentioned previously) describes adding functions to support an `if let` like approach across multiple optionals.
Weakness of My Approach
The downside of not using Implicitly Unwrapped Optionals and force unwrapping is that there is potential for paths that are not expected to be taken and not noticed in debugging. It would be good practice to assert or raise errors in debug when unexpected nils are encountered. Also this would document the expectation. This is a trade off I accept for having the type checker ensure that two whole classes of crashes are eliminated (type errors and nil references). To mitigate it the !! operator is one step but I may yet go further along that path.
Using the !! - My additional operatorIn my previous
post I described a new operator that I have invented and added to my projects. It functions like the ?? nil coalescing operator except in debug mode it asserts that the argument is non nil. I use this in places where I consider it an error to get a nil value but I can provide a suboptimal alternative in the event that this error occurs.
I may also add an additional operator at some point just to assert non-nil status when using optional chaining.
JSON Parsing ExampleThis is a trivial JSON parsing example that mostly shows the use of `if let` but has some conditional chaining too. This is a case where as a developer almost all the optionals could be nil (it might be a server error but the client app needs to be robust to invalid data. Note particularly that some elements (name and id) are considered essential to have a meaningful product while the synopsis and description can be nil and will just be ignored/excluded from the resulting array. Note also the typealiases declared. They make it MUCH easier to write code handling JSON.
This shows a way to handle JSON data that doesn't require third party libraries (many of which are excellent even if some use more operator overloading than I am entirely comfortable with). This is a simplified version of some real code I'm using dealing with a more complicated JSON object but this simplified version still retains the essential techniques.
I hope that someone finds this useful, it has grown bigger than I originally intended. Note also that like everyone else (outside Apple) I have only 6 months Swift experience and my practice is still changing as I learn and try more things. It very much is intended as "you can do this" rather than "you should do this". The particular thing I think you should take away is to consider VERY carefully every time you have an implicitly unwrapped optional or a force unwrap whether it is really necessary and if there is a better option.
Feedback welcome as always. There is quite likely to be a sentence or a section that I have broken off halfway through somewhere in this post but if I keep checking it I may never post it but if you find it let me know. Likewise if anything is wrong or unclear please let me know, I'll be happy to fix and clarify. Also if you disagree add a comment or let me know a better approach.