Hacking the iOS Interview

Swift — Initialization

Shivam Pandey
Hash Coding
Published in
6 min readMar 8, 2021

--

Initializations in Swift can be asked in interviews with respect to both structs and classes. While this concept is relatively easier to understand when it comes to structs, however, things can get a bit tricky when answering questions regarding classes. We need to be aware of the 2-phase initialization for classes along with the convenience and designated initializers. Here, I will try to cover the questions which can be asked in the interviews, so make sure to bookmark this post!

Difference between convenience vs designated initializers?

Both convenience and designated initializers are related to classes. Designated initializers initialize all the stored properties of a class and then move up to the superclass to initialize all its properties:

Usually, a class will have only a single designated initializer.

The convenience initializer does not initialize all the stored properties of the class. However, it will rely on other initializers present in the class to initialize the stored properties of that class with some default values.

Example showing convenience initializer directly calling a designated initializer

Why are convenience initializers provided, when we already have designated initializers?

The convenience initializers act as a secondary initializer, and these are used in those scenarios where it is not required for the caller to provide all the initial values of that class at the time of initialization. The caller will use these initializers to create the instance by providing only some of the stored properties, and the rest of the properties will be initialized with the default values provided inside that same class.

Does Swift provide convenience and designated initializers for structs as well?

As inheritance is not supported in structs, Swift does not provide convenience and designated initializers for structs. However, a custom initializer in a struct can call other initializers present in the class, and these initializers are called delegating initializers.

What are delegating initializers?

Delegating initializers of a struct are those that call other initializers of the same struct. Delegate initialization is useful when you want to provide an alternate initializer argument list but you don’t want to repeat the logic that is in your custom initializer. Also, using delegating initializers helps reduce the amount of code you have to write.

Will the below-given code compile?

It will not compile as a convenience can call another convenience initializer, but eventually, the chain of convenience initializers has to end up with a call to the designated initializer.

Explain initializer delegation for class types?

Swift initializer delegation
Rules for initializer delegation (source: Swift Docs)

It becomes easier to remember the initializer delegation rule for class types after seeing the above image. It can be summed up in two categories:

  • Convenience initializer delegates across the class:
    This means that a convenience initializer can only call other initializers from the same class but cannot call the initializers of the superclass.
  • Designated initializer delegates up the class hierarchy:
    It means that it becomes the responsibility of the designated initializer to call the initializers of the superclass so that the stored properties of the parent class can be initialized. But at the same time, the designated initializer cannot call a convenience initializer of the superclass.

Explain Two-Phase initialization in Swift

Two-phase initialization in Swift is explained very clearly in Swift’s official documentation:

Class initialization in Swift is a two-phase process. In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.

This can be seen in the below code example:

Swift two phase initialization
Example showing Swift’s two-phase initialization

What are the compiler rules to support two-phase initialization?

There are four rules, which if not satisfied, the compiler will not let the code compile. These are important because it helps in preventing the accessing the store properties before they are initialized. The rules can be summarised as below:

  • A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.
Example showing Swift’s two phase initialization’s safety rule
super.init(…) is not allowed to be called until all the properties of the Employee class has been initialized.
  • A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.
self.name is not allowed to be accessed before calling super.init(…)
  • A convenience initializer must delegate to another initializer before assigning a value to any property (including properties defined by the same class). If it doesn’t, the new value the convenience initializer assigns will be overwritten by its own class’s designated initializer.
self.profile is not allowed to be assigned before calling another initializer from the convenience initializer
  • An initializer can’t call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.
getEmployee() method cannot be called until the 1st phase of initialization is complete

What are memberwise initializers?

I have explained the answer to the above question in my earlier post: Swift — Structs vs Classes — Part I. You can refer to the same, and can also go through the other interview questions based on structs vs classes in the same post.

What are failable initializers?

There can be certain situations, which if not satisfied, should not result in the formation of a concrete instance — be it a class, a struct, or an enum.

The interviewer can give you a situation based on the Employee class. He can ask to design a code in such a way that the caller cannot initialize the Employee instance if the age of the employee is less than 18 or greater than 60.

We can add a failable initializer to the Employee class as given below in the example:

Example showing Swift’s failable initializer

What are required initializers?

Required initializers are those initializers that are supposed to be overridden by each and every subclass of that class.

The Citizen class gives an error as it has not implemented the required initializer from its superclass

Next, following up on the required keyword, the interviewer can ask about the explanation of the error shown in the below code:

You must have observed that when you create a subclass of UIView, the compiler forces you to implement init(coder:). When you create the CustomView’s instance using Interface Builder, the method init(coder:) is called so that any custom initialization can be done here.

I hope you find this story helpful. If you enjoyed it, share this with your community, and feel free to hit the clap button below 👏 to help others find it! Thanks for reading. 👍

Get notified every time we post a new story to our publication. Follow us now

We are dedicated to our goal to help every iOS developer grow and give their best in the interview. Every week we’ll be coming up with newer topics. Don’t miss them. Subscribe Now. ✉️

--

--

Shivam Pandey
Hash Coding

Lead Engineer iOS @airtelxlabs | Editor: Hash Coding