Initializers
The form Temperature stores a temperature in degrees Celsius. But your thermometer shows degrees Fahrenheit, and converting them in your head before every entry is a nuisance. It is handier to give the form to a clerk: you name the degrees Fahrenheit, and the clerk converts them and writes the result into the field.
In a struct, such a clerk is called an initializer:
struct Temperature {
var celsius: Double
init(fahrenheit: Double) {
celsius = (fahrenheit - 32) * 5 / 9
}
}
let boiling = Temperature(fahrenheit: 212)
print(boiling.celsius) // 100.0Let's walk through the program step by step:
- Inside
Temperature, aninit(fahrenheit:)is declared. There is nofuncand no name before the parentheses with the parameters, and no->after them. Temperature(fahrenheit: 212)calls thisinit: the parameterfahrenheitgets212.- The body converts the degrees and puts the result into the property
celsius. print(boiling.celsius)prints100.0: water boils at 212 degrees Fahrenheit.
Your own initializer
An initializer is the code that fills in the properties of a new instance. It is declared with the word init and called by the type's name: Temperature(fahrenheit: 212). Its parameters, argument labels and default values work as in a function. An initializer needs no mutating: filling in the properties of a new instance is its job.
An initializer has one rule: by its end, every stored property must have a value, its own or a default one. If some property is left empty, the compiler answers return from initializer without initializing all stored properties.
Here the clerk picture is inaccurate. A clerk may absent-mindedly hand back a form with a blank field, while Swift does not even compile such an init.
The willSet and didSet observers do not run inside init: the instance is still being created.
The memberwise initializer disappears
Swift gives a struct on its own the way of creating an instance with labels for all its properties, like Point(x: 1, y: 2). It is called the memberwise initializer. But as soon as the struct gets its own init, the memberwise one disappears:
let room = Temperature(celsius: 20) // ✗error: incorrect argument label in call (have 'celsius:', expected 'fahrenheit:')The compiler sees the only initializer, init(fahrenheit:), and suggests that the label should be fahrenheit:. If you need both ways, you write the memberwise one yourself, as one more init inside the struct: init(celsius: Double) { self.celsius = celsius }. Now Temperature has two initializers, and Swift picks the right one by the labels.
Common mistake
A parameter of init is named like a property, and self is forgotten:
struct Box {
var width: Int
init(width: Int) {
width = width // ✗
}
}error: cannot assign to value: 'width' is a 'let' constantThe program does not compile. Inside init, the name width means the parameter, and a parameter is a constant. The line width = width tries to put the parameter into itself. To reach the property, write self.width = width: the instance's property on the left, the parameter on the right.
Key points
initfills in the properties of a new instance; by its end every stored property must have a value.- Your own
initremoves the memberwise initializer; if you need it, you write it as one moreinit. - When a parameter and a property have the same name, the property is written with
self:self.width = width.