Swift optional
Optional type in Swift handles the absence of a value. Optional say either "there is a value, and it equals x" or "there isn't a value at all".
To understand about optional type we must know about type safety and type inference and how they are being used combinedly in Swift.
Type Safety
Swift is a type-safe language. A type safe language encourages us to be clear about the types of values our code can work with. If part of our code expects a String
, we can’t pass it an Int by mistake.
var message: String
/// This will throw a compiletime error stating "Can not assign value of type 'Int' to type 'String'". Because we already specified that it's going to be a String.
message = 22
Type Inference
If we don’t specify the type of value we need, Swift uses type inference to work out the appropriate type. Type inference enables a compiler to deduce the type of a particular expression automatically when it compiles our code, simply by examining the values we provide.
/// Variable 'n' is inferred to be of type 'Int'.
var n = 9
/// It will work, because 11 is an 'Int'.
n = 11
Type Safety & Type Inference together
/// 'Type inference' will happen here, we won't specify that this is an 'Int', the compiler itself will find out.
var n = 9
/// It will work, because 11 is an 'Int'.
n = 11
/// Because of 'Type Safety' ability we will get an error message stating: "Can not assign value of type 'Int' to type 'String'".
n = "Hello world"
Let's understand optional type by an example
An optional data type allows nil
to be a value for a variable or constant.
In Swift whenever we use a variable we write var n = 0
or var greet = "Hello"
this style of using variable is known as defining variable.
However there are instances when we do not have a clear definition of a variable in very early stage of the program and want the variable to have a value at some later stage of the program, this style of using variable is also known as declaring variable.
Well using optional we can create a simillar situation where we declare variable or constant without assigning something to it at first, in which case it is empty. To define emptyness Swift offers a reserved keyword called nil
, which stands for empty or nothing.
Hence, if we don't want to assign acutal data to our variable at first we can write var n = nil
and that means that the variable contains nothing. However it will still throw compile time error stating "nil requires a contextual type". Here we assigned nil
at the first place which means empty and there is no data type for empty, thus variable n
doesn't have any data type and it does not follow the type safety and type inference norms.
In Swift, none of the data types include nil
as a valid value. Hence nil
is not a valid value for Int
, String
or any data type variable in Swift. That's why we can not assign nil
to a Int
, String
or any data type variable.
Well this is where Optionals came in to picture by adding ?
to the data type as var n: Int? = nil
makes it an Optional variable of type Int
, which means that the variable n
can either be an Int
or nil
.
We set an optional variable to a valueless state by assigning it the special value nil
:
/// n contains an actual `Int` value of 9
var n: Int? = 9
/// n now contains no value
n = nil
If we define an optional variable without providing a default value, the variable is automatically set to nil for us:
/// greeting is automatically set to `nil`
var greeting: String?
An optional is an enum of two cases
An Optional is a type on its own, actually one of Swift's super-powered enums. It has two possible values, None and Some(T), where T is an associated value of the correct data type available in Swift.
However, instead of using the .none
case we can use nil
to indicate the absence of a value.
enum Optional<Wrapped> {
/// The absence of a value.
case none
/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)
}
Thus, we can define the above greeting
variable as optional as well by using the enum:
let greeting = Optional.some("Hello world!")
print(greeting!.count)
How to find out whether an optional contains a value or not?
Optional binding is used to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable. Optional binding can be used with if and while statements to check for a value inside an optional, and to extract that value into a constant or variable, as part of a single action.
var n: Int? = nil
if ((Calendar.current.component(.day, from: Date()) / 2) > 0) {
n = 9
}
if let x = n {
print(x)
} else {
print("It's an odd day to get your lucky number.")
}
Forced unwrapping
There are instances when the optional does contain a value, we can access its underlying value by adding an exclamation point (!) to the end of the optional’s name. The exclamation point effectively says, “I know that this optional definitely has a value; please use it.” This is known as forced unwrapping of the optional’s value:
var n: Int? = nil
n = 9
if n != nil {
print("n has an integer value of \(n!).")
}
Hence, force unwrapping an optional either returns the value if it exists or triggers a runtime error when the optional is nil
.
Implicitly unwrapped optionals
An implicitly unwrapped optional is a normal optional behind the scenes, but can also be used like a non-optional value, without the need to unwrap the optional value each time it’s accessed.
We can declare optional variables using exclamation mark instead of a question mark. Such optional variables will unwrap automatically and we do not need to use any further exclamation mark at the end of the variable to get the assigned value.
var greeting: String!
greeting = "Hello, Swift!"
if greeting != nil {
print(greeting)
} else {
print("greeting has nil value")
}
An example of using guard
to unwrap an optional in Swift
let greeting: String? = "Hello world!"
guard let value = greeting else {
return
}
print(value)
Optional chaining
The process of querying, calling properties, subscripts and methods on an optional that may be nil
is defined as optional chaining. Optional chaining return two values:
- 1. if the optional contains a
value
then calling its related property, methods and subscripts returns values . - 2. if the optional contains a
nil
value all its its related property, methods and subscripts returnsnil
.
Since multiple queries to methods, properties and subscripts are grouped together failure to one chain will affect the entire chain and results in nil
value.
Optional chaining as an alternative to forced unwrapping
Optional chaining is specified after the optional value with ?
to call a property, method or subscript when the optional value returns some values.
Example for optional chaining with !
class Department {
var name: String
init(name: String) {
self.name = name
}
}
class Employee {
var id: String
var name: String
var department: Department?
init(id: String, name: String, department: String) {
self.id = id
self.name = name
self.department = Department(name: department)
}
}
let emp = Employee(id: "101", name: "Harish", department: "R&D")
print("Department name is \(emp.department!.name)")
Example for optional chaining with ?
class Department {
var name: String
init(name: String) {
self.name = name
}
}
class Employee {
var id: String
var name: String
var department: Department?
init(id: String, name: String, department: String) {
self.id = id
self.name = name
self.department = Department(name: department)
}
}
let emp = Employee(id: "101", name: "Harish", department: "R&D")
if let departmentName = emp.department?.name {
print("Department name is \(departmentName)")
} else {
print("Department name cannot be retreived")
}
Conclusion
Optionals are in the core of Swift and exist since the first version of Swift. An optional
value allows us to write clean code with at the same time taking care of possible nil
values.
With Swift’s Optional
type and its various features, we can often end up with a very concise code having higher chances of being correct.