Optional Chaining In Swift


Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If optional contains a value, the property, method or subscripts call succeeds. if the optional is nil, the property, method or subscript call returns nil. Multiple queries can be chained together and the entire chain fails gracefully if any link in the chain is nil.
Optional chaining in Swift is similar to messaging nil in Objective-C but in a way that works for any type, and can be checked for success or failure. 

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 non-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.
Optional chaining can be called on a nil value, the result of optional chaining call is always an optional value, even if the property, method or subscript you are querying returns a non-optional value. 
Let's take an example where first, two classes called Person and Residence are defined,
  1. class Person {    
  2.     var residence: Residence?    
  3. }    
  5. class Residence {    
  6.     var numberOfRooms = 1    
  7. }    
If you try to access the numberOfRooms property of this person's residence by placing an exclamation mark after residence to force the unwrapping of its value you trigger a runtime error because there is no residence value to unwrap.
  1. let roomCount = john.residence!.numberOfRooms  
  2. // this triggers a runtime error  
The above code john.residence has a non.nil value and will set roomCount to an Int value containing the appropriate number of rooms.

Model Classes for Optional Chaining

You can use optional chaining with calls to properties, methods, and subscripts that are more than one level deep. This enables you to drill down into subproperties within complex models of interrelated types and to check whether it is possible to access properties, methods, and subscripts on those subproperties.
The below code snippet defines four model classes used in multilevel optional chaining. These classes expand upon the Person and Residence model from above by adding a Room and Address class, with associated properties, methods, and subscripts
  1. class Residence {  
  2.     var rooms = [Room]()  
  3.     var numberOfRooms: Int {  
  4.         return rooms.count  
  5.     }  
  6.     subscript(i: Int) -> Room {  
  7.         get {  
  8.             return rooms[i]  
  9.         }  
  10.         set {  
  11.             rooms[i] = newValue  
  12.         }  
  13.     }  
  14.     func printNumberOfRooms() {  
  15.         print("The number of rooms is \(numberOfRooms)")  
  16.     }  
  17.     var address: Address?  
  18. }  
Above Example explained Residence stores an array of room instances, its numberOfRooms property is implemented as a computed property, not a stored property. The computed numberOfRooms property returns count from the rooms array.
The final class in this model is called Address. This class has three optional properties of type String?. The first two properties, buildingName, and buildingNumber are alternative ways to identify a particular building as part of an address. The third property, street, is used to name the street for that address. 
  1. class Address {  
  2.     var buildingName: String?  
  3.     var buildingNumber: String?  
  4.     var street: String?  
  5.     func buildingIdentifier() -> String? {  
  6.         if let buildingNumber = buildingNumber, let street = street {  
  7.             return "\(buildingNumber) \(street)"  
  8.         } else if buildingName != nil {  
  9.             return buildingName  
  10.         } else {  
  11.             return nil  
  12.         }  
  13.     }  
  14. }  
Above Address class also provides a method called buildingIdentifier(), which has a return type of String?. This method checks the properties of the address and returns buildingName if it has a value, or buildingName concatenated with street if both have values, or nil otherwise.

Accessing Properties Through Optional Chaining

As demonstrated in Optional chaining, as an Alternative to Forced unwrapping, you can use optional chaining to access a property on an optional value and to check if that property access is successful. 
Use the above classes to create a new Person instance, and try to access its numberOfRooms property,
  1. let john = Person()  
  2. if let roomCount = john.residence?.numberOfRooms {  
  3.     print("John's residence has \(roomCount) room(s).")  
  4. else {  
  5.     print("Unable to retrieve the number of rooms.")  
  6. }  
  7. // Prints "Unable to retrieve the number of rooms."  
The above example of john.residence is nil, this optional chaining call fails in the same way as before. You can also attempt to set a property's value through optional chaining. 
  1. let someAddress = Address()  
  2. someAddress.buildingNumber = "234"  
  3. someAddress.street = "80 feet road"  
  4. john.residence?.address = personAddress  
The above example which attempts to set the address property of john.residence will fail, because john.residence is currently nil.
The assignment part of the optional chaining means none of the code on the right hand of the = operator is evaluated.

Calling Methods Through Optional Chaining

You can use optional chaining to call a method on an optional value and to check whether that method call is successful. You can do this even if that method does not define a return value. 
  1. func printNumberOfRooms() {  
  2.     print("The number of rooms is \(numberOfRooms)")  
  3. }  
The above method does not specify a return type, However, functions and methods with no return have an implicit return type of void as described in Functions without Return type values.
If you call the above method on an optional value with optional chaining, the method's return type will be with question mark? Void? because return values are always of an optional type when called through optional chaining.
  1. if john.residence?.printNumberOfRooms() != nil {  
  2.     print("It was possible to print the number of rooms.")  
  3. else {  
  4.     print("It was not possible to print the number of rooms.")  
  5. }  
  6. // Prints "It was not possible to print the number of rooms."  

Accessing Subscripts through optional chaining

When you access a subscript on an optional value through optional chaining, you place the question mark before the subscript's brackets, not after. The optional chaining question mark always follows immediately after the part of the expression that is optional.
In the below example try to access the room first name in an array of rooms object.
  1. if let firstRoomName = john.residence?[0].name {  
  2.     print("The first room name is \(firstRoomName).")  
  3. else {  
  4.     print("Unable to retrieve the first room name.")  
  5. }  
  6. // Prints "Unable to retrieve the first room name."  

Linking multiple levels of chaining

  • If the type you are trying to retrieve is not optional, it will become optional because of the optional chaining.
  • If the type you are trying to retrieve is already optional, it will not become more optional because of chaining.