Swift initializers

Convenience, designated, failable and required initializers all in one article!

Initializers prepare a class, struct or enum by setting stored properties before they are used. An initializer can also perform other required setup before the programmer deems it to be ready for use.

Difficulty: Beginner | Easy | Normal | Challenging

Prerequisites:

  • Be able to produce a “Hello, World!” iOS application (guide HERE)
  • Knowledge of classes, structures and enums (guide HERE)

Terminology

failable initializer: an initializer that returns nil if supplied with an invalid value, or a new instance

initializer: methods used to create an instance of a particular type

initialization: the process of preparing an instance of a class, structure, or enumeration for use

Basic initialization

When we initialize instances of a type, we must make sure that all the stored properties are initialized which insures that the the type is not left in an indeterminate state.

Classes

Classes can have initializers for all the stored properties

class Person {
var age: Int
init (age: Int) {
self.age = age
}
}
let dave = Person(age:24)

Or at least an initilizer that initializers all the stored properties

class Dog {
var mood: String
init () {
self.mood = "Happy"
}
}
let bear = Dog()

Structs

You can initilize a struct much like class above.

However, you may not be aware that the Swift compiler can generate an init for you at runtime (you won’t see it in your code, rather you will be able to use it when you run your code). This is known as memberwise initializers for structures, and since there is no work people term this as getting this initializer “For free”.

struct Square {
var height: Int
var width: Int
}
let square = Square(height:2, width: 10)

If you with to omit the external names for the parameters you would need to declare an initializer

struct Square {
var height: Int
var width: Int
init(_ height: Int, _ width: Int)
self.height = height
self.width = width
}
let square = Square(2, 10)

Enums

The Swift compiler works in a similar way for enum

enum Day {
case Sunday
case Monday
case Tuesday
case Wednesday
case Thursday
case Friday
case Saturday
}
let day = Day.Friday

and if you specify an enum with a raw value type you have access to a failable initialize (more on that later) that takes in a raw value type

enum Weather {
case Hot = "Toasty"
case Cold
}
Weather.Hot.rawValue // Toasty
Weather.Cold.rawValue // Cold
Weather(rawValue: "Toasty") // Hot
Weather(rawValue: "Not there") // nil

as you can see the raw value either return an instance (in the case of Hot), or nil (in the case of nil) — classically Optional.

Custom initializers

Using classes as examples (for the following examples classes, structs and enums are similar).

We can have fewer initializers than there are stored properties, which might be useful for a situation like the one that follows:

class Address {
var number: String
var address: String
init(firstLineAddress: String) {
let lineComponents = firstLineAddress.componenets(separatedBy: " ")
self.number = lineComponents.first!
self.address = lineComponents.last!
}
init(number: String, address: String) {
self.number = number
self.address = address
}
}
let address = Address(firstLineAddress: "22 King's Street")
print(address.number, address.address)
let addressQ = Address(number: "44", address: "Queen's Street")
print(addressQ.number, addressQ.address)

where the compiler chooses which of the init initializers will be called.

We can add internal and external names for our initializer:

class Food {
var name: String
init(forename name: String) {
self.name = name
}
}
let fruit = Food(forename: "Orange"

Default initializers

Much like memberwise initializers for structs, if we have default values set for each and every property we do not need to specify an initializer for a class

class Vegetable {
var name: String = "Turnip"
}
let turnip = Vegetable()

As hinted at above, structs default to the memberwise initializer in this case even if each property has a default value.

struct Meat {
var name: String = "Pork"
}
let pig = Meat()
let cow = Meat(name: "Beef")

Convenience initializers

Up to this point we have covered Designated initializers. These are the initializers that prepare each stored property.

Each class must have at least one designated initializer. But convenience initializers are optional, that is that they are designed to make your life easier as supporting initializers.

Code for the following example

Here we have an Animal with a subclass of a Pig. All animals can scream, and Pigs (because they are nice) have names.

class Animal {
var created: Bool
init() {
created = true
}
func screams() {
print ("Ahhhhh")
}
}
class Pig: Animal {
var name: String
init(name: String) {
self.name = name
super.init()
}
convenience override init() {
self.init(name: "No Name")
}
}
let piggy = Pig()
(piggy as Animal).created
let john = Pig(name: "John")
(john as Animal).created

Convenience initializers for classes

It would be nice to be able to instantiate a Pig without a name.

For this task, we are focussing on the following function within the Pig

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

this convenience init (and in fact all convenience inits) calls the designated initalizer from the same call.

Apple call this delegation across, and is signified with the call to self.init

Image for post
Image for post

Designated initializers for classes

For this we are focussing on the following function within the Pig

init() {
self.name = name
super.init(name: "No Name")
}

The super call references the initializer in the super class, in this case the Animal that the pig inherits from

Image for post
Image for post

Automatic inheritance for initializers

Only if:

  • If a subclass doesn’t define any designated initialisers, it inherits all of the designated initalizers from the superclass
  • If a subclass gives an implementation of all of the superclass designated initializers then it automatically inherits all of the superclass convenience initializers

So do demonstrate the first case we can add a new animal (which is in this case a sheep, and the original animal class is repeated here):

class Sheep: Animal {}class Animal {
var created: Bool
init() {
created = true
}
func screams() {
print ("Ahhhhh")
}
}
let wooly = Sheep()

for the second case, we need a superclass with convenience intializers — and the Pig example above indeed provides us with this. Be aware that this feels much like the first example case (stated above).

class Pig: Animal {
var name: String
init(name: String) {
self.name = name
super.init()
}
convenience override init() {
self.init(name: "No Name")
}
}
let lunch = Tamworth(name: "Lunch")

Failable initializers

Much like any optionals in the Swift language, but applied to initializers. This means that either the initializer works and returns a properly formed class, struct or enum. In the case of failure it returns nil.

So failable initiailizers are marked with a question mark ? which is demonstrated in the following code block:

struct Veggie {
let type: String
init?(type: String) {
if type == "Tomato" { return nil }
self.type = type
}
}
let tomato = Veggie(type: "Tomato") // nil
let carrots = Veggie(type: "Carrots") // is a Veggie

As you can see, we return nil when the initilizer fails but there is no need to return anything when it succeeds.

Note that when you override, you can override a failable initializer with a nonfailable initializer (since a value is a possible outcome for an optional). However, the reverse is not true, you cannot override a nonfailable initializer with a failable initialiser (since a non-optional cannot resolve to nil).

Required initializers

Required initializers indicate that every subclass of a class needs to inherit the initializer marked as required.

class Clothes {
var name: String
required init() {
self.name = "No Name"
}
}
class Shoes {
var shoesName: String = "No Shoes Names"
init(shoesName: String) {
self.shoesName = shoesName
}
required init() {
fatalError("init() has not been implemented")
}
}
let adidas = Shoes(shoesName: "Adidas")

Here the required init() had to be implemented in the shoes class — but if it is called will cause a fatalError because of the fatalError("init() has not been implemented") line.

Conclusion…

You’ve a range of tools in order to create Enums, Classes and Structs. These initilizers are essential, and hopefully give you a leg-up in your coding journey.

The fact that Structs have memberwise initializers can put off some, because you are effectively using an initializer that you have not even created yourself!

That’s the magic of Swift! I hope this guide has been at least reasonably interesting for you. As ever…

Happy coding!

Repo

Here is the repo with the relevant code from this article:

https://github.com/stevencurtis/swiftinitilizers

Want to get in contact? Try the link here:

https://twitter.com/stevenpcurtis

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store