p0lAris p0lAris - 4 months ago 21
Swift Question

Swift 3.0 Optional Chaining

I am new to swift so this is definitely a newbie level question. I was pondering on some Swift 3.0 documentation where I noticed a potential error. I am wondering if the example is incorrect (or ambiguous) or I am in fact missing out on some guideline.

See http://swiftdoc.org/v3.0/type/Optional/ the section about Optional Chaining.


OPTIONAL CHAINING

To safely access the properties and methods of a wrapped instance, use the postfix optional chaining operator (
?
). The following example uses optional chaining to access the
hasSuffix(_:)
method on a
String?
instance.

if let isPNG = imagePaths["star"]?.hasSuffix(".png") {
print("The star image is in PNG format")
}
// Prints "The star image is in PNG format"



AFAIU,
imagePaths["star"]?.hasSuffix(".png")
is only supposed to safely unwrap
imagePaths
and run
hasSuffix()
only if
imagePaths["star"]
results in
Optional.some(wrapped)
. This implies that
isPNG
will be either
true
or
false
. Therefore, the implication of the above sample is incorrect where it implicitly claims that if this safely unwraps, then the value is always true.

Here are some examples to explain what I am talking about:

if let isPNG = imagePaths["star"]?.hasSuffix(".png") {
print("The star has png format")
} else {
print("The star does not have png format")
}

if let isPNG = imagePaths["portrait"]?.hasSuffix(".png") {
print("The portrait has png format")
} else {
print("The portrait does not have png format")
}
// "The portrait has png format\n"

if let isPNG = imagePaths["alpha"]?.hasSuffix(".png") {
print("The alpha has png format")
} else {
print("The alpha does not have png format")
}
// "The alpha does not have png format\n"


I am simply wondering if my current analysis is wrong or if SwiftDoc.org needs to change this particular example.

Answer

Your analysis is correct, and the example at SwiftDoc.org is at least misleading, as the following code demonstrates:

let imagePaths = ["star": "image1.tiff"]

if let isPNG = imagePaths["star"]?.hasSuffix(".png") {
    print("The star has png format")
} else {
    print("The star does not have png format")
}

// Output: The star has png format

The optional binding succeeds if

imagePaths["star"]?.hasSuffix(".png")

is not nil, and that is if the optional chaining could be executed, i.e. if imagePaths["star"] != nil.

A correct way to test for all situations would be

if let isPNG = imagePaths["star"]?.hasSuffix(".png") {
    if isPNG {
        print("The star has png format")
    } else {
        print("The star does not have png format")
    }
} else {
    print("No path in dictionary")
}

Of course this can be simplified in various ways (nil-coalescing operators, pattern matching, ...). For example

switch(imagePaths["star"]?.hasSuffix(".png")) {
case true?:
    print("The star has png format")
case false?:
    print("The star does not have png format")
case nil:
    print("No path in dictionary")
}

or

let isPNG = imagePaths["star"]?.hasSuffix(".png") ?? false

(More possibilities in the comments.)

Comments