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?
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) } }
Is there any way to resolve the syntax error while keeping
capitalCitya 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) } }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With