What's the best way to reproduce the behavior of a superclass's convenience initializer?

I recently attempted to create a subclass of NSTextField that would also let me use the convenience initializer init(labelWithString:). However, because of the rules for initializer delegation for class types, I can't call init(labelWithString:) from any of my subclass's designated or convenience initializers. If I had access to the source code I could simply reuse it in my subclass's implementation, but because I don't I have to reverse engineer its behavior instead. Is this really the only way to reproduce the behavior of a superclass's designated initializer, or am I missing something?

(I'm aware that one possible workaround is automatic initializer inheritance—via Rule 2—but I can't implement all of NSTextField's designated initializers because my subclass has a stored property that wouldn't be set by them unless I made it an optional value.)

3 Likes

It's not safe to call init(labelWithString:) if you haven't overridden all the designated initializers, because you don't know which designated initializer it's going to call. That's why the rules for convenience initializer inheritance are there in the first place.

If you really need it, you can override the remaining designated initializers with bodies that just call fatalError(), so that if they turn out to be implemented in terms of something you can't support, you'll at least know about it.

2 Likes

Thanks for your reply, @jrose.

I had suspected that the issue had to do with not knowing which designated initializer the superclass's convenience initializer was going to call (given that convenience initializers call a designated initializer on the dynamic type), so it's good to have confirmation of that. Still, it seems a bit odd to me that simply by introducing any stored property that must be given as a parameter to my subclass's designated initializer—and which by definition would not be present in the signatures of any of the superclass's designated initializers—I suddenly lose the ability to use all of a superclass's convenience initializers. Instead, I am presented with the following two choices:

  1. Make my new stored property an optional value (Ă  la implicitly unwrapped outlets) and use automatic initializer inheritance, or
  2. Reverse engineer the behavior of the superclass's convenience initializer

It's a shame that there doesn't seem to be any other way around this. Forcing programmers to "reinvent the wheel", as it were, when initializing subclasses just doesn't feel very Swifty to me.

Swift is a Safe, Fast, and Expressive language. I'm not sure I 100% agree with the ordering of "Fast" and "Expressive" there, but "Safe" is paramount, and it is not possible for the compiler to guarantee safety in this case.

You're welcome to propose a language feature to say "I know what I'm doing, let me call this" (at the call site) or "I know what I'm doing, make the convenience initializers available" (at the class declaration site). Both of those would be valid languages changes. But I think the default behavior is the correct default, and you do have a way to say what you mean here, albeit a wordy one.

1 Like

Cool, I'll take a look at the Swift Evolution Process and see whether I can come up with a good proposal to work around this limitation. :+1:

Before I do, though, could I ask whether you are aware of any prior proposals to add a similar feature to the language? It would be helpful to know what has been suggested in the past and why that was ultimately rejected—or simply postponed, as the case may be.

I'm not sure I follow…is "a way to say what you mean here, albeit a wordy one" referring to the Swift Evolution Process?

1 Like

I don't think so! Which is both good and bad: good because it means it hasn't already been shot down or found to be flawed, but bad because it means others might not feel as strongly as you.

Heh, no, that was me talking about the present day workaround of overriding all the other designated initializers with fatalError("cannot be implemented for this class").