Demystifying Initializers in Swift Programming

Hussein TijaniHussein Tijani
4 min read

Initializers in Swift are special methods that help with the creation of new instances of a particular entity before they are available for use in the application. Initializers handle the process of setting an initial value for each stored property on that instance and performing any other setup or initialization that’s required before the new instance is ready for use. In Swift, you encounter different initializer types, each with distinct roles. In this article, we’ll unravel the intricacies of each initializer type and explore their practical applications.

Designated Initializers

Designated initializers are the primary initializers of Classes, Structs and Enums in Swift. They handle the assignments of property values and instantiations of methods and are declared using the init keyword. The code below shows an example of a designated initializer.

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

When declaring a class in Swift, the designated initializer must be declared as shown above. However, when declaring Structs this is not compulsory as Structs have a default designated initializer.

The code below illustrates the instantiation of an object of the Human class above.

let human1: Human = Human(name: "John", age: 25, gender: "Male")

print(human1.name) // John
print(human1.age) // 25
print(human1.gender) // Male

Convenience Initializer

Convenience initializers provide a way to instantiate an object of an entity more easily, by providing default values for some of the entity’s properties and calling the designated property of the entity. This is demonstrated in the code below.

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

  // Defining convenience initializer
  convenience init(name: String, age: Int){
    self.init(name: name, age: age, gender: "Female")
  }
}

When instantiating an object of an entity that has a convenience initializer, you can choose to use either the convenience initializer or the designated initializer of the entity. The code example below illustrates how you would create instances using both.

// Using designated initializer
let human1: Human = Human(name: "John", age: 25, gender: "Male")

// Using convenience initializer
let human2: Human = Human(name: "Jane", age: 25)

print(human1.gender) // Male

print(human2.gender) // Female

Failable Initializers

When the initialization of an object could fail due to bad parameter values or the absence of a parameter value, failable initializers are implemented to handle these situations gracefully. These initializers are declared with a? behind the init keyword. The code example below illustrates the declaration of a failable initializer.

class Human {
  var name: String
  var age: Int
  var gender: String
  init?(name: String, age: Int, gender: String){
    if(name.isEmpty || age < 18 || gender.isEmpty){
      return nil
    }
    self.name = name
    self.age = age 
    self.gender = gender
  }
}

If an error occurs when using a failable initializer, nil is returned hence the object returned from a failable initializer’s operation is an Optional. The example below demonstrates the initialization of an object of the Human class above using its failable initializer.

// Passing an invalid value to the age parameter
let human3: Human? = Human(name: "John", age: 0, gender: "Male")

// Passing valid values only 
let human4: Human? = Human(name: "Jane", age: 23, gender: "Female")

// Guarding against a possible nil value
if let human3 = human3{
  print(human3)
}else{
  print("Human3 is nil")
}

if let human4 = human4{
  print(human4.name) // Jane
  print(human4.age) // 23
  print(human4.gender) // Female
}else{
  print("Human4 is nil")
}

Required Initializers

Required initializers are declared to specify that all classes that inherit from the class within which it is declared must initialize them. The example below illustrates the declaration and use of a required initializer.

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

class African: Human {
  var melaninTone: Int

  required init(name: String, age: Int, gender: String){
    self.melaninTone = (age / 2) % 3
    super.init(name: name, age: age, gender: gender)
  }
}

let african: African = African(name: "John", age: 22, gender: "Male")

print(african.name) // John
print(african.age) // 22
print(african.gender) // Male
print(african.melaninTone) // 2

Conclusion

Mastering Swift’s initializers is pivotal in creating well-structured and robust code. From designated and convenience initializers to failable and required ones, these methods empower you to craft efficient and maintainable applications. By harnessing the power of initializers, you’ll create Swift applications that are not only functional but also elegant in design.

As you continue your Swift journey, remember that initializers form the foundation of your code’s birth and set the stage for its productive life.

0
Subscribe to my newsletter

Read articles from Hussein Tijani directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hussein Tijani
Hussein Tijani

Hussein Tijani is a Software Engineer based in Lagos, Nigeria. His area of expertise encompasses the entire product development cycle, API design and development and third-party integrations. He is passionate about sharing his Software Engineering knowledge through technical articles, verbal conversations and social media.