A lookup returns an optional
You read a value from a dictionary with the same subscript you use to write one: dict[key]. The key might not be in the dictionary, so the result is not a plain value but an optional. For a [String: Int] the lookup has the type Int?. It holds the value when the key exists and nil when it does not.
var scores = [String: Int]()
scores["Alice"] = 95
scores["Bob"] = 87
print(scores["Alice"] as Any)
print(scores["Diana"] as Any)
print(type(of: scores["Alice"]))
Optional(95)
nil
Optional<Int>
Swift never returns a made-up value for a missing key and never crashes on the lookup. It hands you a nil and leaves it to you to handle it.
Three ways to get the plain value
Force unwrapping with !
A ! after the lookup turns the optional into a plain Int:
let aliceScore = scores["Alice"]!
print(aliceScore + 1)
96
This is only safe when you know the key is present. If it is missing, the program crashes at run time. Use ! for keys you have just inserted or can otherwise vouch for.
A default value
The subscript form scores["Diana", default: 0] returns the stored value, or the default when the key is missing. The result is a plain Int, not an optional. The ?? operator does the same for an optional:
print(scores["Diana", default: 0])
print(scores["Diana"] ?? -1)
0
-1
A default does not add the key to the dictionary. It only decides what the expression evaluates to.
Optional binding
To run code only when the key exists, use if let:
if let score = scores["Alice"] {
print("Alice has \(score)")
}
if let score = scores["Diana"] {
print("Diana has \(score)")
} else {
print("no score for Diana")
}
Alice has 95
no score for Diana
Inside the first block, score is a plain Int. This form also answers "does this key exist?" without risking a crash.
Checking whether a key exists
Since a missing key gives nil, you can compare the lookup with nil to test for a key. Two other properties give the whole collections: keys and values.
print(scores["Alice"] != nil)
print(scores["Diana"] != nil)
print(scores.keys.sorted())
true
false
["Alice", "Bob"]
A dictionary has no defined order, so scores.keys on its own may list the keys in any order. Calling sorted() makes the result predictable.
Reading every value
To read all the pairs, loop over the dictionary and unpack each pair into a key and a value. Sorting by key makes the order predictable:
for (name, score) in scores.sorted(by: { $0.key < $1.key }) {
print("\(name): \(score)")
}
Alice: 95
Bob: 87
In the loop, name and score are already plain values, so no unwrapping is needed.
The exercise
The starter code gives a dictionary named scores with three students. The lesson asks for one step:
- Below the dictionary, declare a constant
aliceScoreand assign itscores["Alice"]!. The lookup returns anInt?, and the!unwraps it to anInt. This is safe because"Alice"is in the dictionary.
let scores: [String: Int] = ["Alice": 95, "Bob": 87, "Charlie": 72]
let aliceScore = scores["Alice"]!
After this, aliceScore is 95. Because scores is a let, the dictionary is read-only here, which is fine for a lookup.
Common mistakes
- Forgetting the optional.
let n: Int = scores["Alice"]does not compile, because the right side is anInt?. - Force-unwrapping a missing key.
scores["Zed"]!crashes. Preferif letor a default when the key may be absent. - Misspelling the key. Keys are case-sensitive, so
"alice"is a different key from"Alice"and the lookup givesnil.
Key takeaways
dict[key]returns an optional,nilfor a missing key.- Use
!only for keys that must exist,?? valueor[key, default:]for a fallback, andif letto branch on presence.
