In this Swift tutorial, we’ll be discussing an important concept, namely Swift init or Swift initialization. Initialization is what happens when we create an instance of some type.
Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.
Initializers are similar to constructors in java programming. Swift being a type-safe language has placed a lot of rules for initializers. It can get tricky to implement unless you’ve got a good hold of the concept.
init() {
// initialise the stored properties here.
}
Let’s look at a sample class below.
class A{
//Compilation error. No initializer is defined.
var a : Int
var b : String
var c : Int?
let website = "JournalDev"
}
Above class won’t compile. The swift compiler complains that the stored properties aren’t initialized. Stored Properties can’t be kept in an undetermined state. This leaves us with two possible options:
init()
for initializing the properties.Let’s look at each of the approaches one at a time.
class A{
var a : Int = 5
var b : String = "Hello. How you're doing"
var c : Int?
let website = "JournalDev"
}
Here we’ve set a default value for each of the stored properties, hence Swift provides us the default initializer implicitly. All the properties and functions can be accessed using the dot operator over an instance of the class once it’s initialized.
var object = A()
object.a = 10
object.c = 2
The second way is to initialize the stored properties using the init()
method as shown below.
class A{
var a : Int
var b : String
var c : Int?
let website = "JournalDev"
init(a: Int, b: String) {
self.a = a
self.b = b
}
}
var object = A(a: 5, b: "Hello World")
Note: Swift Optional is not a stored properties. Hence, they need not be initialized. Stored properties are accessed inside the init()
method using self
property. Note: self
is used to refer to the current instance within its own instance methods (Similar to this
in java). The above initializer is the primary initializer of the class. It’s also known as designated initializer(we’ll discuss this later). Initializers lets us modify a constant property too.
class A{
var a : Int
var b : String
var c : Int?
let website : String
init(a: Int, b: String, website: String) {
self.a = a
self.b = b
self.website = website
}
}
var object = A(a: 5,b: "Hello World", website: "JournalDev")
Structures being value types, don’t neccessarily require an initializer defined. Structure types automatically receive a memberwise initializer unless you’ve defined custom initializer(s). Following are the code snippets that describe the various ways to initialize a struct.
struct Rect{
var length : Int
var breadth : Int
}
var r = Rect(length: 5, breadth: 10)
struct Rect{
var length : Int = 5
var breadth : Int = 10
}
var r = Rect()
var r1 = Rect(length: 10, breadth: 5)
Since we’ve assigned default values to the stored properties in the above snippet, we receive a default initializer without member initialization alongwith the memberwise initializer.
struct Rect{
var length : Int
var breadth : Int
init(length: Int, breadth: Int) {
self.length = length + 10
self.breadth = breadth + 10
}
}
var r = Rect(length: 10, breadth: 5)
In the above case, we’ve defined our own custom initializer. Using Parameters without External Name When an external name is not needed for an initializer, underscore ‘_’ is used to indicate the same as shown below.
class A{
var a : Int
var b : String
var c : Int?
let website = "JournalDev"
init(_ a: Int, _ b: String) {
self.a = a
self.b = b
}
}
var object = A(5,"Hello World")
struct Rect{
var length : Int
var breadth : Int
init(_ length: Int, _ breadth: Int) {
self.length = length + 10
self.breadth = breadth + 10
}
}
var r = Rect(10, 10)
Initializers for classes can be broadly classified into the following types:
convenience
modifier placed before the init
keywordclass Student{
var name : String
var degree : String
init(name : String, degree: String) {
self.name = name
self.degree = degree
}
convenience init()
{
self.init(name: "Unnamed", degree: "Computer Science")
}
}
var student = Student()
student.degree // "Computer Science"
student.name // "Unnamed"
Convenience Initializers are useful when it comes to assigning default values to stored properties.
It’s possible to call an initializer from another one thereby avoiding code duplication. Value Types like Structures do not support inheritance. Hence the only possible way is to call initializer within the same structure. An example is given below.
struct Rect{
var length : Int
var breadth : Int
init(_ length: Int, _ breadth: Int) {
self.length = length
self.breadth = breadth
}
init(_ length: Int)
{
self.init(length, length)
}
}
var r = Rect(10, 5)
var r1 = Rect(15) //initialises the length and breadth to 15
Classes being reference types support inheritance. Thus initializers can call other initializers from superclass too thereby adding responsibilities to properly inherit and initialize all values. Following are the primary rules defined for handling relationships between initializers.
Following illustration describes the above rules.
Designated initializers must always delegate up. Convenience initializers must always delegate across. super keyword is not possible for a convenience initializer in a subclass.
Subclasses in Swift do not inherit their superclass’s initializers by default unless certain conditions are met(Automatic Initializer Inheritance). This is done to prevent half-baked initialization in the subclass. Let’s look at how designated and convenience initializers work their way through inheritance. We’ll be defining a Vehicle base class that’ll be inherited by the relevant subclasses. We’ll use Enums as a type in the classes. Our base class Vehicle is defined as shown below.
enum VehicleType : String {
case twoWheeler = "TwoWheeler"
case fourWheeler = "FourWheeler"
}
class Vehicle{
var vehicleType : VehicleType
init(vehicleType: VehicleType) {
self.vehicleType = vehicleType
print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
}
convenience init()
{
self.init(vehicleType: .fourWheeler)
}
}
var v = Vehicle(vehicleType: .twoWheeler)
Note: The convenience initializer must call the designated initializer of the same class using self.init
Let’s define a subclass of the above class as shown below.
enum TwoWheelerType : String
{
case scooty = "Scooty"
case bike = "Bike"
}
class TwoWheeler : Vehicle{
var twoWheelerType : TwoWheelerType
var manufacturer : String
init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
self.twoWheelerType = twoWheelerType
self.manufacturer = manufacturer
print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
super.init(vehicleType: vType)
}
}
Important points to note:
super.init
is called.Following code would lead to a compile-time error.
class TwoWheeler : Vehicle{
var twoWheelerType : TwoWheelerType
var manufacturer : String
init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
self.twoWheelerType = twoWheelerType
self.manufacturer = manufacturer
self.vehicleType = vType //Won't compile
super.init(vehicleType: vType)
//self.vehicleType = .fourWheeler //This would work.
}
}
var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)
As explained earlier, the superclass initializer isn’t inherited automatically in the subclass. So the below initialization would fail.
var t = TwoWheeler(vehicleType: .twoWheeler) //manufacturer property isn't initialized.
To override an initializer, the subclass initializer must match with the designated initializer of the superclass. The override
keyword is appended to the initializer in this case.
class TwoWheeler : Vehicle{
var twoWheelerType : TwoWheelerType
var manufacturer : String
init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
self.twoWheelerType = twoWheelerType
self.manufacturer = manufacturer
print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
super.init(vehicleType: vType)
}
override init(vehicleType: VehicleType)
{
print("Class TwoWheeler. Overriden Initializer. \(vehicleType.rawValue)")
self.twoWheelerType = .bike
self.manufacturer = "Not defined"
super.init(vehicleType: vehicleType)
}
The below initializer doesn’t override the one from superclass since the parameter name is different.
//This would give a compile-time error since the parameter v doesn't match with the superclass.
override init(v: VehicleType)
{
self.twoWheelerType = .bike
self.manufacturer = "Not defined"
super.init(vehicleType: v)
}
Using Convenience Initializer to override the one from superclass.
class TwoWheeler : Vehicle{
var twoWheelerType : TwoWheelerType
var manufacturer : String
init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
self.twoWheelerType = twoWheelerType
self.manufacturer = manufacturer
print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
super.init(vehicleType: vType)
}
override convenience init(vehicleType: VehicleType) {
self.init(twoWheelerType: .bike, manufacturer: "Not Defined", vType: .twoWheeler)
self.vehicleType = vehicleType
}
}
var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)
t = TwoWheeler(vehicleType: .twoWheeler)
//Output
Following gets printed on the console:
Class TwoWheeler. Scooty manufacturer is Hero Honda
Class Vehicle. vehicleType is TwoWheeler
Class TwoWheeler. Bike manufacturer is Not Defined
Class Vehicle. vehicleType is TwoWheeler
The convenience initializer has override
keyword appended to it. It calls the designated initalizer of the same class. Note: The order of the keywords convenience
and override
doesn’t matter.
Writing the keyword required
before the initializer indicates that each subclass must implement that initializer. Also, the required
modifier must be present at the respective subclass implementations as well. An example of Required Initializers on the above two classes is given below.
class Vehicle{
var vehicleType : VehicleType
required init(vehicleType: VehicleType) {
self.vehicleType = vehicleType
print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
}
convenience init()
{
self.init(vehicleType: .fourWheeler)
}
}
class TwoWheeler : Vehicle{
var twoWheelerType : TwoWheelerType
var manufacturer : String
init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
self.twoWheelerType = twoWheelerType
self.manufacturer = manufacturer
print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
super.init(vehicleType: vType)
}
required init(vehicleType: VehicleType) {
self.manufacturer = "Not Defined"
self.twoWheelerType = .bike
super.init(vehicleType: vehicleType)
}
}
Note: Adding a required modifier, indicates that the initializer would be overridden. Hence the override keyword can be ommitted in the above case. Using a Required Initializer with Convenience Required and convenience initializers are independent of each other and can be used together. Let’s create another subclass of Vehicle
to demonstrate the use of required
and convenience
modifiers together.
enum FourWheelerType : String
{
case car = "Car"
case bus = "Bus"
case truck = "Truck"
}
class FourWheeler : Vehicle
{
var fourWheelerType : FourWheelerType
var name : String
init(fourWheelerType : FourWheelerType, name: String, vehicleType: VehicleType) {
self.fourWheelerType = fourWheelerType
self.name = name
print("Class FourWheeler. \(self.fourWheelerType.rawValue) Model is \(self.name)")
super.init(vehicleType: vehicleType)
self.vehicleType = vehicleType
}
required convenience init(vehicleType: VehicleType) {
self.init(fourWheelerType: .bus, name: "Mercedes", vehicleType: vehicleType)
}
}
class Car : FourWheeler{
var model : String
init(model: String) {
self.model = model
print("Class Car. Model is \(self.model)")
super.init(fourWheelerType: .car, name: self.model, vehicleType: .fourWheeler)
}
required init(vehicleType: VehicleType)
{
self.model = "Not defined"
print("Class Car. Model is \(self.model)")
super.init(fourWheelerType: .car, name: self.model, vehicleType: vehicleType)
}
}
Important things to note in the above code snippet:
There are two circumstances under which a subclass automatically inherits the initializers from the superclass.
The first rule in action is demonstrated in the snippet below:
class Name {
var name: String
init(n: String) {
self.name = n
}
}
class Tutorial: Name {
var tutorial : String? = "Swift Initialization"
}
var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "JournalDev")
The second rule in action is demonstrated in the snippet below.
class Name {
var name: String
init(n: String) {
self.name = n
}
convenience init()
{
self.init(n: "No name assigned")
}
}
class Tutorial: Name {
var tutorial : String? = "Swift Tutorial"
override init(n : String) {
super.init(n: n)
}
}
var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "JournalDev")
var childObject2 = Tutorial()
print(childObject2.name) //prints "No name assigned
The convenience initializer of the superclass is automatically available in the subclass in the above code.
We can define a failable initializer using the keyword init?
on Classes, Structures or Enumerations which gets triggered when the initialization process fails. Initialization can fail for various reasons: Invalid parameter values, absence of an external source etc. A failable initializer creates an optional value of the type it initializes. We’ll be returning a nil to trigger an initialization failure(Though an init
doesn’t return anything). Failable Initializers With Structures
struct SName {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
var name = SName(name: "JournalDev")
if name != nil {
print("init success") //this gets displayed
}
else{
print("init failed")
}
name = SName(name: "")
if name != nil {
print("init success")
}
else{
print("init failed") //this gets displayed
}
Failable Initializers With Enums
enum CharacterExists {
case A, B
init?(symbol: Character) {
switch symbol {
case "A":
self = .A
case "B":
self = .B
default:
return nil
}
}
}
let ch = CharacterExists(symbol: "C")
if ch != nil {
print("Init failed. Character doesn't exist")
}
class CName {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
var name = CName(name: "")
if name != nil {
print("init success")
}
else{
print("init failed")
}
Note: A failable initializer and a non-failable initializer can’t have the same parameter types and names.
You can override a failable initializer in your subclass. A failable initializer can be overridden with a non-failable initializer but it cannot happen vice-versa. An example of overriding a failable with a non-failable initializer is given below.
class CName {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
var name = CName(name: "")
class SubName : CName{
var age : Int
override init(name: String)
{
self.age = 23
super.init(name: name)!
}
}
Note: Forced unwrapping is used to call a failable initializer from the superclass as part of the implementation of a subclass’s nonfailable initializer. This brings an end to swift initi tutorial. References : Apple Docs
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
Overriding a Failable Initializer This topic I don’t understand one thing that, from subclass overridden init method super.init(name: name)! will crash if it returns nil right.?
- sagar