?? and ?.
Imagine you are going to visit friends with a box that may turn out to be empty, and you take a spare gift in case you need it. If there is something in the box, you give that, and if it is empty, you give the spare one. Either way, you have a gift in your hands.
Often a program needs not two branches but one number: if the box is empty, a spare one will do. With if let this takes several lines, and the ?? operator handles it in one:
let apples = Int("7") ?? 0
let pears = Int("seven") ?? 0
print(apples + pears) // 7Let's go through the program line by line:
Int("7")gives a box with7, and??takes7out of it.Int("seven")gives an empty box, and??takes the spare value0.applesandpearsare ordinary numbers of typeInt, so they can be added: the program prints7.
A default value: ??
The expression Int("seven") ?? 0 has three parts:
- on the left, the optional being opened;
??, the operator that checks whether the optional holds a value;- on the right, the default value: it is used when the left side is
nil.
The result of ?? is no longer an optional but an ordinary value: apples has type Int, not Int?. That is why it can be added and printed without warnings.
?? looks only at nil
The number 0 is not emptiness. A box with a zero holds a real value, and ?? keeps it:
let plums = Int("0") ?? 10
print(plums) // 0The text "0" holds a number, so Int("0") gives a box with 0, and the spare 10 is not needed. In some languages a similar expression replaces zero and empty text too, but ?? in Swift replaces only nil.
A spare value of the same type
Here the spare-gift picture is off. In real life a spare gift can be anything: flowers instead of a book. In Swift, the default value must be of the same type as what the box holds. The line Int("x") ?? "none" does not compile: binary operator '??' cannot be applied to operands of type 'Int?' and 'String'.
Optional chaining: ?.
You can find out how many characters a string has: "Neo".count is 3. The number of characters is read with a dot after the string. But what if the string sits in an optional? For example, it is a user's nickname, and the user may never have set one.
That is what ?. is for: "if there is something in the box, take its count". This is called optional chaining:
var nickname: String? = nil
print(nickname?.count ?? 0) // 0
nickname = "Trinity"
print(nickname?.count ?? 0) // 7
print(type(of: nickname?.count)) // Optional<Int>Let's see what happens:
- While the box
nicknameis empty,nickname?.countdoes not readcountat all and givesnil.?? 0replaces it with zero. - After
nickname = "Trinity"the box holds a string, and?.reads itscount, which is7. type(of:)shows thatnickname?.counthas typeOptional<Int>, that is,Int?.
The answer of a chain always comes in a box: if the box might hold no text, there might be no character count either. That is why ?. is often followed by ??, to get an ordinary value.
If an assignment or a call follows ?. and the box holds nil, Swift skips it: nothing is assigned and nothing is called.
Interviewers like to ask what type nickname?.count has. The right answer is Int?, not Int.
Common mistake
The character count of an optional was read with a plain dot:
let letters = nickname.count // ✗error: value of optional type 'String?' must be unwrapped to refer to member 'count' of wrapped base type 'String'A String? has no count of its own: only the string inside the box has one, and the box may be empty. Put ? before the dot: nickname?.count gives Int?, and nickname?.count ?? 0 gives a plain Int.
Key points
a ?? bgives the value from the optionala, and if it holdsnil, the default valuebof the same type.??replaces onlynil: a box with0is not empty, andInt("0") ?? 10is0.nickname?.countreadscountonly if the optional holds a string, and otherwise givesnil.- The result of optional chaining is an optional again:
nickname?.counthas typeInt?.