dzjustinli3 dzjustinli3 - 12 days ago 5
Swift Question

Struggling to Understand Swift 3 Overriding of Failable Initialisers with Nonfailable Initialisers

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!

Answer

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:

1. Force unwrap the failable initialiser:

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

 2. Making the subclass initialiser failable as well:

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.

Comments