Swift optional chain


Release date:2023-12-02 Update date:2023-12-08 Editor:admin View counts:149

Label:

Swift optional chain

An optional chain is a process that can request and invoke properties, methods, and subscripts, and the target for a request or call may be nil .

The optional chain returns two values:

  • If the target has a value, the call succeeds and returns the value

  • If the target is nil the call will return nil

Multiple requests or calls can be linked into a chain if any node is nil will cause the whole chain to fail.

Optional chain can replace forced parsing

An optional chain can be defined by placing a question mark (?) after the optional value of a property, method, or subscript.

Optional chain’?’

Exclamation point (!) forces the expansion of methods, attributes, and optional chains of subscript scripts

? Call methods, properties, and subscript scripts after placing optional values

! Methods, properties, and subscript scripts are called after placing the optional value to force the expansion of the value

Output more friendly error messages when optional as nil

Force expand execution error when nil is optional

Use the exclamation point (!) Optional chain instance

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

let john = Person()

//Will cause runtime errors
let roomCount = john.residence!.numberOfRooms

The output of the above program execution is as follows:

fatal error: unexpectedly found nil while unwrapping an Optional value

Want to use the exclamation point (!) to force parsing to get this person residence attribute numberOfRooms property value, a run-time error will be raised because there is no residence value.

Use the question mark (?) Optional chain instance

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

let john = Person()

// Link optional residence? Property, if residence exists, retrieve the value of numberOfRooms
if let roomCount = john.residence?.numberOfRooms {
    print("John's room number is \(roomCount)。")
} else {
    print("Unable to view room number")
}

The output of the above program execution is as follows:

Unable to view room number

Because of this attempt to get numberOfRooms may fail, and the optionalchain will return Int? type value, or “optional Int”. When residence when empty (example above), select Int will be empty, so it will be inaccessible numberOfRooms the situation.

It is important to note that even if numberOfRooms yes or no option Int(Int?) . This is also true. As long as the request through the optionalchain means that in the end numberOfRooms always return a Int? instead of Int .

Define a model class for an optional chain

You can use optional chains to call properties, methods, and subscript scripts at multiple levels. This allows you to take advantage of the complexmodel between them to obtain lower-level properties and check whether such underlying properties can be successfully obtained.

Example

Four model classes are defined, including multi-layer optional chains:

class Person {
    var residence: Residence?
}

// Defined a variable rooms, which is initialized as an empty array of type Room []
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("Room number is \(numberOfRooms)")
    }
    var address: Address?
}

// Room Define a name attribute and an initializer for setting the room name
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// The final class in the model is called Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

Call a method through an optional chain

You can use the optional chain to call the method of the optional value and check whether the method call is successful. Even if this method does not return a value, you can still use an optional chain to achieve this.

class Person {
    var residence: Residence?
}

// Defined a variable rooms, which is initialized as an empty array of type Room []
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("Room number is \(numberOfRooms)")
    }
    var address: Address?
}

// Room defines a name attribute and an initializer that sets the room name
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// The final class in the model is called Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()


if ((john.residence?.printNumberOfRooms()) != nil) {
    print("Output room number")
} else {
    print("Unable to output room number")
}

The output of the above program execution is as follows:

Unable to output room number

Use the if statement to check whether the call can be successfully called printNumberOfRooms method: if the method is successfully called through the optional chain printNumberOfRooms the implicit return value of willbe Void if it is not successful, it returns nil .

Invoke the subscript script using the optional chain

You can use the optional chain to try to get a value from the subscript script and check whether the call to the subscript script is successful. However, you cannot set the subscript script through the optional chain.

Example 1

class Person {
    var residence: Residence?
}

// Defined a variable rooms, which is initialized as an empty array of type Room []
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("Room number is\(numberOfRooms)")
    }
    var address: Address?
}

// Room defines a name attribute and an initializer that sets the room name
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// The final class in the model is called Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()
if let firstRoomName = john.residence?[0].name {
    print("First room name \(firstRoomName).")
} else {
    print("Unable to retrieve room")
}

The output of the above program execution is as follows:

Unable to retrieve room

The question mark of the optional chain in the subscript call comes directlyafter the john.residence and before the subscript parentheses, because john.residence is the optional value that the optional chain is trying to get.

Example 2

Instance to create a Residence give an example to john.residence and in his rooms . There are one or more objects in the array Room instance, then you can use the optional chain to pass the Residence thesubscript script is obtained in the rooms the instance in the array:

class Person {
    var residence: Residence?
}

// Defined a variable rooms, which is initialized as an empty array of type Room []
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("Room number is \(numberOfRooms)")
    }
    var address: Address?
}

// Room defines a name attribute and an initializer that sets the room name
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// The final class in the model is called Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "living room"))
johnsHouse.rooms.append(Room(name: "kitchen"))
john.residence = johnsHouse

let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence!.address = johnsAddress

if let johnsStreet = john.residence?.address?.street {
    print("John's street is \(johnsStreet)。")
} else {
    print("Unable to retrieve address. ")
}

The output of the above program execution is as follows:

John's street is Laurel Street。

Access the subscript through an optional link call

Through the optional link call, we can use the subscript to read or write the optional value and determine whether the subscript call is successful ornot.

Example

class Person {
    var residence: Residence?
}

// Defined a variable rooms, which is initialized as an empty array of type Room []
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("Room number is \(numberOfRooms)")
    }
    var address: Address?
}

// Room defines a name attribute and an initializer that sets the room name
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// The final class in the model is called Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()

let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "living room"))
johnsHouse.rooms.append(Room(name: "kitchen"))
john.residence = johnsHouse

if let firstRoomName = john.residence?[0].name {
    print("The first room is named\(firstRoomName)")
} else {
    print("Unable to retrieve room")
}

The output of the above program execution is as follows:

The first room is called the living room

Access the subscript of the optional type

If the subscript returns a nullable type value, such as in Swift Dictionary of key subscript. You can put a question mark after the closing parentheses of the subscript to link the nullable return value of the subscript:

var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]

In the above example, a testScores array, which contains two key-value pairs, set the String of type key maps to an array of integers.

This example uses an optional link call to set the first element in the “Dave” array to 91, the first element in the “Bev” array + 1, and then tries to set the first element in the “Brian” array to 72.

The first two calls are successful because these two key exist. But the key “Brian” does not exist in the dictionary, so the third call fails.

Connect multi-layer links

You can connect multiple layers of optional chains together, and you can digup property methods and subscript scripts at a lower level in the model. However, the multi-layer optional chain cannot add more layers than the optional values that have been returned.

If you try to get it through the optional chain Int value, no matter how many layers of links are used, the Int? . Similarly, if you try to get through the optional chain Int? value, no matter how many layers oflinks are used, the Int? .

Example 1

The following example attempts to get the john of residence in theattribute address of street property. Two layers of optional chains are used to contact. residence and address property, both of which are optional types

class Person {
    var residence: Residence?
}

// Defined a variable rooms, which is initialized as an empty array of type Room []
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("Room number is \(numberOfRooms)")
    }
    var address: Address?
}

// Room defines a name attribute and an initializer that sets the room name
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// The final class in the model is called Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()

if let johnsStreet = john.residence?.address?.street {
    print("John's address is \(johnsStreet).")
} else {
    print("Unable to retrieve address")
}

The output of the above program execution is as follows:

Unable to retrieve address

Example 2

If you work for Address set an instance to act as john.residence.address and the value of address of street property sets an actual value, which you can get through a multi-layer optional chain.

class Person {
   var residence: Residence?
}

class Residence {

    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        get{
            return rooms[i]
        }
        set {
            rooms[i] = newValue
        }
    }
    func printNumberOfRooms() {
        print("Room number 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 != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}
let john = Person()
john.residence?[0] = Room(name: "bathroom")

let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "living room"))
johnsHouse.rooms.append(Room(name: "kitchen"))
john.residence = johnsHouse

if let firstRoomName = john.residence?[0].name {
    print("The first room is \(firstRoomName)")
} else {
    print("Unable to retrieve room")
}

The output result of the above example is:

The first room is the living room

Link functions that return optional values

We can also call methods that return nullable values through optional links,and we can continue to link optional values.

Example

class Person {
    var residence: Residence?
}

// Defined a variable rooms, which is initialized as an empty array of type Room []
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        return rooms[i]
    }
    func printNumberOfRooms() {
        print("Room number is \(numberOfRooms)")
    }
    var address: Address?
}

// Room defines a name attribute and an initializer that sets the room name
class Room {
    let name: String
    init(name: String) { self.name = name }
}

// The final class in the model is called Address
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if (buildingName != nil) {
            return buildingName
        } else if (buildingNumber != nil) {
            return buildingNumber
        } else {
            return nil
        }
    }
}

let john = Person()

if john.residence?.printNumberOfRooms() != nil {
    print("Room number specified)")
}  else {
    print("No room specified room number")
}

The output of the above program execution is as follows:

No room specified room number

Powered by TorCMS (https://github.com/bukun/TorCMS).