Demystifying Initializers in Swift Programming
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.
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.