When creating a custom NSView subclass that does not or cannot take advantage of initializer inheritance (possibly because it has some non-optional stored properties), we also need to provide our own implementation of init(coder:)
. Are there any caveats we need to be aware of if our subclass contains its own subviews?
To start, it seems that we need to provide our own implementation of encode(with:)
. For example:
override func encode(with aCoder: NSCoder) {
super.encode(with: aCoder)
aCoder.encode(subView, forKey: "subview")
}
Should the superclass's implementation of this method be called before or after the subclass's, or does the order not matter?
I found out the hard way that the order does matter in init(coder:)
. For example, this will lead to a runtime crash when subview
is an instance of NSTextField
:
required init?(coder decoder: NSCoder) {
guard let subview = decoder.decodeObject(forKey: "subview") else {
return nil
}
self.subview = subview
super.init(coder: decoder)
}
Apparently we need to decode the superclass before any of its subviews:
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
guard let subview = decoder.decodeObject(forKey: "subview") else {
return nil
}
self.subview = subview
}
One consequence of this appears to be that we must declare our subview properties as optional values because any non-optional values would have to be set before the superclass's initializer is called, but the subviews can't be set until after the superclass has been decoded.
Does this sound about right, or is there anything else that I've missed?
(The intricacies of initialization and optional values made this feel like a relevant question to ask here, but my apologies if it is more related to Cocoa than it is to Swift…)