Table of Contents
What is Optional Chaining?
Optional chaining is a process we can use in conjunction with optionals to call the properties, methods and subscripts on optionals whose values may or may not be nil
. It essentially allows us to optionally execute different pieces of code depending on whether an optional contains a value or not. This helps make our code cleaner and allows us to dispensing with some of the syntax we saw in the last post. Let’s have a look at an example.
class DriversLicence {
var pointsOnLicence = 0
}
class Person {
var licence : DriversLicence?
}
In this example, I’ve got two classes; a DriversLicence
class with a single property called pointsOnLicence
(which obviously is always set to zero!) and a Person
class. The Person class contains properties for the persons first name, last name and an (optional) licence
property that references the persons drivers licence. I’ve declared the licence
property as an optional because some people have a drivers license and others don’t. I’ve also defined the Person
class to have a failable initialiser as I don’t want the class to be initialised if it was provided with an empty string for either the first name
or last name
properties.
Now that the classes are defined, let’s create a person:
let andy = Person()
Now, say I wanted to print out the number of points that I have on my license. In order to print out the points, I have to traverse the optional driversLicence
property in order to get to the associated DriversLicence
instance. The issue though is that the licence
property is an optional and I would normally have to check that it wasn’t nil
before trying to access the properties. One way of doing that would be to use optional binding:
if let licence = andy.licence {
print("Andy has \(licence.pointsOnLicence) points on his license")
} else {
print("Andy doesn't have a drivers licenses")
}
// prints "Andy doesn't have a licence."
Another alternative though is to use optional chaining.
Optional Chaining on Properties
Reading Properties Through Optional Chaining
As I mentioned just now, optional chaining allows us to access the properties of an optional variable or constant without the need to first check whether the optional contains a value of nil
.
With optional chaining, if the optional contains a value, the remaining code in the chain is executed, if it is nil
the chain is broken and the chain as a whole returns nil
. Here’s an example:
let pointsOnLicence = andy.licence?.pointsOnLicence
if let points = pointsOnLicence {
print("Andy has \(points) points on his licence")
} else {
print("Andy doesn't have a drivers licence")
}
// prints "Andy doesn't have a drivers licence."
There are a couple of things to notice with this example.
The first thing, is the ?
on the end of the licence
property in the first line of the example.
This question mark has a slightly different meaning to the one we would normally see when we use optionals. In this case, we use it at the end of an optional variable, constant or property (rather than on the end of a type). Here it means “if the optional has a value, access this other property, otherwise return nil”. This is similar meaning to the forced unwrapping operator (!
) we have used previously but in this case if the optional contains a value of nil
the chain will fail gracefully rather than crash your app.
In the case of the driversLicense
property in our example, this means that if it contains a value the chain would continue and we would access the pointsOnLicense
property otherwise the chain would break and it would return nil
.
The other thing to note in this example, is that the result of accessing a property via an optional chain, is itself always an optional. If you think about it, this is relatively logic.
At any point in the chain, one of the optionals may or may not contain a value of nil
. If they do, the chain is broken and at that point, the result of the chain would be equivalent to the value of the last link of the chain before it was broken (which would be the optional with a value of nil
).
If the chain was successful, it instead returns a value corresponding to the type of the last property in the chain.
So if you step back, the result of the chain could be either nil
or a value of a particular type. Sound familiar? Yep, you guessed it, an optional. In the case the example above, this means that the result of the chain is actually an Int?
rather than an Int
because of the fact that the licence
optional may return nil.
In this example, I’ve explicitly separated the result of the optional chain from unwrapping the resulting optionals value in order to print out the result. As you’ll see later on in this article though, you can combine them into a single line. Here though I wanted to emphasise that the optional chain is independent of optional binding. An important point to take on board.
Setting Properties Through Optional Chaining
Now, in addition to being able to retrieve the values of properties through optional chaining, we can also use optional chains to set them. In similar fashion to accessing properties via optional assignment, the assignment only occurs if the optionals within the chain all contain a non-nil
value:
var points = 3
andy.licence?.pointsOnLicence = points
In this case, the assignment actually fails because the licence
property currently contains a nil
value. This is all well and good, but the question is, how do we know whether the assignment worked or not?
Normally the assignment operator in Swift has no return type (to prevent it being accidentally used in place of the ==
operator) but if you’re following along in a playground you’ll see something strange.
When used in conjunction with optional chaining, the assignment operator in actually returns a value of type Void? (also written as ()? – an empty, optional, tuple), returning nil
if the assignment failed or an empty tuple (()
) if the assignment was successful.
We can therefore re-write our example above and make use of this fact to check whether our assignment was successful:
if let result = andy.licence?.pointsOnLicence += points {
print("Andy now has \(andy.licence!.pointsOnLicence) points on his licence.")
}
// Prints 'Andy now has 3 points on his licence.'
Let’s walk through it. On the right-hand side of the assignment operator we have our optional chain. Here we are using to set a value for the pointsOnLicence
property. As we saw above, the result of the expression is inferred to be an optional so where we combine it with optional binding to check whether the result was nil
or not. If the assignment was successful the result
constant is created, the value returned from the expression is assigned (in this case is an empty tuple (()
) and the contents of the if
statement is executed.
Accessing Subscripts on a Type Where The Subscript Returns An Optional Type
Now that we’ve got the basics of chaining on optional properties clear, let’s look at a slightly more complicated example. As you may know, some data types in Swift support accessing values through subscripts. Arrays and Dictionaries in Swift are both examples. Sometimes though, those subscripts return an optional type. Let’s look at an example.
Let’s first create a dictionary that maps car model (say a Honda Civic) to the minimum and maximum prices that might expect to pay. We’ll store the minimum and maximum prices as a tuple:
var catalogue = ["Honda" : (minPrice:10, maxPrice:100)]
When we access a value in a dictionary using a subscript, the value returned from the subscript is an optional. This is because of the fact that the key, the value we place between the brackets of the subscript, may not actually exist within the dictionary:
var honda = catalogue["Honda"]
In this case the variable honda
is inferred to be of type (minPrice:Int, maxPrice:Int)?
, an optional tuple containing two named values; minPrice
and maxPrice
.
You can see this for yourself if you option-click on the honda
variable in the playground.
Note: It’s not critical to this article but if you’re a bit hazy on using named values in Tuples, go and check out my post on tuples and come back here when you’re done. Don’t worry, I’ll wait.
Still here? Right, so we’ve got our optional tuple but say we wanted to use this within an optional chain and then print out the minPrice
component of the returned optional:
if let price = catalogue["Honda"]?.minPrice {
print("The minimum price is \(price)")
}
// Prints "The minimum price is 10"
Although it looks a little funky, it’s not much different to the optional chaining we’ve already seen. In this case, we access the catalogue
dictionary asking for the value associated with the key Honda Civic
. That returns an optional tuple as we just looked at. As we’ve seen previously, we then place a question mark (?
) after the optional and follow that with the remainder of the chain to access the minPrice value within the tuple. As with other previous chains, the value returned is itself an optional which we unwrap with optional binding and print the result.
We can also use the same syntax to set values as well. Here we again rely on the fact that when setting a property via optional chaining returns an optional tuple (Void?
or ()?
):
if let result = catalogue["Honda"]?.maxPrice = 30 {
print(catalogue)
}
// prints ["Honda" : (10, 30)]
Ok, relatively straightforward. Let’s look at another example though.
Accessing Subscripts That Return Optionals on Optional Values
This time, what about if the catalogue itself was an optional dictionary?:
var otherCatalogue : Dictionary? = ["Lotus" : (minPrice: 50, maxPrice: 200)]
Let’s take it slowly. First let’s work out how to access the contents of the dictionary:
var lotus = otherCatalogue?["Lotus"]
As you can see, because the dictionary itself is optional, we need to use a question mark after the name of the dictionary in order to access it’s contents. The question mark is placed between the name and the opening square bracket. In reality, this is actually an optional chain, it’s essentially saying: “If otherCatalogue
is not nil (i.e. the dictionary itself), access the subscript with the key Lotus
“.
As with the previous example, the value that is returned by the chain is inferred to be of type (minPrice:Int, maxPrice:Int)?
.
Ok, so far so good. Now, let’s combine it with accessing the minPrice
value within the tuple. We saw how to do that in the previous example. Again we’ll use optional binding to print it out:
if let price = otherCatalogue?["Lotus"]?.minPrice {
print("The minimum price is \(price)")
}
// prints "The minimum price is 40"
Notice the extra question mark? This time, we add a question mark after the subscript of the dictionary as well as before it. This second question mark is really saying: “Access the value of in the subscript ‘Lotus’ and if the value that is returned is not nil
, access the minPrice
value within the returned tuple.
Optional Chaining on Methods
So, we’ve looked at how we can use optional chaining to get and set properties via optional values, but the same concept can also be used to call methods. Let’s re-visit our example.
This time, I’ve extended the example to include the idea of a vehicle which may or may not have an owner
. I’ve also modified the Person
class to contain an array to hold the vehicles that an individual owns and finally the DriversLicence
class gains a new function that returns whether the licence is valid for a particular vehicle (it’s stubbed for now to always return true
).
class DriversLicence {
var pointsOnLicence = 0
func isValidForVehicle(vehicle : Vehicle) -> Bool {
// ...
return true
}
}
class Vehicle {
var owner: Person?
}
class Person {
var licence : DriversLicence?
}
Now, as I mentioned, we can also use optional chaining to conditionally call a method on an optional type. In the following example, we use optional chaining to determine whether my drivers licence allows me to drive a car:
let andy = Person()
andy.licence = DriversLicence()
let car = Vehicle()
car.owner = andy
if let canDriveVehicle = andy.licence?.isValidForVehicle(car) {
if (canDriveVehicle) {
print("Andy's licence allows him to drive the car.")
} else {
print("Andy's license doesn't allow him to drive the car.")
}
} else {
print("Andy doesn't have a licence.")
}
// Prints "Andy's licence allows him to drive the car."
So what is happening here? In similar fashion to accessing properties via an optional chain, the code to the right of the optional in the chain (in this case the call to isValidForVehicle()
) is only executed if the value in the licence
optional is not nil
. If licence
does contain a value, the chain returns the value obtained from calling the isValidForVehicle()
method (a Bool
) otherwise it returns nil
. The result of the chain is an optional value of type Bool?
.
As we’ve seen many times though, in order to access the value in an optional we need to unwrap it. In this example then, I’ve again combined the optional chain with optional binding to access the contents of the resulting optional. The canDriveVehicle
constant is therefore only defined if the result of the chain is not nil
. If it does contain a value, the first branch of the if
statement is then executed and we evaluate the value contained in that constant (either true
or false
) and print out whether I can or can’t drive the car.
In this case, due to the fact that I stubbed the isValidForVehicle()
method to always return true, it looks like I’m in luck, I can drive the car.
Multiple Levels of Chaining
Beyond using optional chaining to traverse chains containing a single optional, we can extend this idea for chains containing multiple optionals. This applies to accessing properties, calling functions or even accessing subscripts on a type (such as when accessing the contents of an optional array).
For example, say I took our car
instance above and wanted to find out how many points the owner of the car had on their licence:
if let points = car.owner?.licence?.pointsOnLicence {
print("The car's owner has \(points) points on their licence.")
}
// Prints "The car's owner has 0 points on their licence."
As you can see here, the optional chain now contains multiple optionals, not just one. Despite this, all the same rules apply though.
The optional chain itself returns an optional. The chain will return nil
if any of the optionals in the chain are nil
or the value of the pointsOnLicence
property otherwise. In this case, the return type is still of type Int?
(rather than Int??
or some other variant) as things don’t become more optional just because we’ve traversed more than one optional in the chain.
We can also call methods with multiple optionals in the chain as well. For example, let’s check if the owner of the car has a licence that allows them to drive it:
if let canDriveVehicle = car.owner?.licence?.isValidForVehicle(car) {
if (canDriveVehicle) {
print("The owner of the car has a licence that allows them to drive it.")
} else {
print("The owner of the car doesn't have a licence that allows them to drive it.")
}
} else {
print("The car either doesn't have an owner or the owner doesn't have a drivers licence.")
}
// Prints "The owner of the care has a licence that allows them to drive it."
Optional Chaining on Optional Protocol Requirements
Ok, we’re nearly there but there is just one more thing I want to look at before we wrap up. So far, in all the examples we’ve looked at I’ve used optionals for things that might not return a value. Beyond this though, optional chaining can also be used to access properties and methods that might not actually exist such as with optional protocol requirements.
When defining a protocol, some methods or properties can be marked as optional. Currently, we do this in Swift by prefixing the protocol declaration with @objc
and then using the optional
keyword before the property and method declarations:
@objc protocol MyProtocol {
optional var myVal : Int { get set }
optional func optionalMethod()
}
Now, let’s declare two classes that conform to this protocol. The first will implement both the optional property and optional method whilst the second won’t. In the case of the first class, we also have to annotate the property and method with the @objc
keyword to ensure that they satisfy the requirements of the @objc
protocol:
class classA : MyProtocol {
@objc var x : Int = 0
@objc func optionalMethod() {
print("Optional Method Called")
}
}
class classB : MyProtocol {}
Next, let’s declare two instances of these classes. In both cases, we’ll refer to them via their protocols rather than refer to the classes directly. This will allow us to test out the optional nature of the property and method:
var a : MyProtocol = classA()
var b : MyProtocol = classB()
Right, we’re all setup, let’s look at accessing the x
property first.
Accessing a property that has been marked as optional within a protocol is simple, it’s much like accessing a property that isn’t marked as optional except the type of the value returned will always be an optional:
let aValue = a.x
if let aValue = aValue {
print(aValue)
}
// prints 0
let bValue = b.x
if let bValue = bValue {
print(bValue)
}
// Doesn't print anything.
Here, both aValue
and bValue
are both inferred to be of type Int?
. Only the a
results in something printed out though. This is because only classA
has actually implemented the x
property. In the case of classB
we attempt to access the property, which fails, and nil
is returned. This is an example of optional chaining gracefully failing.
So what about methods? Let’s have a look:
a.optionalMethod?()
// Prints "Optional Method Called."
b.optionalMethod?()
// Doesn't print anything.
As you can see, when calling methods that are marked as optional within a protocol, we again have to use a question mark. This time, we use the question mark between the name of the method and the parentheses. Again, it’s a mini-optional chain. The call to execute the method (the parentheses) is only executed if the method exists. This is subtly different from placing the question mark after the parentheses as we’ve seen previously which would mean we were checking the value returned from the method.
Right, I’m going to leave it there for today. I hope this has helped. As ever, if you have questions, comments or corrections, please get in touch. Until next time.