Optional chaining

Hi,

I have a syntax as below -

let favoriteColors2 = ["Rubul" : "Red", "Jeniffer" : "Pink"]

let jenifferColor = favoriteColors2["Jeniffer"].lowercased()

the Xcode ice shows me an error message as "Value of optional type 'String?' must be unwrapped to refer to member 'lowercased' of wrapped base type 'String'
Chain the optional using '?' to access member 'lowercased' only for non-'nil' base values. Fix
Force-unwrap using '!' to abort execution if the optional value contains 'nil'. Fix ".

I was pondering why it says as Optional , as favoriteColors2 variable is already declared (inferenced) & defined too. Thus making it compulsory to prefix a "?" just before the dot (.) on the second line of the above code.
Could someone kindly clarify the Why of this case pls?

Thanks in advance.

Good question. The issue is that swift can't know if the key "Jeniffer" exists in favoriteColors2. If the key doesn't exist, it has to return something. So though favoriteColors2 is a Dictionary<String, String>, getting an item cannot return just a plain String because the key might not exist. So it returns a String? (optional String). If the key is not found then it will be nil.

Thank you. So, does this especially apply in case of (such a sort of ) hash map declaration / definition and it's respective access only or otherwise ?

I see that , essentially I can declare and define those vars as below cases :- ?

var favoriteColors2: [String : String]? = ["Rubul" : "Red", "Jeniffer" : "Pink"]
let jenifferColor = favoriteColors2?["Jeniffer"]?.lowercased()

Or

var favoriteColors2: [String? : String?] = ["Rubul" : "Red", "Jeniffer" : "Pink"]
let jenifferColor = favoriteColors2["Jeniffer"]??.lowercased()

Or

var favoriteColors2 = ["Rubul" : "Red", "Jeniffer" : "Pink"]
let jenifferColor = favoriteColors2["Jeniffer"]?.lowercased()

Thanks again.

This one says that favoriteColors2 is optional. You can set it to nil, in which case there is no dictionary to look values up from. So you first need to ask "is there a table?", then "what's the table's value for "Jeniffer"?

This one says that the keys and values of favoriteColors2 are optional, so nil is a valid key and value. You can set favoriteColors2[nil] = "Blue". It's kind of awkward to set a nil value through the subscript, but you can write favoriteColors2.updateValue(nil, forKey: "Rubul") so that "Rubul" is still present in the dictionary with a value of nil.

This is almost certainly what you want. Just write your dictionary normally. The only thing that is optional is the value you look up from the table.

This isn't anything special to dictionaries / hash maps. Swift has a strong focus on safety; it want to prevent developers from accidentally crashing their programs. The goal of Optionals is to make return types explicit and require developers to deal with cases that might lead to crashes. Of course, you can opt out of this safety by force unwrapping (the ! postfix operator). As a developer, this can take a little getting used to at first; but once you're used it, you will love the confidence that when you deal with variable typed say String, you'll know that it MUST always contain a string.

Optionals aren't special either. Unfortunately, sometimes the syntactic sugar added to optionals can make things a little unclear at first. Optionals are just an enum with two cases: .some and .none.

The following are equivalent:

let optStringFullSugar1: String? = nil
let optStringNoSugar1: Optional<String> = Optional.none

if optStringFullSugar1 == optStringNoSugar1 {
    print("equal")
}

and likewise:

let optStringFullSugar2: String? = "hello"
let optStringNoSugar2 = Optional.some("hello")

if optStringFullSugar2 == optStringNoSugar2 {
    print("equal")
}

Note, the sugared version is preferred. Plus it saves a lot of typing.

Also, dictionary subscripting, the [foo] operator, isn't special either. Subscripting is just a function and, if you wish, you can added subscripting to your types. But the key point is that it's just a function, and like all functions it must have a return type.

In Swift we have 3 main ways to indicate that a function has failed: returning an Optional wrapping our success return type, throwing an exception, and, new to Swift 5, returning a Result wrapping our success return type.

All that is happening here is that the a Dictionary<Key, Value>'s subscript get function returns an Optional to handle cases where the Key doesn't exist in the dictionary.

So, these are the same:

var favoriteColors2: [String : String]? = ["Rubul" : "Red", "Jeniffer" : "Pink"]
var favoriteColors2 = Optional.some(["Rubul" : "Red", "Jeniffer" : "Pink"])

so:

let jenifferColor = favoriteColors2?["Jeniffer"]?.lowercased()

Is the same as:

var jenifferColor: Optional<String> = Optional.none
switch favoriteColors2 {
case Optional.some(let colors):
    switch colors["Jeniffer"] {
    case Optional.some(let color):
        jenifferColor = Optional.some(color.lowercased())
    case .none:
        jenifferColor = Optional.none
    }
case .none:
    jenifferColor = Optional.none
}    

Does that make sense?

3 Likes

yes. overcome'd it, thanks!!

Terms of Service

Privacy Policy

Cookie Policy