Shortly after my last post Segue from UITableViewCell Taps in Swift, Apple released Xcode 6.3 Beta 1, which includes Swift 1.2. There are many updates to the language that I was quite happy about, but let’s talk about my favorite one today, improved Optional Binding.
The new version allows you to bind multiple optionals, as well as check a boolean expression related to them in a single line, avoiding the nesting we currently must do when working with multiple Optional Bindings.
Multiple Optional Bindings — Avoid the “Pyramid of Doom”
So, I apparently called what everybody else calls the “Pyramid of Doom” the “Tower of Ifs” in my previous article, despite hearing of the Pyramid from the Edge Cases podcast, ah well. They were more talking about highly nested callbacks via blocks or closures, which isn’t exactly this, but the nesting required for multiple Optional Bindings in early Swift make the term appropriate in this context as well. Nonetheless, with this improvement, let’s look at what we wrote last time for prepareForSegue:
Before Multiple Optional Bindings Version
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == blogSegueIdentifier { if let destination = segue.destination as? BlogViewController { if let blogIndex = tableView.indexPathForSelectedRow?.row { destination.blogName = swiftBlogs[blogIndex] } } } }
Like I said before, I’m not a fan of that Pyramid of Doom there. Now, you can chain multiple if-let statements, which would result in the following code:
After Multiple Optional Bindings Version (First Refactor)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == blogSegueIdentifier { if let destination = segue.destination as? BlogViewController, let blogIndex = tableView.indexPathForSelectedRow?.row { destination.blogName = swiftBlogs[blogIndex] } } }
Since we only had 2 if-let statements, it doesn’t look a WHOLE lot better, but it does keep us from needlessly nest the code. This is especially important if there was an else associated with this if statement (then there would be elses nested in there, and that would look even worse). Thanks to Colin Eberhardt for pointing even more issues (and a little history on the term) on his post Tearing Down Swift’s Optional Pyramid of Doom. He also goes into some ways to deal with this before this change was implemented using some functional programming. It’s definitely an interesting read.
Multiple Optional Bindings with Normal Boolean Conditional
We could have used the where clauses discussed below to remove 1 more level of nesting in the above example, but that use is inconsistent with how where is used in other contexts, usually checking the objects it is working on, or components thereof. The segue identifier, nor the blogSegueIdentifier are components of the destination of blogIndex, so it seemed like bad practice to me. Now, we have a better option!
Now, we can start with a normal if-statement if we want, and then continue with Optional Binding when the initial if-statement evaluates to true, so, now, the above example becomes:
After Multiple Optional Bindings Version (Second Refactor)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == blogSegueIdentifier, let destination = segue.destination as? BlogViewController, let blogIndex = tableView.indexPathForSelectedRow?.row { destination.blogName = swiftBlogs[blogIndex] } }
There we have it. While the conditional itself is rather long, it is in ONE level of nesting. There isn’t much we would do if it was the correct segue, but the destination and blogIndex couldn’t be optionally bound, we don’t really need to have an else for each of those individual cases. If any of these evaluate to false, the whole conditional fails, and we don’t have to worry about dealing with multiple else clauses for the intermediate if-statements.
If you need those, you can do it the old way, but especially when you have many optionals to use, getting into 5 levels or more of nesting can get a bit… interesting to work with. Maybe somebody might want the intermediate levels when debugging, but all that really does here is let you know if the destination isn’t a BlogViewController (which really should be caught in testing, rather than with “else” clauses in production code), or if there is no selected row in the tableView (which shouldn’t happen because that is what starts this particular segue, unless it was cleared beforehand, I suppose).
If you had more than one segue you would probably use a switch statement instead of an if-statement. If you only have a small amount though, this could still be of use. You can read more about Switch statements in the earlier post Loops, Switch Statements, and Ranges in Swift.
Now we can have all of our Multiple Optional Bindings and conditional checking in one line. Is it almost as verbose as the original way? Yes, since we still have to write the code to actually do what we need (like reading the indexPath and casting the destinationviewController). However though, we do not have to nest multiple Optional Bindings inside themselves, and that’s the important part.
Conclusion
I am also a fan of the delayed initialization of constants (“let” values), requiring them to by initialized before they are used, but not necessarily when they’re declared. Type casting has seen some changes as well, and NSSet finally gets a Swift equivalent called Set. You can read about the new Set type in the newer post Swift Set Type.
As far as non-syntax changes, I am glad they have add incremental building, so that every Swift file does not need to be recompiled each time, particularly if only one has been changed. They have also better optimized many things under the hood, especially for Debug builds.
I am glad since the original writing of this post, that they changed the indexPathForSelectedRow from a method to a property, since it really was just a getter method in the first place. It might be just a couple less parentheses, but it still makes it look just a bit cleaner.
I hope you found this article helpful. If you did, please don’t hesitate to share this post on Twitter or your social media of choice, every share helps. Of course, if you have any questions, don’t hesitate to contact me on the Contact Page, or on Twitter @CodingExplorer, and I’ll see what I can do. Thanks!