Something About Optional Assignment

| /

We all know that in Swift, optional chaining is an important feature that can help us write concise and elegant code.

However, the optional assignment derived from the optional chain may have some strange phenomena when used.

The following discussion and code are based on Swift5.7

We often use optional chaining for value or assignment in code, such as

1
2
3
4
5
6
7
8
9
10
11
12
class Person {
var residence: Residence?
}

class Residence {
var numberOfRooms = 1
}

var person = Person()

person.residence?.numberOfRooms // => nil, nothing happened
person.residence?.numberOfRooms = 2 // => nothing happened

In the Swift documentation it is described as follows

Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil.
If the optional contains a value, the property, method, or subscript call succeeds;
if the optional is nil, the property, method, or subscript call returns nil.
Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

Optional chaining can actually be used in assignment operations, such as

1
2
3
4
var someThing: String?

someThing? = "x" // => nil, nothing happened
someThing = "x" // => x

If it is nil, the assignment operation will not be performed, and nil will be returned.

In some cases, it may play a role.

But when there are multiple optional types, things get a little more complicated.

Multiple optional types are not as rare as you might think, such as

Sometimes when you want to leave some placeholder keys in the dictionary, you may write like this.

Of course, I personally don’t recommend such a design type

1
2
3
var sth: [String: String?] = ["x": "y"]

print(sth["x"]) // => Optional(Optional("y"))

At this point, optional assignment may have some ambiguous things happen

1
2
3
4
5
6
7
8
sth["x"] = nil
print(sth) // => [:]
print(sth["x"]) // => nil

// but if
sth["x"]? = nil
print(sth) // => ["x": nil]
print(sth["x"]) // => Optional(nil)

So what actually happens is something like

1
2
3
4
if sth["x"] != nil {
sth["x"] = .some(nil)
// not => sth["x"] = nil
}

We will find a similar phenomenon when we reduce directly to double optional types

1
2
3
4
5
var tt: String?? = nil
tt? = "a" // => nil, nothing happened
tt = "b" // => Optional(Optional("b"))
tt? = nil // => Optional(nil)
tt = nil // => nil

To sum up, optional assignment is an operation that may be useful in some scenarios, but be sure to evaluate the possible risks when using it.

Multiple optional types may bring unexpected performance, which is related to the underlying implementation of Swift’s implicit wrapping, unwrapping, etc., and needs to be used with caution.