Initialization

Initialization is the process of preparing an instance for use — giving every stored property a valid starting value and doing any setup the type needs. Swift's safety guarantees lean heavily on initializers: an instance can never exist with an unset property. This chapter covers everything from a struct's free memberwise initializer to the careful two-phase choreography that makes class inheritance safe.

Setting initial values for stored properties

Every stored property must hold a value by the time initialization finishes. You can supply that value with a default in the declaration or inside an initializer. An initializer is a special method named init:

struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}

var f = Fahrenheit()
print("\(f.temperature)° F")   // → 32.0° F

When a property always starts the same way, a default value is cleaner and ties more closely into default and memberwise initializers: var temperature = 32.0.

Customizing initialization

Initializers can take parameters with argument labels, just like functions. Provide several to support different ways of creating an instance:

struct Celsius {
    var temperatureInCelsius: Double

    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}

let boiling = Celsius(fromFahrenheit: 212.0)
print(boiling.temperatureInCelsius)   // → 100.0

Parameters without external names

Write _ before the parameter name to call the initializer without an argument label:

struct Celsius2 {
    var value: Double
    init(_ celsius: Double) { value = celsius }
}
let body = Celsius2(37.0)   // no label needed

Optional and constant properties

Optional properties default to nil automatically, so they need no initial value. A constant (let) property can be assigned exactly once during initialization — by the class or struct that introduces it — and is then fixed.

class SurveyQuestion {
    let text: String          // constant, set once in init
    var response: String?       // optional, defaults to nil

    init(text: String) {
        self.text = text
    }
    func ask() { print(text) }
}

let q = SurveyQuestion(text: "Do you like cheese?")
q.ask()                          // → Do you like cheese?
q.response = "Yes, very much."

Default initializers and memberwise initializers

If a class or struct gives default values to all its stored properties and defines no initializer of its own, Swift provides a default initializer that creates an instance with those defaults. Structures additionally receive a free memberwise initializer — even when properties have no defaults — with one parameter per stored property:

struct Size {
    var width = 0.0
    var height = 0.0
}

let empty = Size()                       // default initializer
let box = Size(width: 3.0, height: 4.0)  // memberwise initializer

Note: Defining your own initializer in the same declaration as a struct suppresses the memberwise one. To keep both, write the custom initializer in an extension instead.

Initializer delegation for value types

Value types (structs and enums) can have one initializer call another with self.init, avoiding duplicated setup. (Value types have no inheritance, so they only ever delegate to themselves.)

struct Point { var x = 0.0, y = 0.0 }

struct Rect {
    var origin = Point()
    var size = Size()

    init() {}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

Class inheritance and initialization

Classes need more care because an inherited property must be initialized too. Swift has two kinds of class initializer.

The delegation rules guarantee a single path up the chain:

  1. A designated initializer must call a designated initializer of its immediate superclass (delegate up).
  2. A convenience initializer must call another initializer in the same class (delegate across).
  3. A convenience initializer must ultimately reach a designated initializer.

Two-phase initialization

Class initialization happens in two phases. In phase 1, each class sets its own stored properties and delegation walks up to the base class, so by the top of the chain all memory is initialized. Only then does phase 2 begin, walking back down so each class can customise further and access self, call methods, or read properties. This is why you must set your own properties before calling super.init().

class Food {
    var name: String
    init(name: String) { self.name = name }   // designated
    convenience init() { self.init(name: "[Unnamed]") }
}

class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity          // phase 1: own property first
        super.init(name: name)             // then delegate up
    }
    override convenience init(name: String) {
        self.init(name: name, quantity: 1)
    }
}

let egg = RecipeIngredient()             // name "[Unnamed]", quantity 1
let flour = RecipeIngredient(name: "Flour", quantity: 5)

Initializer inheritance and overriding

Subclasses do not inherit their superclass initializers by default — this prevents creating an instance that the subclass hasn't fully set up. When you provide a matching designated initializer, mark it override (as with RecipeIngredient.init(name:) above, which overrides the inherited convenience initializer). Two rules grant automatic inheritance:

  1. If the subclass defines no designated initializers, it inherits all of the superclass's designated initializers.
  2. If the subclass implements all of its superclass's designated initializers (by inheriting them per rule 1 or by overriding them), it also inherits all the superclass's convenience initializers.

Failable initializers

When initialization can legitimately fail — bad input, a missing resource — declare a failable initializer with init?. Return nil to signal failure; the result is an optional instance.

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}

let cat = Animal(species: "Cat")      // Animal? — has a value
let nameless = Animal(species: "")    // nil

Enumerations with raw values get a failable init?(rawValue:) for free. You can also use init! to produce an implicitly unwrapped optional instance.

Required initializers

Prefix a class initializer with required to force every subclass to implement it. Subclass implementations also write required (not override) so the requirement keeps propagating down the hierarchy.

class Component {
    required init() {}
}
class Button: Component {
    required init() {
        // custom setup
        super.init()
    }
}

Setting a default value with a closure

When a default value needs setup logic, assign it from an immediately invoked closure. The trailing () runs the closure once and stores its result. Because the instance isn't fully initialized yet, the closure cannot access other properties or self.

struct Chessboard {
    let squares: [Bool] = {
        var board: [Bool] = []
        var isBlack = false
        for _ in 1...8 {
            for _ in 1...8 {
                board.append(isBlack)
                isBlack.toggle()
            }
            isBlack.toggle()
        }
        return board
    }()   // note the () — the closure runs immediately
}

Wrapping up

Initialization is Swift's promise that no instance is ever half-built. Structs hand you memberwise initializers for free; classes use designated and convenience initializers with two-phase initialization to make inheritance safe; and failable, required, and closure-based defaults cover the trickier cases. Next we look at the other end of an object's life — deinitialization.