class Human { var name: String var age: Int init(name: String, age: Int) { self.name = name self.age = age } }
class Student: Human { var school: String init(name: String, age: Int, school: String) { self.school = school super.init(name: name, age: age) } }
class UnivStudent: Student { var major: String init(name: String, age: Int, school: String, major: String) { self.major = major super.init(name: name, age: age, school: school) } convenience init(age: Int) { self.init(name: "unknown", age: age, school: "Stanford", major: "computer") } }

Inheritance chain
Let's look at the safety checks the compiler needs
before a class's initialization process.

Safety checks
This is a process the compiler performs.
It runs 4 checks.
① All properties of the current class must be initialized before the parent class's init runs.
init(name: String, age: Int, school: String, major: String) { self.major = major super.init(name: name, age: age, school: school) }
In the UnivStudent class, the current class's property is major.
Through self.major = major, it checks that all of UnivStudent's properties are initialized.
The parent class can't initialize properties that only the child class has, so this check is performed.
② Properties inherited from the parent must be assigned after the parent's initialization finishes.
init(name: String, age: Int, school: String, major: String) { self.major = major self.age = 14 // ERROR! super.init(name: name, age: age, school: school) }
self.age is a property inherited from the Human class.
But it tried to initialize this value before calling the parent's initializer, so an error occurs.
③ Same as ②, in a convenience initializer you also have to initialize properties after calling the designated initializer.
convenience init(age: Int) { self.age = 14 // ERROR self.init(name: "unknown", age: age, school: "Stanford", major: "computer") }
④ Before phase 1 of initialization is done, you can't call instance properties/methods' properties,
and you can't reference the self property.
class UnivStudent: Student { var major: String var me: UnivStudent = self // ERROR! var me: UnivStudent = { self }() // ERROR! var me: UnivStudent { self } // Okay init(name: String, age: Int, school: String, major: String) { self.major = major me = self // ERROR! super.init(name: name, age: age, school: school) } convenience init(age: Int) { self.init(name: "unknown", age: age, school: "Stanford", major: "computer") } }
A stored property can't reference self while phase 1 initialization isn't complete.
But a computed property doesn't reference self, it just returns a value, so it's allowed.