Store a non-optional or an optional to an optional
The basics. This isn’t rocket science:
optItem = 5 // optItem is now .some(5) optItem = optValue // optItem is whatever optValue is
Summary
How often do you do this? All the time
How ridiculous is this approach? Not at all
Store an optional to a non-optional
Many functions and methods that return optional values. When you use try?
with throwing, that returns optional values too. You often need to store those results to a non-optional variable or property.
To do this, you test for nil and then store an unwrapped version of any non-nil results. Here are a few approaches.
Conditional Binding
Use if let
to conditionally bind the optional and then perform the assignment:
if let optItem = optItem { item = optItem // if optItem is non-nil }
You can also use if case
if you really want to although these do exactly the same things as if let
:
// Sugared optional if case let optItem? = optItem { item = optItem // if optItem is non-nil } // External let if case let .some(optItem) = optItem { item = optItem // if optItem is non-nil } // Internal let if case .some(let optItem) = optItem { item = optItem // if optItem is non-nil }
Nil Coalescing
You can use nil coalescing to provide a fallback value:
item = optItem ?? fallbackValue
If you don’t want a fallback value, you can use the item’s original value:
item = optItem ?? item
A slight caution. I don’t know if the compiler optimizes away the “assign self to self” case. If it has to do both the check and assignment, this may be less efficient than the if let
approach.
Also, I’m not giving this high marks for efficiency or readability because I think it’s worth the extra if let
words to make clear the intent that you only update item if optItem
is non-nil
Summary
How often do you do this? Often
How ridiculous is this approach? Not at all
Update optional only if optItem is non-nil
This variation describes a scenario where you skip updates when an existing optional is nil. In this case, nil means “don’t touch this optional”. I can count up to zero the number of times this scenario has ever arisen for me.
The obvious solution:
if optItem != nil { optItem = newValue }
The extremely weird solution using a ?
marker:
optItem? = nonOptionalValue
In this use of ?
, the rhs must be a non-optional, guaranteed at compile time. This is a pretty obscure Swift language feature. (Thanks to Joe Groff for reminding me it existed.)
Or you could do this (which is kind of silly) for “test for non-nil receiver” assignment:
if let optValue = optValue { optItem? = optValue }
In this example, the rhs of the ?
-powered assignment has to be non-optional. Conditionally binding the optional enables you to use it with ?.
Madalin Sava offers the following simple alternative. It gets high marks for parsimony, low marks (as everything in this section does) for non-obvious outcomes:
optItem? = optValue ?? optItem!
Summary
How often do you do this? Never
How ridiculous is this approach? Extremely
Update optional only if optItem is nil
This variation can best be described as “set once, use mostly”. Once assigned to a non-nil value, the optional should never be overwritten. The simplest approach is to check for nil before assignment:
if optItem == nil { optItem = newValue }
Or burn an operator, which you probably won’t want to do:
infix operator =?? : AssignmentPrecedence // "fill the nil" operator public func =??<T>(target: inout T?, newValue: T?) { if target == nil { target = newValue } } optItem =?? newValue
See also: SE-0024
Summary
How often do you do this? I don’t do this but I can see the utility when hooking up assets that you don’t want to overwrite. This is kind of a mad-world version of implicitly unwrapped optionals but one where you test to ensure you’ll never change them again, not one (as with IUO’s) where every successive change has to be a non-nil value.
How ridiculous is this approach? Not ridiculous but also not common.
Update optional only if new value is non-nil
This scenario basically mimics implicitly unwrapped optionals but with added safety and no IUO crashing. You always test for non-nil so once set to a valid value the optional will never return to nil.
Limit updates to non-nil new values and discard nil assignments:
if let newValue = newValue { optItem = newValue }
Or this (which feels wasteful as it performs a re-assignment for nil-values, doesn’t it?):
optItem = newValue ?? optItem
or burn an operator, which again you probably won’t want to do:
infix operator =? : AssignmentPrecedence // "assign non-nil values" operator public func =?<T>(target: inout T, newValue: T?) { if let newValue = newValue { target = unwrapped } }
See also: Swift Evolution.
Summary
How often do you do this? I don’t but I can see how people might want to use this with non-IUO optionals.
How ridiculous is this approach? Not ridiculous but also not common.
I’m sure I’ve gotten some of this write-upwrong. Tell me and I’ll fix.
16 Comments
The thing I keep wanting is a sugared way to take a non-optional value and make it optional based on a bool condition. When combined with ?? it is really powerful…
Can you explain more about what that might look like? Because it’s easy enough to do let optional = bool ? nonoptional : nil
I think what he means is that you sometimes want to replace a value by another in case it doesn’t satisfy a certain condition. So, something like this: https://gist.github.com/anonymous/0c97a1547c90c3dda5ad947e516d23b9
Also, instead of:
if let optValue = optValue {
optItem? = optValue
}
you can do:
optItem? = optValue ?? optItem!
Here force unwrapping is safe because it will only happen if optItem is not nil.
I prefer reassigning a variable to if conditionals.
I’m hesitant to do that because what if both optValue and optItem are nil?
If optItem is nil, the rhs expression will not be evaluated, just like any optional chaining/assignment.
Argh. You’re right.
x.flatMap { y = $0 } where x is optional ????
Looks to me like these two are equivalent, assuming sufficient optimization:
if optItem == nil { optItem = newValue }
optItem = optItem ?? optValue
I was thinking about this while trying to figure out which operators makes the most sense in place of ‘=??’ and ‘=?’, and was considering ‘??=’ for the former because of the surface similarity to operators like ‘+=’:
item += value // same as item = item + value
optItem ??= optValue // same as optItem = optItem ?? optValue
However, I don’t really think ‘??’ is a the same kind of operator as ‘+’ (and all of the others that can used like ‘+=’), so I don’t think ‘??=’ makes sense after all. Warming up to your choices ‘=??’ and ‘=?’ after all.
(copy & paste error, feel free to omit one ‘after all’ in my last paragraph)
That articles like this even need written confirms my notion that the lack of nil messaging in Swift was a bad decision. It’s the one thing that makes objective-c easy to deal with, as a human. I find debugging items with nils a lot easier than having to write this convoluted code always checking for nils or unwrapping objects.
Thanks for taking the time to write this.
Hey Eric,
Thanks for clearing all the doubts, although I am knowing all of them but still it clears the concept. Well, I saw that you came up
if case let
that really new for me. Can please clarify it more detail.what’s the difference between the below codes and when to use them appropriately?
if case let optItem? = optItem {
item = optItem // if optItem is non-nil
}
if let optItem = optItem {
item = optItem // if optItem is non-nil
}
Thanks 🙂
Regarding the “Update optional only if new value is non-nil” case:
Following the first example:
if let newValue = newValue {
optItem = newValue
}
I’d normally just shorten that, because both “optItem” and “newValue” are optionals and must be of the same type.
The outcome is the same.
optItem = newValue
Say optItem is “foo”. Then assign nil. Your solution, optItem is now nil. My solution, optItem remains “foo”.
I would love to see an Sugar for value of optional, if not nil. Otherwise default value.
Here is a production example:
static func createFbUser(fbId: String, name: String?) throws -> FbUser {
let graph = "mutation createFBPerson($input: CreateFbUserInput!) {createFbUser(input: $input) {changedFbUser {fbID,name,id}}}"
let variables_name = name != nil ? "\"\(name!)\"" : "null"
let variables = "{ \"input\": { \"fbID\": \"\(fbId)\", \"name\": \(variables_name) } }"
Here, I would be very glad to something like
let variables = "{ \"input\": { \"fbID\": \"\(fbId)\", \"name\": \(name !? "null") } }"
Sorry, what I meant is
let variables = "{ \"input\": {\"name\": \(name !? "\"\(name)\"": "null") } }"
Which, if name has a value would produce “{ “input”: { “name”: “Maxim Veksler” } }”
but if name does not have a value would produce “{ “input”: { “name”: null } }”