I'm trying to learn the concept of overriding a failable initialiser in swift and have encountered the following statements:
the only way to delegate up to the superclass initializer is to force-unwrap the result of the failable superclass initializer.
The textbook did not provide any code to really explain what it really means? Could someone please kindly explain it to me? Even better if it comes with an code example!
I have to admit, the section "Overriding a Failable Initialiser" is quite confusing.
The following example should clarify this scenario:
Suppose, you have a base class with a failable initialiser:
class Base {
let name: String
init?(name: String) {
guard !name.isEmpty else {
return nil
}
self.name = name
}
}
Note that a failing initialiser returns an Optional.
Here, the initialiser requires that you pass a non-empty string, otherwise the initialiser "fails" - that is, it returns an optional whose value is nil
(respectively .None
).
Now, let's define a class that derives from Base
. In the first version, this will not compile, tough!
class Derived: Base {
let age: Int
init(name: String, age: Int) {
self.age = age
super.init(name: name) //<- error
}
}
The compiler issues the following error:
error: a non-failable initializer cannot chain to failable initializer 'init(name:)' written with 'init?'
super.init(name: name)
^
The problem here is, that the non-failing initialiser of the subclass delegates to the failing base class initialiser.
We have two options to correct the issue:
class Derived: Base {
let age: Int
init(name: String, age: Int) {
self.age = age
super.init(name: name)! // <- force unwrap
}
}
The caveat with this solution is, that if you pass an empty name
to the subclass initialiser, e.g.
let derived = Derived(name: "", age: 12)
it results in a fatal error when trying the force unwrap the optional from the base class initialiser:
fatal error: unexpectedly found nil while unwrapping an Optional value
class Derived: Base {
let age: Int
init?(name: String, age: Int) { // use failable initialiser
self.age = age
super.init(name: name) // <- propagate the failure with init?
}
}
This solution simply propagates the nil
result from the base class initialiser to the caller - which makes the caller responsible to handle the optional appropriately.