ProgrammingiOS Developer

How do failable initializers (`init?`) work in Swift? When and why should they be used, and what important nuances should be considered?

Pass interviews with Hintsage AI assistant

Answer

Failable initializers (init?) in Swift allow describing a situation where creating an instance of a type may fail and return nil. They are often used for validating input data or transformations that may not succeed. In a failable initializer, you can explicitly return nil, indicating that the object creation was unsuccessful.

Example:

struct User { let name: String let age: Int init?(name: String, age: Int) { guard !name.isEmpty, age > 0 else { return nil } self.name = name self.age = age } }

This way, you can prevent the creation of incorrect objects.

Important nuances:

  • In an inheritance chain, failable initializers can only be overridden by failable initializers.
  • When using a structure with a failable initializer in an array, where mapping initialization is done (compactMap), it is convenient for filtering invalid instances.

Tricky question

Question: What is the difference between init? and init!, and when should you use a failable initializer with implicit unwrapping?

Answer: init? returns an optional (<type?>), and if the initialization failed, it will return nil, requiring safe handling. init! returns an implicitly unwrapped optional (<type!>), and if the initialization failed, it will also return nil, but using such an object without checking will lead to a runtime crash. Use init! only if you are sure that the initialization cannot fail in your context (e.g., when working with storyboard in UIKit).

let value = Int("abc") // value will be nil

Examples of real errors due to ignorance of the nuances of the topic.


Story

When parsing JSON manually, a regular initializer was used instead of a failable one. This led to the creation of "empty" users, as validation did not work, and the application did not filter invalid data.


Story

Using init! with potentially invalid data caused the application to crash after an API update: the input data format changed, and an exception occurred during object extraction due to implicit unwrapping of nil.


Story

In a custom implementation of failable init, we forgot to explicitly return nil for certain scenarios, resulting in the structure being initialized with "dirty" fields, which later caused bugs in the business logic.