Start learning free

LESSON 207 OF 415 · FUNCTIONS & CLASSES

Constructors in Swift: Writing an init

Lesson Read the full explanation

TL ; DR:

  • A constructor is a special method that runs automatically when you create an object. It's used to set up the object's initial state.


  • In Swift, a constructor is called an initializer and is defined using init:

    class Dog {
        var name: String
        var age: Int
    
        init(name: String, age: Int) {
            self.name = name
            self.age = age
        }
    }
    

  • Then create objects with arguments: Dog(name: "Rex", age: 4) — the initializer fills in the fields for you.



Full lesson:

In the previous lesson you set fields after creating the object:

var dog = Dog()
dog.name = "Rex"
dog.age = 4

That works, but it's noisy and easy to forget. Initializers let you initialize the fields the moment Dog(...) is called.


Defining an initializer

A Swift initializer has these features:

  • It is always named init (not the class name).

  • It has no return type.

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

The self keyword

The parameter name and the field name have the same identifier. Without self, both sides of name = name would refer to the parameter (the local one wins). self is a built-in reference to "the current object", so self.name is the field, and bare name is the parameter.


Calling the initializer

let rex  = Dog(name: "Rex", age: 4)
let luna = Dog(name: "Luna", age: 7)

By the time these lines finish, rex.name is already "Rex", rex.age is 4, and the same for Luna.


The default initializer

If every stored property has a default value (as in the previous lesson), Swift gives the class a default initializer, Dog(). If a property has no default value, you must write an init that sets it. The moment you define your own initializer, Dog() is no longer available: you must call Dog with the arguments you specified.


Your task

The Dog class already has the name and age fields. Add an initializer that takes name: String and age: Int and assigns them using self. Then in the main code, create two dogs:

let rex  = Dog(name: "Rex", age: 4)
let luna = Dog(name: "Luna", age: 7)

Expected output:

Rex is 4
Luna is 7
STEP 1 OF 2
    Ctrl + Enter to check

    SWIFT · FUNCTIONS & CLASSES

    Constructors in Swift, explained

    What a constructor does

    A constructor sets an object up at the moment it is created. In Swift it is called an initializer, and it is declared with the keyword init. Without one, you would create an object and then fill in its fields one assignment at a time:

    var dog = Dog()
    dog.name = "Rex"
    dog.age = 4
    

    That is noisy, and nothing stops you from forgetting a line. An initializer puts the setup into the call itself: Dog(name: "Rex", age: 4) returns a finished object.

    Defining an initializer

    class Dog {
        var name: String
        var age: Int
    
        init(name: String, age: Int) {
            self.name = name
            self.age = age
        }
    }
    

    Two features set an initializer apart from a method. It is always named init, never after the class, and it has no func keyword and no return type. Also notice that the properties name and age have no default values. That is allowed because the initializer assigns every one of them. Swift insists that all stored properties have a value before the initializer finishes.

    The self keyword

    The parameter name and the property name share an identifier. Inside init, a bare name means the parameter, because the nearer declaration wins. self is a reference to the object being built, so self.name is the property. Writing name = name would assign the parameter to itself, and in fact does not compile: the compiler reports cannot assign to value: 'name' is a 'let' constant, since parameters are constants.

    Calling the initializer

    Swift uses the parameter names as argument labels in the call:

    let rex = Dog(name: "Rex", age: 4)
    let luna = Dog(name: "Luna", age: 7)
    

    The labels are required, and so is their order. Each of these fails to compile: Dog("Rex", 4) gives missing argument labels 'name:age:' in call, and Dog(age: 4, name: "Rex") gives argument 'name' must precede argument 'age'. If you don't want labels, put an underscore before the parameter name, as in init(_ name: String), and call Dog("Rex").

    The default initializer

    If every stored property has a default value and you write no initializer, Swift gives the class init(), which is why Dog() worked in earlier lessons. The moment the properties lack defaults you must write an initializer. And once you write one, the empty Dog() is no longer available unless you add it: Dog() against the initializer above fails with missing arguments for parameters 'name', 'age' in call.

    A class can have several initializers with different parameters, as long as each one sets up all the properties. A convenience init is a shortcut that calls another initializer of the same class with self.init(...), for example to supply a default age of 0.

    The exercise

    The starter Dog declares name and age but has no initializer, and mainFunction prints rex and luna without creating them. Add the initializer, then create both dogs:

    class Dog {
        var name: String
        var age: Int
    
        init(name: String, age: Int) {
            self.name = name
            self.age = age
        }
    }
    
    class Solution {
        func mainFunction() {
            let rex = Dog(name: "Rex", age: 4)
            let luna = Dog(name: "Luna", age: 7)
            print("\(rex.name) is \(rex.age)")
            print("\(luna.name) is \(luna.age)")
        }
    }
    

    The output is:

    Rex is 4
    Luna is 7
    

    Common mistakes

    • Dropping self. when parameter and property share a name, which fails as shown above.
    • Leaving a property unset. If init assigns only name, the compiler rejects it, because age has no value.
    • Adding func or a return type. An initializer has neither.
    • Omitting the labels at the call site.

    Key takeaways

    • An initializer is written init(...), with no func and no return type.
    • It must give every stored property a value.
    • Use self.name to mean the property when a parameter has the same name.
    • Parameter names become argument labels at the call, unless you write _.
    • Defining your own initializer removes the automatic Dog().
    Keep going with SwiftAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free