Swift Multiple Init Methods – Convenience Init

In Swift, you can add multiple init methods to a class by writing a convenience init method in an extension.

For example, let’s create a Fruit class and add a second init method to it:

class Fruit {
    let name: String?
    let color: String?

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

extension Fruit {
    convenience init(_ csv: String) {
        let parts = csv.components(separatedBy: ",")
        let name = parts[0]
        let color = parts[1] 
        self.init(name: name, color: color)
    }
}

Now you can use either one of the init methods. The Swift compiler determines which one to use based on the arguments you give it.

let apple = Fruit(name: "Apple", color: "Red")
let banana = Fruit("Banana,Yellow")

To add multiple inits to structures, leave the convenience keyword out. Also, you do not need to write the additional inits to an extension.

In this guide, you learn how to add multiple init methods to classes and structures in Swift.

Initialization in Swift

To create an object that represents a type in Swift, the object has to be initialized.

The default way to initialize objects in Swift is via designated initializers.

A designated initializer is an initializer you implement into a class as an init method.

For example, let’s create a Fruit class:

class Fruit {
    let name: String?
    let color: String?

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

Now it is possible to instantiate Fruit objects like this:

let banana = Fruit(name: "Banana", color: "Yellow")

But what if you wanted to initialize Fruit objects in an alternative way and keep the default initializer in place?

This is where convenience initializers can help you.

Convenience Initializers in Swift

In Swift, you can introduce alternative initializers to a type. These initializers are called convenience initializers.

A convenience initializer, as the name suggests, is just for convenience. It can only be used as an addition to the designated initializer. A convenience initializer has to use the original initializer to instantiate objects.

To declare a convenience initializer in Swift:

  1. Write an extension to the type you want to initialize.
  2. Write the alternative initializer by using convenience keyword in front of the init method.
  3. Specify the desired behavior into the convenience initializer method.
  4. Call the original initializer from the convenience init to actually initialize an object.

For instance, let’s continue with the Fruit class seen before.

In addition to being able to initialize a Fruit object like this:

Fruit(name: "Banana", color: "Yellow")

You would also like to initialize one from a string where the name and color are separated by a comma.

Fruit("Banana,Yellow")

To make this possible, we need to write a convenience initializer to the Fruit class. This acts as an alternative initializer for the class.

class Fruit {
    let name: String?
    let color: String?

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

extension Fruit {
    convenience init(_ csv: String) {
        // Parse the name and color from a string
        let parts = csv.components(separatedBy: ",")
        let name = parts[0]
        let color = parts[1]
        // Call the original initializer
        self.init(name: name, color: color)
    }
}

Pay attention to how the convenience initializer turns the argument into values that are valid for the original initializer. Then it calls the original initializer to create a Fruit object.

A convenience initializer in swift allows multiple inits in classes
A convenience initializer always uses the original initializer to create objects

Now it is possible to use both initializers to create Fruit objects:

let banana = Fruit(name: "Banana", color: "Yellow")
let apple = Fruit("Apple,Red")

The way Swift compiler knows which one it should use is by looking at the parameters passed into the Fruit. When it sees the name and color labels, it knows to use the designated initializer. When it sees no parameter labels at all, it knows we want to use the convenience initializer.

And as you might already guess, there is no limit to how many convenience initializers you can have. You can add multiple convenience initializers to the same extension.

For example, let’s add two convenience initializers to the Fruit class:

class Fruit {
    let name: String?
    let color: String?

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

extension Fruit {
    convenience init(_ csv: String) {
        let parts = csv.components(separatedBy: ",")
        let name = parts[0]
        let color = parts[1]
        self.init(name: name, color: color)
    }

    convenience init(_ dict: [String: String]) {
        let name = dict["name"]!
        let color = dict["color"]!
        self.init(name: name, color: color)
    }
}

So far you have only worked with classes and convenience initializers. But what if you want to add multiple inits to a structure in Swift?

Multiple Inits in Structs in Swift

Similar to classes, structures can have multiple init methods.

To implement multiple init methods into a struct in Swift, leave the convenience keyword out. Also, you do not need to write the init methods to an extension.

For example, let’s re-create the above Fruit class with a structure that has the three initializers:

struct Fruit {
    let name: String?
    let color: String?

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

    init(_ csv: String) {
        let parts = csv.components(separatedBy: ",")
        let name = parts[0]
        let color = parts[1]
        self.init(name: name, color: color)
    }

    init(_ dict: [String: String]) {
        let name = dict["name"]!
        let color = dict["color"]!
        self.init(name: name, color: color)
    }
}

Now you can create Fruit objects with any of these three initializers:

let apple = Fruit(name: "Apple", color: "Red")
let banana = Fruit("Banana,Yellow")
let clementine = Fruit(["name": "Clementine", "color": "Orange"])

Here, the Swift compiler determines which init method to use based on the arguments.

Next, let’s simplify the Fruit structure a bit.

Memberwise Initializer and Structs in Swift

A Swift struct comes with a memberwise initializer that assigns each property to self. Thus, you do not need the first init method.

However, when you write additional initializers to the struct, the memberwise initializer goes away. To keep it in place, move the custom initializers into an extension:

struct Fruit {
    let name: String?
    let color: String?

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

extension Fruit {
    init(_ csv: String) {
        let parts = csv.components(separatedBy: ",")
        let name = parts[0]
        let color = parts[1]
        self.init(name: name, color: color)
    }

    init(_ dict: [String: String]) {
        let name = dict["name"]!
        let color = dict["color"]!
        self.init(name: name, color: color)
    }
}

Then, remove the init method from the Fruit struct.

struct Fruit {
    let name: String?
    let color: String?
    // init is automatically implemented by Swift
}

extension Fruit {
    init(_ csv: String) {
        let parts = csv.components(separatedBy: ",")
        let name = parts[0]
        let color = parts[1]
        self.init(name: name, color: color)
    }

    init(_ dict: [String: String]) {
        let name = dict["name"]!
        let color = dict["color"]!
        self.init(name: name, color: color)
    }
}

Now we are done. This structure behaves the same way as before. You can try it by creating Fruit objects in three different ways:

let apple = Fruit(name: "Apple", color: "Red")
let banana = Fruit("Banana,Yellow")
let clementine = Fruit(["name": "Clementine", "color": "Orange"])

Why/When Use Convenience Initializers in Swift?

A convenience initializer supports the initialization process. This makes calling the designated initializer more streamlined.

As a concrete example, let’s create a simple User class:

class User {
    let name: String?

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

Now you can create User objects by specifying the name of the user:

let user1 = User(name: "Alice")

But what if you do not know the name of the user yet? It would be convenient to be able to create User objects without having to specify the name for the user.

To do this, you can implement a convenience initializer that sets the name “Unknown” when no arguments are given.

class User {
    let name: String?

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

extension User {
    convenience init() {
        self.init(name: "Unknown")
    }
}

Now you can create User objects both with or without a name:

let user1 = User(name: "Alice")
let user2 = User()

As you can see, this makes creating users a bit more convenient. Now you have a consistent way to create blank users. You do not have to come up with a placeholder name each time you create a user without a name.

Also, even though structures do not use the convenience keyword, the same applies to them. An alternative initializer in a structure can make the initialization process more streamlined.

Conclusion

Today you learned how to create multiple init methods in Swift.

To recap, you can create multiple initializers into classes by writing convenience initializers. A convenience initializer can only be written into an extension, not directly into the class.

A convenience initializer acts as an alternative initializer that does some processing before calling the original initializer.

When dealing with structs, you can write multiple init methods without using the convenience keyword. You also do not need to write the init methods into an extension.

Implementing additional initializers is useful when you want to support the initialization process for different types of input. For example, to create a User object, you can have a main initializer for creating users with names. To create a nameless user, you can have a convenience initializer that automatically assigns a default name.

Thanks for reading.

Happy coding!

Further Reading

Structs vs Classes in Swift

50 Swift Interview Questions

Leave a Comment

Your email address will not be published. Required fields are marked *