Why Use Required Initializers in Swift Classes

Why use required Initializers in Swift classes?

It's actually just a way of satisfying the compiler to assure it that if this class were to have any subclasses, they would inherit or implement this same initializer. There is doubt on this point, because of the rule that if a subclass has a designated initializer of its own, no initializers from the superclass are inherited. Thus it is possible for a superclass to have an initializer and the subclass not to have it. required overcomes that possibility.

One situation where the compiler needs to be satisfied in this way involves protocols, and works like this:

protocol Flier {
init()
}
class Bird: Flier {
init() {} // compile error
}

The problem is that if Bird had a subclass, that subclass would have to implement or inherit init, and you have not guaranteed that. Marking Bird's init as required does guarantee it.

Alternatively, you could mark Bird as final, thus guaranteeing the converse, namely that it will never have a subclass.

Another situation is where you have a factory method that can make a class or its subclass by calling the same initializer:

class Dog {
var name: String
init(name: String) {
self.name = name
}
}

class NoisyDog: Dog {

}

func dogMakerAndNamer(whattype: Dog.Type) -> Dog {
let d = whattype.init(name: "Fido") // compile error
return d
}

dogMakerAndNamer is calling the init(name:) initializer on Dog or a Dog subclass. But how can the compiler be sure that a subclass will have an init(name:) initializer? The required designation calms the compiler's fears.

What's the difference between a required initializer and a designated initializer?

Required initialisers and designated initialisers are not really related, though the associated keywords required and convenience are both used to specify restrictions on subclasses.

Required Initialisers

A required initialiser makes a guarantee that you can initialise a type, or any of its sub-types, with that initialiser. If you have an initialiser in a protocol and you conform something to that protocol, you have to use required (if it's a class) because that protocol guarantees that the initialiser is present on that class, and any of its subclasses. When you use required on an initialiser of a class, that signals that all of its subclasses can also be initialised using that method. This means you also need to add that initialiser to any of its subclasses.

protocol TestProtocol {
init()
}

class TestClass: TestProtocol {
required init() {

}
}

Here, the required keyword must be present because any subclasses of TestClass must also provide init() (because they also conform to TestProtocol).

Having a required initialiser allows you to initialise a class without knowing what it is at compile time, which is useful for a variety of reasons:

let classType: TestProtocol.Type = TestClass.self
let object = classType.init()

If your class conformed to multiple protocols, each with a different initialiser for example, each of those initialisers must also be required:

protocol OtherProtocol {
init(thing: Int)
}

class OtherClass: TestClass, OtherProtocol {
let thing: Int

required init() { // Required from superclass/its protocol
self.thing = 0
}

required init(thing: Int) { // Required from new protocol
self.thing = thing
}
}

Note that adding super.init() isn't required in this special case, because Swift will automatically include the call if it takes no parameters.

In all the above examples, the initialisers are designated because they do not include the convenience keyword.

Even if you didn't have any protocols, you can still make use of required by initialising a type of a class which isn't known at compile time:

class BaseClass {
let value: Int

required init(value: Int) {
self.value = value
}
}

class SubClass: BaseClass {
required init(value: Int) { // Required from superclass
super.init(value: value) // Must call desginated initialiser of superclass
}
}

let someBaseClassType: BaseClass.Type = SubClass.self
let someBaseClassInstance = someBaseClassType.init(value: 1)

Designated Initialisers

A designated initialiser is one which isn't a convenience initialiser (i.e, marked with convenience). A designated initialiser must make sure that all properties of the class have a value before the initialiser finishes (or a super initialiser is called). Convenience initialisers only don't have this requirement because they must themselves call a designated initialiser.

class OtherSubClass: BaseClass {
convenience required init(value: Int) {
self.init() // Must call designated initialiser of this class
}

init() {
super.init(value: 0) // Must call designated initialiser of superclass
}
}

(This is fairly contrived example.)

In my experience, convenience initialisers are rarely useful and I tend to find the problems they solve can be solved using optional arguments on designated initialisers instead. One also needs to consider the fact that initialisers can't call convenience initialisers on their superclass, so make sure you don't have any convenience initialisers which provide functionality that your designated initialisers don't if you intend your class to be subclassed!


Structs and enums don't use the required or convenience keywords because these keywords are both used to indicate initialisation rules for subclasses, which only classes support: The required keyword indicates that subclasses must provide that initialiser, and the convenience keyword indicates that subclasses cannot call that initialiser. Despite not having the keywords, they must still provide initialisers defined in any protocols they conform to, and you can write 'convenient' initialisers which call self.init, just without the convenience keyword.


To respond to your statements:

  • Required initialisers don't have to be designated.
  • Designated initialisers don't have to be required.
  • Classes can have multiple required and designated initialisers.

What are convenience required initializers in Swift?

A convenience required initializer is an initializer that is enforced onto all subclasses but is not the designated initializer. This means that said initializer will eventually call a designated initializer in its initialization chain.

Designated Initialisers

A designated initialiser is the canonical initializer for a class and the one which all required and convenience initialisers should call. The Docs say:

Designated initializers are the primary initializers for a class. A
designated initializer fully initializes all properties introduced by
that class and calls an appropriate superclass initializer to continue
the initialization process up the superclass chain.

Convenience Initialisers

A convenience initialiser is an initializer that sets up certain configuration information on a class...conveniently. Documentation:

Convenience initializers are secondary, supporting initializers for a
class. You can define a convenience initializer to call a designated
initializer from the same class as the convenience initializer with
some of the designated initializer’s parameters set to default values.
You can also define a convenience initializer to create an instance of
that class for a specific use case or input value type.

You do not have to provide convenience initializers if your class does
not require them. Create convenience initializers whenever a shortcut
to a common initialization pattern will save time or make
initialization of the class clearer in intent

Required Initialisers

Required initializers can be thought of as a binding contract between a parents interface and subsequent subclasses. Its your means of enforcing that all your children are aware of and implement a certain set of initialisers.

Write the required modifier before the definition of a class
initializer to indicate that every subclass of the class must
implement that initializer:

difference between override init and required init? - swift

override init(frame: CGRect) is used when you create the view (in this case the button) programmatically.

required init?(coder: NSCoder) is used when the view is created from storyboard/xib.

since the latter is required you must implement its body. However if you are not going to create the button manually the first one is not required and can be omitted

setup is called in both because however you choose to create the button you want to setup its custom behavior so it will work as you intended it to

Swift - A little confusion between Required Initializer vs. Delegating initializer

A required initializer means that every subclass must also implement the same initializer. Since you made the init(_:) initializer required in the Dog class, every subclass (and their subclasses) of Dog must also provide the same required init(_:) initializer.

From the looks of things, you don't need to make the init(_:) initializer in the Dog class required. Remove that keyword and then you can remove the init(_:) initializer from the NewDog class.

Bonus - both init methods in your NewDog class are designated initializers since neither is a convenience initializer.

You need to learn the differences and uses of default, convenience, and required initializers. This is all clearly covered in the Intializers section of The Swift Programming Language book.

Swift Required initializers in EVReflection

It's indeed caused by the extra init

init(blah: String) {
}

That one could also be changed to:

convenience init(blah: String) {
self.init()
}

Then it won't complain about adding the required initializer.

With the convenience you specify that you will call a required initializer from there. Without that the compiler can't be sure that you do.

See also https://docs.swift.org/swift-book/LanguageGuide/Initialization.html

What is the difference between convenience init vs init in swift, explicit examples better

Standard init:

Designated initializers are the primary initializers for a class. A
designated initializer fully initializes all properties introduced by
that class and calls an appropriate superclass initializer to continue
the initialization process up the superclass chain.

convenience init:

Convenience initializers are secondary, supporting initializers for a
class. You can define a convenience initializer to call a designated
initializer from the same class as the convenience initializer with some
of the designated initializer’s parameters set to default values. You can
also define a convenience initializer to create an instance of that class
for a specific use case or input value type.

per the Swift Documentation

In a nutshell, this means that you can use a convenience initializer to make calling a designated initializer faster and more "convenient". So convenience initializers require the use of self.init instead of the super.init you might see in an override of a designated initializer.

pseudocode example:

init(param1, param2, param3, ... , paramN) {
// code
}

// can call this initializer and only enter one parameter,
// set the rest as defaults
convenience init(myParamN) {
self.init(defaultParam1, defaultParam2, defaultParam3, ... , myParamN)
}

I use these a lot when creating custom views and such that have long initializers with mainly defaults. The docs do a better job explaining than I can, check them out!

Preferred way to initialize a class in Swift

“If a property always takes the same initial value, provide a default value rather than setting a value within an initializer. The end result is the same, but the default value ties the property’s initialization more closely to its declaration. It makes for shorter, clearer initializers and enables you to infer the type of the property from its default value. The default value also makes it easier for you to take advantage of default initializers and initializer inheritance, as described later in this chapter.”

Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l

Why must constructing an object of class type 'someClass' with a metatype value use a 'required' initializer?

Consider the case where we also have a subclass:

class SomeClass {

}

class SomeSubclass : SomeClass {

}

If you store the class type in a variable:

var anotherClass = SomeClass.self

The variable anotherClass is of type SomeClass.Type.

You can later assign this variable to a subclass:

anotherClass = SomeSubclass.self

This is valid because SomeSubclass.Type is a SomeClass.Type. At this point, anotherClass() would fail if the initializer is not implemented in the subclass. This is what the compiler is protecting against.

In your sample code, this is impossible: you used let instead of var so changing the type is impossible. It may be that the safety checks just aren't nuanced enough to notice this.



Related Topics



Leave a reply



Submit