Swift loves it’s optionals (or nullables if you come from a C# background).
Whilst optionals may lead to fewer unhandled null reference exceptions or the likes they also lead to different ways of unwrapping values.
Declaring variables
First off let’s look at declaring a string type, we might write
let s: String = "Hello"
This is not an optional declaration and hence requires an initial value, i.e. remove the = “Hello” and the compiler complains or try to set to nil and the compiler complains – because this variables is expected to be non-nil. If we want to declare a type as possibly being nil then we need to mark it as such using the optional syntax.
Declaring optionals
let s: String? = "Hello"
The ? on a variable/value declares a type of optional string. We can also declare options in a long hand form
let s: String = Optional.some("Hello")
Equally, the following are equivalent
let s1: String?
let s2: String? = Optional.none
Basically we’re saying a “some” value or “none” value or nil may be assigned to this variable.
One thing to note regarding optionals is that to get the actual value assigned, you have to “unwrap” it from the optional. Basically think in terms of your variable is wrapped in an Optional type and to access it, you need to get (unwrap) the value out of that type.
Implicitly unwrapped optional
Interestingly Swift also has implicitly unwrapped optionals. Whereas an optional may have a value, no value or nil, an implicitly unwrapped optional may only contain a value or nil. Here’s the syntax for implicit optionals (note the trailing !)
let s: String!
The general use of an implicitly unwrapped optional is where maybe you declare a value initially but know it will be set to a value before use and will not be nil again.
Obviously we can still check if a value is nil or not, but the compiler will assume you know what you’re doing so if you’re using a value that’s nil the compiler will not warn you.
Note: If you come from a language like C# or Java, then in the example above, just think that this is more like the way you’d declare Strings in those languages and ofcourse you have the same issues regarding ensuring the value is non-nil/null in those languages.
Why not just using implicitly unwrapped optionals as they seem simple? The main reason is the same as why C# introduced nullable reference types. If you want to do things yourself, no problem, but if you want the compiler to highlight potential misused of variables, i.e. have the compiler warn if it looks like you might be using a nil. Then you’re best to only use implicitly unwrapped optionals sparingly and stick to the optional equivalent.
If you’re a little confused by implicitly unwrapped and optionals, let’s look at this code
var s1: String?
var s2: String!
print(s1.length)
print(s2.length)
Neither variable has been set to a value, hence both are none or nil. If we compile this code the Swift compiler will complain with
error: value of optional type ‘String?’ must be unwrapped to refer to member ‘length’ of wrapped base type ‘String’
print(s1.length)
Because the compiler has our back.
Now commenting out print(s1.length) and recompiling, all looks good to the compiler, but ofcourse when we run this code it’ll blow up with an error such as
Unexpectedly found nil while implicitly unwrapping an Optional value
Basically the compiler see’s the implicitly unwrapped optional essentially ignore it as we (the developer) told it that we know what we’re doing.
Unwrapping an optional as part of a conditional test
We’ll often wish to check if an optional is nil or not and then unwrap it to get the actual underlying value. Using the following syntax does both steps in one go
if let optionalValue = optionalValue {
// do something with the unwrapped optionalValue
}
Swift also supports the guard statement which is basically another condition operator so we can also unwrap in the same way as above, but with a guard, i.e.
guard let optionalValue = optionalValue else {
// failed to unwrap
return
}
// do something with the unwrapped optionalValue
Switch
We can use a switch statement on optionals as well, for example
let s: String? = "Hello"
switch s {
case .some(let s): print("SOME \(s)")
case .none: print ("NONE")
}
Using map with optionals
We can use map with optionals
let x: String? = "Hello"
let y = x.map { $0 + "World" }
// y is a String?
Unwrapping with !
In some situations you may know an optional value is not nil in which case you simply wish to unwrap the value without any conditional code in place, that’s when you used the ! operator to force unwrap – basically the developer is saying this is a valid value so just unwrap it. Ofcourse, if the developer is wrong then this will have unintended consequences.
The syntax is as follows
let optionalValue: String? = "Hello"
print(optionalValue!)
Optional chaining
We can use the optional chaining ?. operator on types, for example
let optionalValue: String? = "Hello"
print(s?.count)
The nil coalescing operator
Along with the optional chaining operator ?. we also have the nil coalescing operator
let optionalValue: String? = "Hello"
print(optionalValue ?? "NONE")