With optional chaining, if I have a Swift variable
var s: String?
s might contain nil, or a String wrapped in an Optional. So, I tried this to get its length:
let count = s?.characters?.count ?? 0
However, the compiler wants this:
let count = s?.characters.count ?? 0
My understanding of optional chaining is that, once you start using ?.
in a dotted expression, the rest of the properties are made optional and are typically accessed by ?.
, not .
.
So, I dug a little further and tried this in the playground:
var s: String? = "Foo"
print(s?.characters)
// Output: Optional(Swift.String.CharacterView(_core: Swift._StringCore(_baseAddress: 0x00000001145e893f, _countAndFlags: 3, _owner: nil)))
The result indicates that s?.characters
is indeed an Optional instance, indicating that s?.characters.count
should be illegal.
Can someone help me understand this state of affairs?
An optional in Swift is basically a constant or variable that can hold a value OR no value. The value can or cannot be nil. It is denoted by appending a “?” after the type declaration. For example: var tweet: String?
Optional binding stores the value that you're binding in a variable. 2. Optional chaining doesn't allows an entire block of logic to happen the same way every time. 2. Optional binding allows an entire block of logic to happen the same way every time.
You can use optional chaining when attempting to call a method which may not exist. This can be helpful, for example, when using an API in which a method might be unavailable, either due to the age of the implementation or because of a feature which isn't available on the user's device.
Optionals: Swift introduced optionals that handle the absence of a value, simply by declaring if there is a value or not. An optional is a type on it's own! enum: An enumerated type is a data type consisting a set of members of the type.
Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil. Optional chaining in Swift is similar to messaging nil in Objective-C, but in a way that works for any type, and that can be checked for success or failure.
Optional chaining return two values − Since multiple queries to methods, properties and subscripts are grouped together failure to one chain will affect the entire chain and results in 'nil' value. Optional chaining is specified after the optional value with '?' to call a property, method or subscript when the optional value returns some values.
Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil.
Since multiple queries to methods, properties and subscripts are grouped together failure to one chain will affect the entire chain and results in 'nil' value. Optional chaining is specified after the optional value with '?' to call a property, method or subscript when the optional value returns some values. Optional Chaining '?'
When you say:
My understanding of optional chaining is that, once you start using
?.
in a dotted expression, the rest of the properties are made optional and are typically accessed by?.
, not.
.
I would say that you are almost there.
It’s not that all the properties are made optional, it’s that the original call is optional, so it looks like the other properties are optional.
characters
is not an optional property, and neither is count
, but the value that you are calling it on is optional. If there is a value, then the characters
and count
properties will return a value; otherwise, nil
is returned. It is because of this that the result of s?.characters.count
returns an Int?
.
If either of the properties were optional, then you would need to add ?
to it, but, in your case, they aren’t. So you don’t.
Edited following comment
From the comment:
I still find it strange that both
s?.characters.count
and(s?.characters)?.count
compile, but(s?.characters).count
doesn't. Why is there a difference between the first and the last expression?
I’ll try and answer it here, where there is more room than in the comment field:
s?.characters.count
If s
is nil, the whole expression returns nil
, otherwise an Int
. So the return type is Int?
.
(s?.characters).count // Won’t compile
Breaking this down: if s
is nil
, then (s?.characters)
is nil
, so we can’t call count
on it.
In order to call the count
property on (s?.characters)
, the expression needs to be optionally unwrapped, i.e. written as:
(s?.characters)?.count
Edited to add further
The best I can get to explaining this is with this bit of playground code:
let s: String? = "hello"
s?.characters.count
(s?.characters)?.count
(s)?.characters.count
((s)?.characters)?.count
// s?.characters.count
func method1(s: String?) -> Int? {
guard let s = s else { return nil }
return s.characters.count
}
// (s?.characters).count
func method2(s: String?) -> Int? {
guard let c = s?.characters else { return nil }
return c.count
}
method1(s)
method2(s)
On the Swift-users mailing list, Ingo Maier was kind enough to point me to the section on optional chaining expressions in the Swift language spec, which states:
If a postfix expression that contains an optional-chaining expression is nested inside other postfix expressions, only the outermost expression returns an optional type.
It continues with the example:
var c: SomeClass?
var result: Bool? = c?.property.performAction()
This explains why the compiler wants s?.characters.count in my example above, and I consider that it answers the original question. However, as @Martin R observed in a comment, there is still a mystery as to why these two expressions are treated differently by the compiler:
s?.characters.count
(s?.characters).count
If I am reading the spec properly, the subexpression
(s?.characters)
is "nested inside" the overall postfix expression
(s?.characters).count
and thus should be treated the same as the non-parenthesized version. But that's a separate issue.
Thanks to all for the contributions!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With