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?