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 1Default 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: 100Memberwise 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 4Custom 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 1Defining 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.0Failable 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: falseThe 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: 4Initializers 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 trueRules 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
nilfor invalid input. - Classes use designated and convenience initializers with two-phase setup.
