What a constructor does
A constructor sets an object up at the moment it is created. In Swift it is called an initializer, and it is declared with the keyword init. Without one, you would create an object and then fill in its fields one assignment at a time:
var dog = Dog()
dog.name = "Rex"
dog.age = 4
That is noisy, and nothing stops you from forgetting a line. An initializer puts the setup into the call itself: Dog(name: "Rex", age: 4) returns a finished object.
Defining an initializer
class Dog {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
Two features set an initializer apart from a method. It is always named init, never after the class, and it has no func keyword and no return type. Also notice that the properties name and age have no default values. That is allowed because the initializer assigns every one of them. Swift insists that all stored properties have a value before the initializer finishes.
The self keyword
The parameter name and the property name share an identifier. Inside init, a bare name means the parameter, because the nearer declaration wins. self is a reference to the object being built, so self.name is the property. Writing name = name would assign the parameter to itself, and in fact does not compile: the compiler reports cannot assign to value: 'name' is a 'let' constant, since parameters are constants.
Calling the initializer
Swift uses the parameter names as argument labels in the call:
let rex = Dog(name: "Rex", age: 4)
let luna = Dog(name: "Luna", age: 7)
The labels are required, and so is their order. Each of these fails to compile: Dog("Rex", 4) gives missing argument labels 'name:age:' in call, and Dog(age: 4, name: "Rex") gives argument 'name' must precede argument 'age'. If you don't want labels, put an underscore before the parameter name, as in init(_ name: String), and call Dog("Rex").
The default initializer
If every stored property has a default value and you write no initializer, Swift gives the class init(), which is why Dog() worked in earlier lessons. The moment the properties lack defaults you must write an initializer. And once you write one, the empty Dog() is no longer available unless you add it: Dog() against the initializer above fails with missing arguments for parameters 'name', 'age' in call.
A class can have several initializers with different parameters, as long as each one sets up all the properties. A convenience init is a shortcut that calls another initializer of the same class with self.init(...), for example to supply a default age of 0.
The exercise
The starter Dog declares name and age but has no initializer, and mainFunction prints rex and luna without creating them. Add the initializer, then create both dogs:
class Dog {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
class Solution {
func mainFunction() {
let rex = Dog(name: "Rex", age: 4)
let luna = Dog(name: "Luna", age: 7)
print("\(rex.name) is \(rex.age)")
print("\(luna.name) is \(luna.age)")
}
}
The output is:
Rex is 4
Luna is 7
Common mistakes
- Dropping
self.when parameter and property share a name, which fails as shown above. - Leaving a property unset. If
initassigns onlyname, the compiler rejects it, becauseagehas no value. - Adding
funcor a return type. An initializer has neither. - Omitting the labels at the call site.
Key takeaways
- An initializer is written
init(...), with nofuncand no return type. - It must give every stored property a value.
- Use
self.nameto mean the property when a parameter has the same name. - Parameter names become argument labels at the call, unless you write
_. - Defining your own initializer removes the automatic
Dog().
