Search code examples
swiftinitialization

Implicitly Unwrapped Optional when a constant that cannot be defined during initialisation, But Error Comes


By Drewag in his answer to the question

Every member constant must have a value by the time initialization is complete. Sometimes, a constant cannot be initialized with its correct value during initialization, but it can still be guaranteed to have a value before being accessed.

Using an Optional variable gets around this issue because an Optional is automatically initialized with nil and the value it will eventually contain will still be immutable. However, it can be a pain to be constantly unwrapping a variable that you know for sure is not nil. Implicitly Unwrapped Optionals achieve the same benefits as an Optional with the added benefit that one does not have to explicitly unwrap it everywhere.

The following code defines two classes, Country and City, each of which stores an instance of the other class as a property. Every country must have a capital city and every city must always belong to a country.

class Country {
    let name: String
    var capitalCity: City! //why I can't use let!
    init(name: String, capitalName: String){
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
    
    deinit {
        print("\(name) has been de-initialized the city \(capitalCity.name) is gone with with the country")
    }
    
}

class City{
    let name: String
    unowned let country: Country
    init(name: String, country: Country){
        self.name = name
        self.country = country
    }
    
    deinit {
        print("The city \(name) has been de-initialized ")
    }
}

    var country = Country(name: "Canada", capitalName: "Ottawa")
}

However, if I changed the line var capitalCity: City! into let capitalCity: City!, the compiler given out the following error warning.

enter image description here

Question: Isn't that we can use Implicitly Unwrapped Optional when a constant that cannot be defined during initialization? What's the error here?


Solution

  • The important part is here:

    City(name: capitalName, country: self)
    

    You are definitely using self in the expression which is executed before the assignment to the property capitalCity.

    If you want to use self in any of the expressions, it needs to be in the second phase of two phase initialization, which means all properties needs to be initialized before the usage of self.

    With using var, Swift assigns a default initial value nil for the property capitalCity. So the property can be considered as "already initialized", so, you can use self after you have initialized another property name.

    (You know giving nil to a let-constant of ImplicitlyUnwrappedOptional is ridiculous.)

    By the way private(set) var is often used in similar cases:

        private(set) var capitalCity: City!