Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'self' used before all stored properties are initialized

I'm working through a learn-swift playground and upgrading it to Swift 2.0 as I learn the language. The following code (which likely worked with prior versions of Swift) now generates two errors: "'self' used before all stored properties are initialized" and "Constant 'self.capitalCity' used before initialized"

class Country
{
    let name: String
    let capitalCity: City!

    init(name: String, capitalName: String)
    {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City
{
    let name: String
    unowned let country: Country

    init(name: String, country: Country)
    {
        self.name = name
        self.country = country
    }
}

reading an answer to a similar question I see that I can change let capitalCity: City! to var capitalCity: City! and the syntax error is resolved.

I realize that in this contrived example a country's capital city can change, so that would be fine, but what if there were a case where the value really was a constant...

Is there any way to resolve the syntax error while keeping capitalCity a constant?

like image 687
Ultrasaurus Avatar asked Dec 26 '15 19:12

Ultrasaurus


3 Answers

In this case I would suggest you to make the property a variable but hiding it (make it seem like a constant) through a computed property:

class Country {     let name: String      private var _capitalCity: City!     var capitalCity: City {         return _capitalCity     }      init(name: String, capitalName: String) {         self.name = name         self._capitalCity = City(name: capitalName, country: self)     } } 
like image 111
Qbyte Avatar answered Sep 19 '22 21:09

Qbyte


Is there any way to resolve the syntax error while keeping capitalCity a constant?

Not the way you have things set up. The source of the problem is actually that in order to set capitalCity, you have to create a City whose country is self. That is the use of self to which the compiler is objecting:

self.capitalCity = City(name: capitalName, country: self)                                                     ^^^^ 

Since you have configured City's country as a constant, you must supply this value when you initialize your City. Thus you have no way out; you must make capitalCity an Optional var so that it has some other initial value that is legal, namely nil. Your proposed solution actually works like this:

class Country {     let name: String     var capitalCity: City! = nil // implicit or explicit      init(name: String, capitalName: String)     {         self.name = name         // end of initialization!         // name is set (to name), and capitalCity is set (to nil)...         // ... and so the compiler is satisfied;         // now, we _change_ capitalCity from nil to an actual City,         // and in doing that, we _are_ allowed to mention `self`         self.capitalCity = City(name: capitalName, country: self)     } } 
like image 41
matt Avatar answered Sep 23 '22 21:09

matt


Just do:

private(set) var capitalCity: City!

which gives you the read-only public interface you want.

I understand that you find var capitalCity: City! contrived. I'd say that the selected answer is truly contrived; it adds lines of code that have no purpose other than resolving a language-related issue. Lines like that are meaningless and meaning is what matters most in code.

like image 38
meaning-matters Avatar answered Sep 19 '22 21:09

meaning-matters