There are two absolutely **crucial** pieces of Swift-specific information that are missing from the existing answers which I think help clear this up completely.
1. If a protocol specifies an initializer as a required method, that initializer must be marked using Swift's `required` keyword.
2. Swift has a special set of inheritance rules regarding `init` methods.
The **tl;dr** is this:
> If you implement any initializers, you are no longer inheriting any of the superclass's designated initializers.
<sub>The only initializers, if any, that you will inherit, are super class convenience initializers that point to a designated initializer which you happened to override.</sub>
So... ready for the long version?
----------
### Swift has a special set of inheritance rules regarding `init` methods.
I know this was the second of two points I made, but we can't understand the first point, or why the `required` keyword even exists until we understand this point. Once we understand this point, the other one becomes pretty obvious.
<sub>All of the information I cover in this section of this answer is from Apple's documentation found [here][1].</sub>
From the Apple docs:
> **Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default.** Swift’s approach prevents a situation in which a simple initializer from a superclass is inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized.
<sub>Emphasis mine.</sub>
So, straight from the Apple docs right there, we see that Swift subclasses will not always (and usually don't) inherit their superclass's `init` methods.
**So, when do they inherit from their superclass?**
There are two rules that define when a subclass inherits `init` methods from its parent. From the Apple docs:
> **Rule 1**
>
> If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
>
> **Rule 2**
>
> If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.
Rule 2 isn't particularly relevant to this conversation because `SKSpriteNode`'s `init(coder: NSCoder)` is unlikely to be a convenience method.
So, your `InfoBar` class was inheriting the `required` initializer right up until the point that you added `init(team: Team, size: CGSize)`.
If you were to have not provided this `init` method and instead made your `InfoBar`'s added properties optional or provided them with default values, then you'd have still been inheriting `SKSpriteNode`'s `init(coder: NSCoder)`. However, when we added our own custom initializer, we stopped inheriting our superclass's designated initializers (and [convenience initializers][2] which didn't point to initializers we implemented).
So, as a simplistic example, I present this:
class Foo {
var foo: String
init(foo: String) {
self.foo = foo
}
}
class Bar: Foo {
var bar: String
init(foo: String, bar: String) {
self.bar = bar
super.init(foo: foo)
}
}
let x = Bar(foo: "Foo")
Which presents the following error:
> Missing argument for parameter 'bar' in call.
[![enter image description here][3]][3]
If this were Objective-C, it'd have no problem inheriting. If we initialized a `Bar` with `initWithFoo:` in Objective-C, the `self.bar` property would simply be `nil`. It's probably not great, but it's a perfectly *valid* state for the object to be in. It's **not** a perfectly valid state for the Swift object to be in. `self.bar` is not an optional and cannot be `nil`.
Again, the only way we inherit initializers is by not providing our own. So if we try to inherit by deleting `Bar`'s `init(foo: String, bar: String)`, as such:
class Bar: Foo {
var bar: String
}
Now we're back to inheriting (sort of), but this won't compile... and the error message explains exactly why we don't inherit superclass `init` methods:
> **Issue:** Class 'Bar' has no initializers
>
> **Fix-It:** Stored property 'bar' without initializers prevents synthesized initializers
If we've added stored properties in our subclass, there's no possible Swift way to create a valid instance of our subclass with the superclass initializers which couldn't possibly know about our subclass's stored properties.
----------
### Okay, well, why do I have to implement `init(coder: NSCoder)` at all? Why is it `required`?
Swift's `init` methods may play by a special set of inheritance rules, but protocol conformance is still inherited down the chain. If a parent class conforms to a protocol, its subclasses must to conform to that protocol.
Ordinarily, this isn't a problem, because most protocols only require methods which don't play by special inheritance rules in Swift, so if you're inheriting from a class that conforms to a protocol, you're also inheriting all of the methods or properties that allow the class to satisfy protocol conformance.
However, remember, Swift's `init` methods play by a special set of rules and aren't always inherited. Because of this, a class that conforms to a protocol which requires special `init` methods (such as `NSCoding`) requires that the class mark those `init` methods as `required`.
Consider this example:
protocol InitProtocol {
init(foo: Int)
}
class ConformingClass: InitProtocol {
var foo: Int
init(foo: Int) {
self.foo = foo
}
}
This doesn't compile. It generates the following warning:
> **Issue:** Initializer requirement 'init(foo:)' can only be satisfied by a 'required' initializer in non-final class 'ConformingClass'
>
> **Fix-It:** Insert required
It wants me to make the `init(foo: Int)` initializer required. I could also make it happy by making the class `final` (meaning the class can't be inherited from).
So, what happens if I subclass? From this point, if I subclass, I'm fine. If I add any initializers though, I am suddenly no longer inheriting `init(foo:)`. This is problematic because now I'm no longer conforming to the `InitProtocol`. I can't subclass from a class that conforms to a protocol and then suddenly decide I no longer want to conform to that protocol. I've inherited protocol conformance, but because of the way Swift works with `init` method inheritance, I've not inherited part of what's required to conform to that protocol and I must implement it.
----------
### Okay, this all makes sense. But why can't I get a more helpful error message?
Arguably, the error message might be more clear or better if it specified that your class was no longer conforming to the inherited `NSCoding` protocol and that to fix it you need to implement `init(coder: NSCoder)`. Sure.
But Xcode simply can't generate that message because that actually won't always be the actual problem with not implementing or inheriting a required method. There is at least one other reason to make `init` methods `required` besides protocol conformance, and that's factory methods.
If I want to write a proper factory method, I need to specify the return type to be `Self` (Swift's equivalent of Objective-C's `instanceType`). But in order to do this, I actually need to use a `required` initializer method.
class Box {
var size: CGSize
init(size: CGSize) {
self.size = size
}
class func factory() -> Self {
return self.init(size: CGSizeZero)
}
}
This generates the error:
> Constructing an object of class type 'Self' with a metatype value must use a 'required' initializer
[![enter image description here][4]][4]
It's basically the same problem. If we subclass `Box`, our subclasses will inherit the class method `factory`. So we could call `SubclassedBox.factory()`. However, without the `required` keyword on the `init(size:)` method, `Box`'s subclasses are not guaranteed to inherit the `self.init(size:)` that `factory` is calling.
So we must make that method `required` if we want a factory method like this, and that means if our class implements a method like this, we'll have a `required` initializer method and we'll run into the exact same problems you've run into here with the `NSCoding` protocol.
----------
Ultimately, it all boils down to the basic understanding that Swift's initializers play by a slightly different set of inheritance rules which means you're not guaranteed to inherit initializers from your superclass. This happens because superclass initializers can't know about your new stored properties and they couldn't instantiate your object into a valid state. But, for various reasons, a superclass might mark an initializer as `required`. When it does, we can either employ one of the very specific scenarios by which we actually do inherit the `required` method, or we must implement it ourselves.
The main point here though is that if we're getting the error you see here, it means that your class isn't actually implementing the method at all.
As perhaps one final example to drill in the fact that Swift subclasses don't always inherit their parent's `init` methods (which I think is absolutely central to fully understanding this problem), consider this example:
class Foo {
init(a: Int, b: Int, c: Int) {
// do nothing
}
}
class Bar: Foo {
init(string: String) {
super.init(a: 0, b: 1, c: 2)
// do more nothing
}
}
let f = Foo(a: 0, b: 1, c: 2)
let b = Bar(a: 0, b: 1, c: 2)
This fails to compile.
[![enter image description here][5]][5]
The error message it gives is a little misleading:
> Extra argument 'b' in call
But the point is, `Bar` doesn't inherit any of `Foo`'s `init` methods because it hasn't satisfied either of the two special cases for inheriting `init` methods from its parent class.
If this were Objective-C, we'd inherit that `init` with no problem, because Objective-C is perfectly happy not initializing objects' properties (though as a developer, you shouldn't have been happy with this). In Swift, this simply will not do. You can't have an invalid state, and inheriting superclass initializers can only lead to invalid object states.
[1]:
[To see links please register here]
[2]:
[To see links please register here]
[3]:
[4]:
[5]: