I was just updating my tutorial series on developing iOS 8 apps in Swift for Beta 5, and I was looking at this bit of code:
var name = result["trackName"] as? String if !name? { name = result["collectionName"] as? String }
The code is setting name to the result[“trackName”], if it can successfully cast to String. If it can’t then maybe it’s blank, or null, or something’s just not right.
So, then on line 2, it checks to see if name is not set still by using !name?, checking to see if it’s nil. If so, then switch over to the “collectionName” key. Sometime’s this example API call returned names of songs as “trackName”, and sometimes it returned names of albums in the key “collectionName”
All this code is really just checking/setting the variable name in order to get something in there. But it’s sort of verbose, and the Xcode 6 Beta 5 update actually made optionals no longer conform to BooleanType any more, so this code gives an error on this line:
if !name?
ERROR: Type ‘String?’ does not conform to protocol ‘BooleanType.Protocol’
Apple’s Beta 5 update notes mentions this change, and the new preferred method of performing this check. From the release notes:
“Optionals no longer conform to the BooleanType (formerly LogicValue) protocol, so they
may no longer be used in place of boolean expressions (they must be explicitly compared with v != nil). ”
So I could update my code to say something like this:
var name = result["trackName"] as? String if name == nil { name = result["collectionName"] as? String }
This works, but beta 5 added one more thing, the nil coalescing operator! That’s quite a mouthful, and if you use that term at parties people will think you’re super smart. They probably will not want to talk to you much after that, but it’s just because they are jealous.
Okay, so back on topic. The nil coalescing operator…
Basically this new operator does the same check as above, but it does it in short form using an operator, ??
Placing it between two operands does the check with the left hand value. If it’s nil it then prefers the right-hand value. So for example, I can rewrite my statement above as the following:
let trackName = result["trackName"] as? String let collectionName = result["collectionName"] as? String let name : String = trackName ?? collectionName
This code is a bit more concise, with pretty much the same output. I can also easily add a third option to fall back on if for some crazy reason neither of these variables are set:
let name = trackName ?? collectionName ?? "Untitled"
By doing this we get an added benefit: the type “String” can now be inferred 🙂
The only downside is that both the trackName and collectionName lookups are performed. We could eliminate this as well by rewriting this as a single (slightly harder to read) line.
let name = (result["trackName"] as? String) ?? (result["collectionName"] as? String) ?? "Untitled"
But personally, I think it’s cleaner to leave it as three lines, even if it is slightly less performant.
Looking at the definitions for the ?? operator I see this:
infix operator ?? { associativity right precedence 110 } ..... func ??<T>(optional: T?, defaultValue: @autoclosure () -> T) -> T
You’ll notice the left-hand operand is of type T?, and the right-hand operand is of type @autoclosure () -> T. Or in other words, the left-hand side takes an optional, and the right-hand side takes basically any expression (any closure, except you don’t need to specify it’s a closure, the @autoclosure keyword turns it in to one.) That means the right-hand operand can take just about any expression, such as:
isEnabled = screenSaverDisabled ?? self.lastTimeSeenMoving > (CACurrentMediaTime()-5) // -or- var someValue = someOptional ?? self.someMethod() ?? self.someProperty
The only requirement is that each of these operands are of the same type.
Happy coalescing!
Excited to learn more? Take a look at my upcoming book.