Easy Tutorial
❮ Swift Fallthrough Statement Swift Closures ❯

Swift Initialization Process

Initialization is the process of preparing to use a class, structure, or enumeration type instance. This process involves setting initial values for each property in the instance and performing any necessary setup or initialization tasks.

Swift initializers use the init() method.

Unlike constructors in Objective-C, Swift initializers do not return a value. Their primary task is to ensure that new instances are correctly initialized before they are used for the first time.

Class instances can also define deinitializers to perform memory cleanup tasks before the class instance is deallocated.


Initial Assignment of Stored Properties

Classes and structures must set appropriate initial values for all stored properties when an instance is created.

When stored properties are assigned values in an initializer, their values are set directly and do not trigger any property observers.

Stored properties assignment process in initializers:


Initializers

Initializers are called when creating a new instance of a specific type. They are similar in form to instance methods without any parameters, named with the keyword init.

Syntax

init() {
    // Code to execute after instance creation
}

Example

The following structure defines an initializer init without parameters, which initializes the stored properties length and breadth to 6 and 12, respectively:

struct Rectangle {
    var length: Double
    var breadth: Double
    init() {
        length = 6
        breadth = 12
    }
}
var area = Rectangle()
print("Rectangle area is \(area.length * area.breadth)")

Output of the above program:

Rectangle area is 72.0

Default Property Values

You can set initial values for stored properties within the initializer; alternatively, you can set default values during property declaration.

Using default values can make your initializer more concise and clear, and allow automatic inference of property types from the default values.

The following example sets default values during property declaration:

struct Rectangle {
    // Set default values
    var length = 6
    var breadth = 12
}
var area = Rectangle()
print("Rectangle area is \(area.length * area.breadth)")

Output of the above program:

Rectangle area is 72

Initializer Parameters

You can provide initializer parameters when defining the init() method, as shown below:

struct Rectangle {
    var length: Double
    var breadth: Double
    var area: Double

    init(fromLength length: Double, fromBreadth breadth: Double) {
        self.length = length
        self.breadth = breadth
        area = length * breadth
    }

    init(fromLeng leng: Double, fromBread bread: Double) {
        self.length = leng
        self.breadth = bread
        area = leng * bread
    }
}

let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("Area is: \(ar.area)")

let are = Rectangle(fromLeng: 36, fromBread: 12)
print("Area is: \(are.area)")

Output of the above program:

Area is: 72.0
Area is: 432.0

Internal and External Parameter Names

Like function and method parameters, initializer parameters have an internal parameter name used within the initializer and an external parameter name used when calling the initializer.

However, unlike functions and methods, initializers do not have a distinguishable name before the parentheses. Therefore, the initializer to be called is determined primarily by the parameter names and types within the initializer.

If you do not provide an external name for an initializer parameter, Swift automatically generates an external name that matches the internal name.

struct Color {
    let red, green, blue: Double
    init(red: Double, green: Double, blue: Double) {
        self.red   = red
        self.green = green
struct Color {
    var red: Double
    var green: Double
    var blue: Double

    init(red: Double, green: Double, blue: Double) {
        self.red = red
        self.green = green
        self.blue = blue
    }

    init(white: Double) {
        red = white
        green = white
        blue = white
    }
}

// Creating a new Color instance by passing values for the three external parameters and calling the initializer
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)

print("red value is: \(magenta.red)")
print("green value is: \(magenta.green)")
print("blue value is: \(magenta.blue)")

// Creating a new Color instance by passing values for the three external parameters and calling the initializer
let halfGray = Color(white: 0.5)
print("red value is: \(halfGray.red)")
print("green value is: \(halfGray.green)")
print("blue value is: \(halfGray.blue)")

The output of the above program is:

red value is: 1.0
green value is: 0.0
blue value is: 1.0
red value is: 0.5
green value is: 0.5
blue value is: 0.5

Without External Name Parameters

If you do not want to provide an external name for an initializer parameter, you can use an underscore _ to explicitly describe its external name.

struct Rectangle {
    var length: Double

    init(frombreadth breadth: Double) {
        length = breadth * 10
    }

    init(frombre bre: Double) {
        length = bre * 30
    }
    // Without providing an external name
    init(_ area: Double) {
        length = area
    }
}

// Calling without providing an external name
let rectarea = Rectangle(180.0)
print("Area is: \(rectarea.length)")

// Calling without providing an external name
let rearea = Rectangle(370.0)
print("Area is: \(rearea.length)")

// Calling without providing an external name
let recarea = Rectangle(110.0)
print("Area is: \(recarea.length)")

The output of the above program is:

Area is: 180.0
Area is: 370.0
Area is: 110.0

Optional Property Types

If your custom type includes a stored property that logically allows for a nil value, you need to define it as an optional type.

When a stored property is declared as optional, it will be automatically initialized to nil.

struct Rectangle {
    var length: Double?

    init(frombreadth breadth: Double) {
        length = breadth * 10
    }

    init(frombre bre: Double) {
        length = bre * 30
    }

    init(_ area: Double) {
        length = area
    }
}

let rectarea = Rectangle(180.0)
print("Area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("Area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("Area is: \(recarea.length)")

The output of the above program is:

Area is: Optional(180.0)
Area is: Optional(370.0)
Area is: Optional(110.0)

Modifying Constant Properties During Initialization

You can modify the value of a constant property at any point during the initialization process as long as it is set to a definite value by the time initialization finishes.

For an instance of a class, its constant properties can only be modified during initialization in the class where they are defined; they cannot be modified in a subclass.

Even though the length property is now a constant, we can still set its value in the class's initializer:

struct Rectangle {
    let length: Double?

    init(frombreadth breadth: Double) {
        length = breadth * 10
    }

    init(frombre bre: Double) {
        length = bre * 30
    }

    init(_ area: Double) {
        length = area
    }
}

let rectarea = Rectangle(180.0)
print("Area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("Area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("Area is: \(recarea.length)")

The output of the above program is:

Area is: Optional(180.0)
Area is: Optional(370.0)
Area is: Optional(110.0)
length = breadth * 10
}

init(frombre bre: Double) {
    length = bre * 30
}

init(_ area: Double) {
    length = area
}
}

let rectarea = Rectangle(180.0)
print("Area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("Area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("Area is: \(recarea.length)")

The above program execution output is:

Area is: Optional(180.0)
Area is: Optional(370.0)
Area is: Optional(110.0)

Default Initializer

The default initializer will simply create an instance with all property values set to their default values:

In the following example, the ShoppingListItem class has default values for all its properties and is a base class without a superclass, so it will automatically get a default initializer that sets all properties to their default values.

class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()

print("Name is: \(item.name)")
print("Quantity is: \(item.quantity)")
print("Purchased: \(item.purchased)")

The above program execution output is:

Name is: nil
Quantity is: 1
Purchased: false

Memberwise Initializers for Structure Types

If a structure type provides default values for all its stored properties and does not provide a custom initializer, it automatically receives a memberwise initializer.

When you call the memberwise initializer, you can initialize the member properties by passing parameters with the same names as the properties.

The following example defines a structure Rectangle, which includes two properties length and breadth. Swift can infer their type as Double based on their initial values 100.0 and 200.0.

struct Rectangle {
    var length = 100.0, breadth = 200.0
}
let area = Rectangle(length: 24.0, breadth: 32.0)

print("Rectangle area: \(area.length)")
print("Rectangle area: \(area.breadth)")

Since both stored properties have default values, the structure Rectangle automatically receives a memberwise initializer init(width:height:). You can use it to create a new instance of Rectangle.

The above program execution output is:

Rectangle area: 24.0
Rectangle area: 32.0

Initializer Delegation for Value Types

Initializers can call other initializers to perform part of the instance initialization. This process is known as initializer delegation and can reduce code duplication among multiple initializers.

In the following example, the Rect structure delegates the initialization process to Size and Point:

struct Size {
    var width = 0.0, height = 0.0
}
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)
    }
}

// Both origin and size properties use the default values Point(x: 0.0, y: 0.0) and Size(width: 0.0, height: 0.0):
let basicRect = Rect()
print("Size struct initial values: \(basicRect.size.width, basicRect.size.height) ")
print("Rect struct initial values: \(basicRect.origin.x, basicRect.origin.y) ")

// Assign parameter values to the corresponding stored properties
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
    size: Size(width: 5.0, height: 5.0))

print("Size struct initial values: \(originRect.size.width, originRect.size.height) ")
print("Rect struct initial values: \(originRect.origin.x, originRect.origin.y) ")

// First calculate the origin coordinates from the values of center and size.
// Then call (or delegate to) the init(origin:size:) initializer to assign the new origin and size values to the corresponding properties
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
    size: Size(width: 3.0, height: 3.0))

print("Size struct initial values: \(centerRect.size.width, centerRect.size.height) ")
print("Rect struct initial values: \(centerRect.origin.x, centerRect.origin.y) ")

The program output is as follows:

Size struct initial values: (0.0, 0.0) 
Rect struct initial values: (0.0, 0.0) 
Size struct initial values: (5.0, 5.0) 
Rect struct initial values: (2.0, 2.0) 
Size struct initial values: (3.0, 3.0) 
Rect struct initial values: (2.5, 2.5)

Constructor Delegation Rules

Value Types Class Types
Do not support inheritance, so the process of constructor delegation is relatively simple, as they can only delegate to other constructors provided by themselves. You can use self.init to reference other constructors of the same value type in a custom constructor. They can inherit from other classes, which means that classes are responsible for ensuring that all inherited stored properties are also correctly initialized during construction.

Class Inheritance and Construction Process

Swift provides two types of class constructors to ensure that all stored properties in class instances are initialized, namely designated constructors and convenience constructors.

Designated Constructor Convenience Constructor
The main constructor in the class A secondary, auxiliary constructor in the class
Initializes all properties provided by the class and calls constructors of the superclass chain to initialize the superclass. Convenience constructors can be defined to call a designated constructor in the same class and provide default values for its parameters. You can also define convenience constructors to create instances for specific purposes or specific inputs.
Every class must have at least one designated constructor Provide convenience constructors only when necessary
init(parameters) { statements } convenience init(parameters) { statements }

Designated Constructor Example

class mainClass {
    var no1 : Int // Local stored variable
    init(no1 : Int) {
        self.no1 = no1 // Initialization
    }
}
class subClass : mainClass {
    var no2 : Int // New subclass stored variable
    init(no1 : Int, no2 : Int) {
        self.no2 = no2 // Initialization
        super.init(no1:no1) // Superclass initialization
    }
}

let res = mainClass(no1: 10)
let res2 = subClass(no1: 10, no2: 20)

print("res is: \(res.no1)")
print("res2 is: \(res2.no1)")
print("res2 is: \(res2.no2)")

The program output is as follows:

res is: 10
res2 is: 10
res2 is: 20

Convenience Constructor Example

class mainClass {
    var no1 : Int // Local stored variable
    init(no1 : Int) {
        self.no1 = no1 // Initialization
    }
class subClass: mainClass {
    var no2: Int
    init(no1: Int, no2: Int) {
        self.no2 = no2
        super.init(no1: no1)
    }
    // Convenience method requires only one parameter
    override convenience init(no1: Int) {
        self.init(no1: no1, no2: 0)
    }
}
let res = mainClass(no1: 20)
let res2 = subClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("res2 is: \(res2.no1)")
print("res2 is: \(res2.no2)")

The above program execution output is:

res is: 20
res2 is: 30
res2 is: 50

Inheritance and Overriding of Constructors

Subclasses in Swift do not inherit their parent class constructors by default.

Parent class constructors are only inherited under certain and safe conditions.

When you override a parent class designated constructor, you need to use the override modifier.

class SuperClass {
    var corners = 4
    var description: String {
        return "\(corners) sides"
    }
}
let rectangle = SuperClass()
print("Rectangle: \(rectangle.description)")

class SubClass: SuperClass {
    override init() {  // Overriding constructor
        super.init()
        corners = 5
    }
}

let subClass = SubClass()
print("Pentagon: \(subClass.description)")

The above program execution output is:

Rectangle: 4 sides
Pentagon: 5 sides

Designated and Convenience Constructors Example

The following example demonstrates how designated constructors, convenience constructors, and automatic constructor inheritance interact.

It defines a class hierarchy containing two classes, MainClass and SubClass, and shows how their constructors interact.

class MainClass {
    var name: String

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

    convenience init() {
        self.init(name: "[Anonymous]")
    }
}
let main = MainClass(name: "tutorialpro")
print("MainClass name is: \(main.name)")

let main2 = MainClass()
print("No corresponding name: \(main2.name)")

class SubClass: MainClass {
    var count: Int
    init(name: String, count: Int) {
        self.count = count
        super.init(name: name)
    }

    override convenience init(name: String) {
        self.init(name: name, count: 1)
    }
}

let sub = SubClass(name: "tutorialpro")
print("MainClass name is: \(sub.name)")

let sub2 = SubClass(name: "tutorialpro", count: 3)
print("count variable: \(sub2.count)")

The above program execution output is:

MainClass name is: tutorialpro
No corresponding name: [Anonymous]
MainClass name is: tutorialpro
count variable: 3

Failable Constructors for Classes

If a class, structure, or enumeration type object might fail during its construction, you can define a failable constructor for it.

Variables might fail to initialize due to:

To handle such potential failures during construction, you can add one or more failable constructors to the definition of a class, structure, or enumeration type. The syntax involves adding a question mark after the init keyword (init?).

Example

class MainClass {
    var name: String

    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}

let main = MainClass(name: "tutorialpro")
if let main = main {
    print("MainClass name is: \(main.name)")
}

let main2 = MainClass(name: "")
if main2 == nil {
    print("MainClass instance is nil")
}

The above program execution output is:

MainClass name is: tutorialpro
MainClass instance is nil
struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}

// Create an Animal object using the failable initializer and check if it was successful
// The type of someCreature is Animal? instead of Animal
let someCreature = Animal(species: "Giraffe")

// Print "Animal initialized as Giraffe"
if let giraffe = someCreature {
    print("Animal initialized as \(giraffe.species)")
}

Output of the above program:

Animal initialized as Giraffe

Failable Initializers for Enumerations

You can create a failable initializer with one or more parameters to get a specific enumeration member.

Example

In this example, an enumeration named TemperatureUnit is defined. It includes three possible members (Kelvin, Celsius, and Fahrenheit) and a failable initializer to find the enumeration member corresponding to a Character value:

enum TemperatureUnit {
    // Kelvin, Celsius, Fahrenheit
    case Kelvin, Celsius, Fahrenheit
    init?(symbol: Character) {
        switch symbol {
        case "K":
            self = .Kelvin
        case "C":
            self = .Celsius
        case "F":
            self = .Fahrenheit
        default:
            return nil
        }
    }
}

let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
    print("This is a defined temperature unit, so initialization succeeded.")
}

let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
    print("This is not a defined temperature unit, so initialization failed.")
}

Output of the above program:

This is a defined temperature unit, so initialization succeeded.
This is not a defined temperature unit, so initialization failed.

Failable Initializers for Classes

Failable initializers for value types (such as structs or enumerations) can trigger failure at any point.

However, failable initializers for classes can only trigger failure after all class properties have been initialized and all constructor delegation within the class has completed.

Example

In this example, a class named StudRecord is defined. Since the studname property is a constant, once the StudRecord class is successfully constructed, the studname property will definitely have a non-nil value.

class StudRecord {
    let studname: String!
    init?(studname: String) {
        self.studname = studname
        if studname.isEmpty { return nil }
    }
}
if let stname = StudRecord(studname: "Failable Initializer") {
    print("Module is \(stname.studname)")
}

Output of the above program:

Module is Failable Initializer

Overriding a Failable Initializer

Like other initializers, you can override a failable initializer in a subclass.

You can also override a failable initializer with a non-failable initializer in a subclass.

You can override a failable initializer with a non-failable initializer, but not vice versa.

A non-failable initializer can never delegate to a failable initializer.

Example

The following example demonstrates failable and non-failable initializers:

class Planet {
    var name: String

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

    convenience init() {
        self.init(name: "[No Name]")
    }
}

class NamedPlanet: Planet {
    override init(name: String) {
        super.init(name: name.isEmpty ? "[No Name]" : name)
    }
}
init(name: String) {
    self.name = name
}

convenience init() {
    self.init(name: "[No Planets]")
}
}
let plName = Planet(name: "Mercury")
print("The name of the planet is: \(plName.name)")

let noplName = Planet()
print("Planet with no name: \(noplName.name)")

class Planets: Planet {
    var count: Int

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

    override convenience init(name: String) {
        self.init(name: name, count: 1)
    }
}

The output of the program execution is:

The name of the planet is: Mercury
Planet with no name: [No Planets]

Failable Initializer init!

Typically, we define a failable initializer by adding a question mark after the init keyword (init?). However, you can also define a failable initializer by adding an exclamation mark after the init keyword (init!). Here is an example:

struct StudRecord {
    let stname: String

    init!(stname: String) {
        if stname.isEmpty { return nil }
        self.stname = stname
    }
}

let stmark = StudRecord(stname: "tutorialpro")
if let name = stmark {
    print("Student name is specified")
}

let blankname = StudRecord(stname: "")
if blankname == nil {
    print("Student name is empty")
}

The output of the program execution is:

Student name is specified
Student name is empty
❮ Swift Fallthrough Statement Swift Closures ❯