===== Full Notes =====
Optional Chaining
Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil.
- If the optional contains a value, the property, method, or subscript 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 that can be checked for success or failure.
Optional Chaining as an Alternative to Forced Unwrapping
Sample of Forced Unwarpping class Person { var residence: Residence? }
class Residence { var numberOfRooms = 1 } let john = Persion() let roomCount = john.residence!.numberOfRooms // this triggers a runtime error, because residence maybe nil
Optional chaining provides an alternative way to access the value of numberOfRooms.
if let roomCount = john.residence?.numberOfRooms { println("John's residence has \(roomCount) room(s).") } else { println("Unable to retrieve the number of rooms.") } // prints "Unable to retrieve the number of rooms. // Even numberOfRooms is Int, optional chaining will always return it as Int?
Defining Model Classes for Optional Chaining
class Residence { var rooms = Room[]() var numberOfRooms: Int { return rooms.count } subscript(i: Int) -> Room { return rooms[i] } func printNumberOfRooms() { println("The number of rooms is \(numberOfRooms)") } var address: Address? } class Room { let name: String init(name: String) { self.name = name } } class Address { var buildingName: String? var buildingNumber: String? var street: String? func buildingIdentifier() -> String? { if buildingName { return buildingName } else if buildingNumber { return buildingNumber } else { return nil } } }
Calling Properties Through Optional Chaining
It is the same as above
let john = Person() if let roomCount = john.residence?.numberOfRooms { println("John's residence has \(roomCount) room(s).") } else { println("Unable to retrieve the number of rooms.") }
Calling Methods Through Optional Chaining
func printNumberOfRooms() { println("The number of rooms is \(numberOfRooms)") } “if john.residence?.printNumberOfRooms() { println("It was possible to print the number of rooms.") } else { println("It was not possible to print the number of rooms.") } // prints "It was not possible to print the number of rooms.
Calling Subscripts Through Optional Chaining
if let firstRoomName = john.residence?[0].name { println("The first room name is \(firstRoomName).") } else { println("Unable to retrieve the first room name.") } // 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 the chaining.
“if let johnsStreet = john.residence?.address?.street { println("John's street name is \(johnsStreet).") } else { println("Unable to retrieve the address.") } // prints "Unable to retrieve the address.”
Chaining on Methods With Optional Return Values
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() { println("John's building identifier is \(buildingIdentifier).") } // prints "John's building identifier is The Larches. “if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString { println("John's uppercase building identifier is \(upper).") } // prints "John's uppercase building identifier is THE LARCHES.”