Misha Stone
Misha Stone

Reputation: 641

Initializing a Subclassed UIViewController with a Required Coder Init - What to Do?

This is probably the silliest question to date - but I am having problems with initializing a subclassed view controller with a customized required coder initializer (specifically using the QRCoder pod; and to not expose code I don't own, I'll be using example classes in my case).

Here are the essentials.

class A: UIViewController {

    public var name = String()

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        name = "This is a test"
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }
}

Then we have...

class B: A {


    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        name = "What what"
    }
}

If I attempt to generate a new view controller of B in say, a button tap on C...

class C: UIViewController  {

    let button = UIButton()

    override func viewDidLoad() {
        super.viewDidLoad()

        button(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)}

    } 

    @objc func buttonTapped(_ sender: Any) {
        let viewOfB = B()
        present(viewOfB, animated: true)
    }
}

It doesn't compile = because my call of let viewOfB = B() is missing the coder parameter.

The problem is, if I add a coder parameter, what in the world do I put in there? I've tried filling it with just an empty(?) NSCoder, like so

let emptyCoder = NSCoder()
let viewOfB = B(coder: emptyCoder)

But then upon a run and button tap, I get the following error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -decodeObjectForKey: cannot be sent to an abstract object of class NSCoder: Create a concrete instance!'

I've tried adding a convenience initializer to A (where I have to run a self.init instead of super.init for some reason), but doing that gives me an EXC_BAD_ACCESS_ERROR.

What exactly do I need to provide to an init:(coder) instance to be able to... get things going?

Upvotes: 0

Views: 566

Answers (1)

matt
matt

Reputation: 535304

What exactly do I need to provide to an init:(coder) instance to be able to... get things going?

Nothing. Stop thinking about init(coder:).

Here's the actual problem. When you say B(), you are calling init(). But there is no init(), because where would it come from? You didn't implement any such method in A. And it is not inherited from the superclass (UIViewController), because you effectively destroyed all inherited initializers when you implemented init(coder:).

So if you want to say B(), you must implement init() explicitly in A, yourself, like this:

class A: UIViewController {

    public var name = String()

    init() {
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        name = "This is a test"
    }
}

Upvotes: 1

Related Questions