Swift Initializers

An initializer prepares a new instance for use. Swift requires every stored property to hold a value before the instance becomes available. The initializer guarantees this rule, which removes a whole family of bugs caused by half-built objects.

Think of checking into a hotel. The receptionist collects your name, assigns a room, and hands over a key. Nobody enters a room before these steps finish. An initializer plays the receptionist for your objects.

Diagram: Building an Instance

 Player(name: "Ava")
        |
        v
 +----------------------+
 | init(name:)          |
 |  name  = "Ava"       |
 |  level = 1           |
 +----------------------+
        |
        v
 Ready instance: name "Ava", level 1

Default Values

Give a property a default value and Swift builds a simple initializer automatically.

class Notebook {
    var pages = 100
    var color = "Blue"
}

let book = Notebook()
print(book.pages)   // Output: 100

Memberwise Initializer

Every struct receives a free initializer that accepts all stored properties by name.

struct Point {
    var x = 0
    var y = 0
}

let origin = Point()
let spot = Point(x: 3, y: 4)
print(spot.x, spot.y)   // Output: 3 4

Custom Initializers

Write an init method when you need your own setup rules. Use self to separate parameter names from property names.

struct Player {
    let name: String
    var level: Int

    init(name: String) {
        self.name = name
        self.level = 1
    }
}

let hero = Player(name: "Ava")
print("\(hero.name) starts at level \(hero.level)")
// Output: Ava starts at level 1

Defining a custom initializer inside a struct removes the free memberwise initializer. Place the custom initializer in an extension when you want to keep both.

Multiple Initializers

A type can offer several initializers with different parameters.

struct Temperature {
    var celsius: Double

    init(celsius: Double) {
        self.celsius = celsius
    }

    init(fahrenheit: Double) {
        celsius = (fahrenheit - 32) * 5 / 9
    }
}

let boiling = Temperature(fahrenheit: 212)
print(boiling.celsius)   // Output: 100.0

Failable Initializers

Some input is simply invalid. A failable initializer, written as init?, returns nil when the setup cannot succeed.

struct Email {
    let address: String

    init?(address: String) {
        guard address.contains("@") else { return nil }
        self.address = address
    }
}

print(Email(address: "ava@example.com") != nil)   // Output: true
print(Email(address: "not-an-email") != nil)      // Output: false

The result is an optional. Callers unwrap it before use.

Designated and Convenience Initializers

Classes use two initializer roles. A designated initializer sets every property of its own class and calls its parent when one exists. A convenience initializer offers a shortcut and must call another initializer of the same class.

class Vehicle {
    var name: String
    var wheels: Int

    init(name: String, wheels: Int) {
        self.name = name
        self.wheels = wheels
    }

    convenience init(name: String) {
        self.init(name: name, wheels: 4)
    }
}

let car = Vehicle(name: "Sedan")
print(car.wheels)   // Output: 4

Initializers in Subclasses

A subclass initializer sets its own properties and hands control to the parent initializer afterward. Swift calls this rule two-phase initialization.

class Bike: Vehicle {
    var hasBell: Bool

    init(name: String, hasBell: Bool) {
        self.hasBell = hasBell
        super.init(name: name, wheels: 2)
    }
}

let cycle = Bike(name: "Roadster", hasBell: true)
print(cycle.wheels, cycle.hasBell)   // Output: 2 true

Rules for Class Initializers

Swift enforces three rules that keep classes safe. A designated initializer must call a designated initializer of its direct parent. A convenience initializer must call another initializer of the same class. Every convenience initializer must end by reaching a designated initializer.

A subclass inherits parent initializers only under limited conditions. Defining any designated initializer in the subclass stops automatic inheritance, and the subclass offers only its own initializers.

Required Initializers

Mark an initializer with required to force every subclass to provide it. Frameworks use this tool to guarantee that a particular way of building an object always exists.

Common Mistakes

Using a property before every stored property has a value triggers an error. Set all properties before calling methods on self. Subclass initializers must set their own properties before calling super.init.

Forgetting to unwrap the result of a failable initializer leads to optional types in later code. Use if let or guard let right after the call.

Real-World Use

Profile screens create user objects from server data and reject records with missing fields through failable initializers. Games create characters with a convenience initializer that fills in beginner stats. Settings structs rely on default values, and one short call builds a full configuration.

Key Takeaways

  • Initializers make sure every property holds a value before use.
  • Structs receive a memberwise initializer for free.
  • Failable initializers return nil for invalid input.
  • Classes use designated and convenience initializers with two-phase setup.