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.
- Designated initializers are the primary initializers: each fully initializes all properties introduced by its class and calls up to a superclass initializer. Every class has at least one.
- Convenience initializers are secondary helpers
marked
convenience. They must ultimately call a designated initializer of the same class.
The delegation rules guarantee a single path up the chain:
- A designated initializer must call a designated initializer of its immediate superclass (delegate up).
- A convenience initializer must call another initializer in the same class (delegate across).
- 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:
- If the subclass defines no designated initializers, it inherits all of the superclass's designated initializers.
- 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.