Optional Chaining in swift is the process in which we can query or call the methods, properties or subscripts of an optional that might be nil
. Generally, in swift optional chaining will return a value in two ways.
- If optional contains a value, then calling the property, method or subscript of an optional will return a
value
. - In case if optional is
nil
, then calling the property, method or subscription of an optional will returnnil
.
In swift, multiple queries can be chained together due to that if any link in the chain fails or return a nil
value, the entire chain will fail and return a nil
value.
Optional Chaining as an Alternative to Forced Unwrapping
You specify optional chaining by placing a question mark (?
) after the optional value on which you wish to call a property, method or subscript if the optional is not-nil
. This is very similar to placing an exclamation mark (!
) after an optional value to force the unwrapping of its value. The main difference is that optional chaining fails gracefully when the optional is nil
, whereas forced unwrapping triggers a runtime error when the optional is nil
.
The result of optional chaining will be of the same type as expected return value with wrapped in an optional. A property that normally returns an String
will return a String?
when accessed through optional chaining.
Example:
The below code snippets demonstrate how optional chaining differs from forced unwrapping and enables you to check for success.
Defining models and structure:
First, two classes called Company
and Address
are defined:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Company { var address: Address? } class Address { var street: String var postcode: String var city: String var country: String init(street: String, postcode: String, city: String, country: String) { self.street = street self.postcode = postcode self.city = city self.country = country } } |
Address
instances have the optional property like street, postcode, city, country. Company
instances have an optional address
property of type Address?
.
If you create a new Company
instance, its address
property is default initialized to nil
, by virtue of being optional. In the code below, webkul
has a address
property value of nil
:
1 |
let webkul = Company() |
If you try to access the country
property or any other property of this company’s address
, by placing an exclamation mark after address
to force the unwrapping of its value, you trigger a runtime error, because there is no address
value to unwrap:
1 2 |
let country = webkul.address!.country // this triggers a runtime error |
The code above succeeds when webkul.address
has a non-nil
value and will set country
to an String
value containing the appropriate country. However, this code always triggers a runtime error when address
is nil
, as illustrated above.
Uses:
Optional chaining provides an alternative way to access the value of country
. To use optional chaining, use a question mark in place of the exclamation mark:
1 2 3 4 5 6 |
if let country = webkul.address?.country { print("Webkul's head office is in \(country).") } else { print("Unable to retrieve the country.") } // Prints "Unable to retrieve the country." |
This tells Swift to “chain” on the optional address
property and to retrieve the value of country
if address
exists.
Because of the attempt to access the potential to fail, the optional chaining returns a value of type String?
, or “optional String
”. When address
is nil
, as in the example above, this optional String
will also be nil
, to reflect the fact that it was not possible to access country
. The optional String
is accessed through optional binding to unwrap the string and assign the non-optional value to the country
variable.
Note that this is true even though country
is a non-optional String
. The fact that it is queried through an optional chain means that the call to country
will always return an String?
instead of an String
.
You can assign a address
instance to webkul.address
, so that it no longer has a nil
value:
1 |
webkul.address = Address(street: "Sector 63", postcode: "201301", city: "Noida", country: "Indeia") |
webkul.address
now contains an actual Address
instance, rather than nil
. If you try to access country
with the same optional chaining as before, it will now return an String?
that contains the country
value as India
:
1 2 3 4 5 6 |
if let country = webkul.address?.country { print("Webkul's head office is in \(country).") } else { print("Unable to retrieve the country.") } // Prints "Webkul's head office is in India." |