AssertingNilCoalescing Operator !!
I believe that Swift needs one more operator to deal with optionals, in this post I present my concept to make dealing with them better and safer. It isn't a beginners introduction to optionals or Swift. There are plenty of good ones out there already.
infix operator !! { associativity right precedence 110 }
public func !!<A>(lhs:A?, rhs:@autoclosure()->A)->A {
assert(lhs != nil)
return lhs ?? rhs()
}
How this code works will be explained near the end of this post so don't feel you need to understand it now. It adds an additional operator !!
to a Swift project for dealing with optionals. It is designed to retain the benefits of strong typing proving the validity of some aspects of the code while documenting the expectation that the left hand argument will not be nil and exposing (in debug builds only) if that belief about the code is wrong.
Unlike other operators it behaves differently in a debug build than a release build. In release builds it is exactly the same as the built in ??
nil coalescing operator return the argument on the left unless it is nil in which case the right hand argument will be returned.
What are Optionals and how can I use them [Edited to add this section]
Having not found a good full coverage guide I was planning another post going through all the operators and approaches for dealing with optionals I was planning another post to cover that. Luckily this comprehensive post has appeared and if you aren't clear about what Optionals are or how you can operate on them in Swift I recommend reading the linked post before you dive into the rest of this.
How I Use Optionals
I really love optionals. The making explicity and provable by the type system whether or not variables can be nil is a great thing. That doesn't mean that they can't be a nuisance to deal with. Apple's frameworks were designed with Objective C in mind and nils are common. While in many cases you can convert them at source there are places where you really need optionals.
I try very hard never to use force unwrapping !
and only do it after an explicit nil check and preferably then wrapped in a function.
What I never use is is implicitly unwrapped optionals. The only time I would even consider it is if I know it will be set very shortly after the object containing the optional is initialised and it will never become nil again. If I can avoid in any way I do. The strong initialisation rules make it tricky sometimes but it is usually possible (lazy variables are often the solution). (Colin Eberhardt has good post on dealing with initialization difficulties).
The powerful tools of optional chaining (for lvalues not just rvalues) and nil coalescing (??
) mean that you don't need to have deeply nested if let
statements in almost all cases.
ViewControllers and @IBOutlets
The place I find optionals most pervasive and hard to factor out is user interface code that contains IBOutlets to interface between the design created in Interface Builder and the code that works with the objects. The problem is that the objects that the outlets reference are not instantiated at the same time as the ViewController but are only present after viewDidLoad has been called.
The code generated creates the IBOutlets as optionals but it creates them with !
to indicate that they are implicitly unwrapped optionals. This allows access to them as if they were non-optional variables which keeps the code tidy but it is definitely not safe. As mentioned they are nil until viewDidLoad and they may also be set to nil again later. It is also possible that the outlet is not properly hooked up, either because it or the object that it refers to is deleted in Interface Builder or the same class may be used with multiple different view designs and some may not link all the outlets.
What I do is manually change all the IBOutlets to proper ?
optionals and deal with that through the code as I need to using a combination of optional chaining, if let
and nil coalescing.
What is Missing
I didn't realise that I was missing anything until I mentioned on Twitter that I had raised a radar about Interface Builder creating IBOutlets as implicitly unwrapped and @brentdax responded pointing out that the benefit of the implicitly unwrapped optionals is that they cause a crash on an error instead of silently hiding it. This didn't change my practices immediately but it was a nagging concern that culminated led me to inventing this solution of the AssertingNilCoalescing operator last night. [Note: I haven't had time to do a literature search yet and this may already be a well known concept. I may well not be the first inventor of this.]
What !!
does
Precondition Check 0 - Do you understand "??
"?
If you've got this far it is important that you understand the normal Swift nil coalescing operator ??
. Apple's documentation is reasonable but assumes an understanding of force unwrapping. What it does is that it returns the argument on the left unless it is nil in which case it returns the argument on the right. The type of the argument on the left is the optional variant of the the type of the object on the right. It is syntactic sugar for the ternary operator with a not nil condition.
b ?? c // is equivalent to
b != nil ? b : c
The expressions result can be assigned to a variable or used in a function argument if you want.
func printString(s:String) {
println(s)
}
var a:String? = nil
// printString(a) - This wouldn't compile
// printString(a!) - This would compile but crash
printString(a ?? "A is nil") // This compiles and works printing "A is nil"
Precondition Check 1 - Do you understand "assert()"?
Assert is used to add a runtime check of a condition that will stop execution if the condition isn't true. However this check only takes place in debug builds. Apple's Swift team wrote an excellent blog post about how it works. This is used to document and declare the expected conditions at points in the code that will be checked and enforced causing a crash for the developer to lead them to cases they did not consider or catch when the code changes to make the assumptions no longer hold while minimising the impact to the customer as the code will carry on from them (and also the expression to check will not be evaluated at all so there will be no performance impact in production).
Back to what !!
does
So understanding ??
and assert as you now do it makes sense to say that !!
has exactly the same behaviour in release (non-debug) builds. However in debug builds it will raise an error in the event of a nil left expression rather than ever return the right hand value.
result = b !! c
// is exactly equivalent to:
assert( b != nil )
result = b ?? c
How does it work
Quite a lot of Swift's more advanced features are demonstrated in this short function so I'm going to go through line by line.
// This line declares that we want the !! function to usable as an
// operator, how it associates and the precedence. These associativity
// and precedence settings are exactly the same as those for ??
infix operator !! { associativity right precedence 110 }
// This is the actual function.
// <A> indicates that the function is generic, in this case A is not
// restricted and can be absolutely any type.
// lhs:A? - The lhs has to be an expression with type of the optional
// version of A.
// rhs:@autoclosure()->A - The rhs is an @autoclosure which means that
// the expression is not evaluated before the function is called (the
// usual case) but converted to a closure that can be called as
// required or skipped entirely. The result of the rhs expression must
// be of type A and not an optional type.
// ->A indicates the function returns an A (not an optional A).
public func !!<A>(lhs:A?, rhs:@autoclosure()->A)->A {
// Use the standard Swift alert. DEBUG only and will stop execution
if lhs is nil
assert(lhs != nil)
// If the assert above hasn't fired use the built in ?? operator and return the result.
return lhs ?? rhs()
}
When is !!
useful
!!
definitely complements replace ??
as in many cases a nil case on the lhs is not an error condition and is an expected value. Howeer having a different syntax allows you to be clear about where you are being purely defensive because it shouldn't be nil (use !!
) and where it is expected that a nil value could occur (use ??
).
Assert Nil What?
Asserting nil coalescing operator. It does what it says, sometimes it is best not to be too imaginative with naming.
My recomendations
My recommendation is that if you currently use the implicitly unwrapped optionals or force unwrapping operator (both use !
) that you stop in almost all places and switch to real optionals and use !!
whereever you would have unwrapped before and provide either a safe value (possibly newly initialised instance) on the right hand side or especially with numeric values you may be able to provide an estimate.
Every time you force unwrap or use an implicitly unwrapped optional you are disabling/bypassing the proofs that the Swift compiler would normally do on your behalf. This new operator gives you another tool to help you avoid that and still get additional runtime checks done in debug.
Casting
Just like force unwrapping I never do forced casts. What I instead do is the conditional cast followed by the !!
return tableView.dequeueReusableCellWithIdentifier("cellID", forIndexPath: indexPath) as?
CustomTVC !! CustomTVC(frame: CGRectZero)
The conditional cast as?
returns nil if the object is not an instance of the type specified and then the asserting nil coalescing operator kicks in. The above is actually a perfect example of when it can be used because the type of cell returned by dequeueReusableCellWithIdentifier
depends on that set in Interface Builder if there is a typo in the cellID or a in the value of the ID in the storyboard it will cause failures and you want to know in development.
Why !!
not $%^$
I chose !!
because of the similarity to ??
and the fact that it is to be used in places that !
for force unwrapping would be appropriate suggested that I stick to that operator. It documents that it should never be nil like !
would but offers the safe fallback like using ??
.
Testing
The operator is tricky to test because of the assert. I haven't yet written a test as such for the debug case with a nil argument (suggested approach is to spawn a process to check for the assert in) but I've wrapped a couple of other basic tests and the code in this project but I don't really suggest that you drop the code in as a framework as it is such a small amount of code. It probably belongs within a bigger library of utilities or can be directly dropped into your project.
Results
I did a very quick search and audit of ??
operators in my app and replaced those where I felt that it should never be nil. It revealed one bug and one case where I needed to revert to ??
because it was accessing the optional before the view loaded. The bug was that an outlet had been hooked up from one view in Interface Builder but another sharing the same class hadn't set the the outlet up. The good default value had hidden this bug.
Next steps
I may replace some "if let" clauses with an alternative that will assert if not. That way the form of the code will document whether nil is expected or just a theoretical risk.
Request To Apple
I've raised a radar (19117378) asking Apple to add such an operator to the standard set. It is too early to know if they will do so but I think having a common and consistent way to do this would be good so the syntax makes sense to everyone if !!
is going to have documentation value.
The above is my opinion...
...and some of it is opinionated. I'm interested to learn from your opinion, email, Tweet or add a comment. Let me know if any parts are unclear or even gibberish. Also if you you have some links to really good articles on optionals and the tools for dealing with them I will try to add rather than using my rushed descriptions of how they work. I may post an article on all the tools in detail later but that wasn't the focus of this one. [Edit: as mentioned before this comprehensive post means that I don't feel the need to write a slightly less thorough version any more. Although I might write an opinionated piece about how I do things with those tools.
Update - Feedback
Firstly this is a valuable link to an article about the benefits of Swift in terms of safety in a real project that was posted before my original post and I had read it. I didn't link to it although I possibly should have because while I found it a good post it confirmed existing views with a well written example case study. In my it doesn't say anything that would suggest !! is a bad idea (actually the contrary) although that wasn't @owend's view.
It is fair to say that feedback that I have seen so far (8th December) has overall been mixed with more negative than positive. Much of the conversation resulted from @NatashaTheRobot tweeting the post (and she also included it in this week's newsletter - thanks Natasha)
Also @dimpiax liked the overall concept but disliked that it was implemented as an operator.
Much of the concern was about different behaviour in production and that testing should be on the release version and that the app would get into unexpected states. In my view these are important things to think about but really apply to any use of assert.
I'll try and post a follow up with a more thorough response later this week. The short version is that my mind hasn't been changed yet although clearly !! isn't the operator to use in every cases, sometimes it is better to crash (rather than writing corrupt data for example) and it may be good to create your own version that can log errors to your backend in production if the nil case does get hit. I would re-iterate that cases of nil should be considered serious bugs and fixed but that having a safe fallback for when that bug happens isn't a bad thing.
Thanks for all the feedback, especially where you disagree. It is good to be challenged.