Let's think again the reason of why Swift init method is designed like this before we continue to dig deeper into it.

In fact, the core considering is for safety. In Objective-C, init method is not safe at all: there is no guarantee that neither the init method will be called only once when the object getting initiated, nor the variables in the instance would be ready to use after that method. It may even cause other issues if you are using the property in the init method. Although Apple suggests not using property in init method, it is not a compelling rule, so some developers always do such thing.

Turn to Swift, there is a very strict init method. The designated init method is strengthened. An init method without any modifiers is a designated init method, in which all non-optional properties should be assigned. And the super version of init method is also a must (either explicitly or implicitly) in the child class. No matter how an instance gets initiated, all needed properties will be assigned, to ensure the instance is fully initiated when the init method finishes.

class ClassA {
    let numA: Int
    init(num: Int) {
        numA = num
    }
}

class ClassB: ClassA {
    let numB: Int

    override init(num: Int) {
        numB = num + 1
        super.init(num: num)
    }
}

In the example above, we can use let to assign a constant variable. This is only permitted in init method and after an instance initiated, the constant cannot be changed anymore. This is very useful when creating a multithread program. Constant variables will be always safe to use once assigned in init method, without any concern about annoying multithread issues.

There is a different type to init method. We can add convenience keyword before init to make it a convenience init method. It is a "second-class citizen" in Swift world, since all convenience must call the designated init method in the same type to finish initiating. Moreover, convenience cannot be neither overridden in subclass nor called from subclass by super.

class ClassA {
    let numA: Int
    init(num: Int) {
        numA = num
    }

    convenience init(bigNum: Bool) {
        self.init(num: bigNum ? 10000 : 1)
    }
}

class ClassB: ClassA {
    let numB: Int

    override init(num: Int) {
        numB = num + 1
        super.init(num: num)
    }
}

In a child class, once we implement a needed init method for convenience init method of the parent class, we will be able to use convenience init of the parent class. In the code above, we override the init(num: Int). Because of that, we can use convenience init(bigNum: Bool) to initiate the subclass even there is no init method which accepts a bigNum in ClassB:

let anObj = ClassB(bigNum: true)
// anObj.numA = 10000, anObj.numB = 10001

In summary, an init method should always stick to these two principles:

  1. The init path should fully initiate the object. All properties should be configured properly. This could be guaranteed by calling the designated init method of the current type;
  2. Designated init method in subclass must call a designated init method in super, which could guarantee the super class gets initiated properly.

Sometimes, we also want the subclass to implement some designated init methods of the super class. By adding required to these methods in the super class, we can force subclass to override them. The advantage of this would be the convenience init method which depends on the designated method that could be used even in subclass. A good example is the init(bigNum: Bool) above. If we want this method to be used for any subclass at any time, we would mark the depended init(num: Int) as required. When we call init(bigNum: Bool) in subclass, there is always a path for proper initiating.

class ClassA {
    let numA: Int
    required init(num: Int) {
        numA = num
    }

    convenience init(bigNum: Bool) {
        self.init(num: bigNum ? 10000 : 1)
    }
}

class ClassB: ClassA {
    let numB: Int

    required init(num: Int) {
        numB = num + 1
        super.init(num: num)
    }
}

Last but not least, not only for designated init method, even for convenience init method we can add required to ask subclass to implement. It is advantageous when the subclass not using the convenience init methods in superclass.