TIL by Ed

Writing up things I’ve learnt.

Swift Initializer Confusion

So this all came around when subclassing UIView. Something like below

1
2
3
4
5
6
7
class CustomView: UIView {

    override init () {
        super.init();
        self.backgroundColor = UIColor.redColor()
    }
}

and creating an instance like below

1
let customView = CustomView()

But xcode hits me with the error

‘required’: initializer ‘init(coder:)’ must be provided by subclass of ‘UIView’

After a bit of reading, it seems that effectively what I have done is override a designated initializer in UIView. You see, unlike Objective-C, Swift subclasses do not inherit their superclass initializers by default. Actually, if we read the documentation around Automatic Initializer Inheritance, we can understand why that compiler error is coming up. I’ve just quoted the two rules that apply here straight from the documentation

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.

So here it seems I broke Rule 1. I haven’t inherited the init(coder:) initializer because I have defined my own designated initializer by overriding init. And since UIView conforms to NSCoding, the init(coder:) initializer is required.

Ok, so this kinda sucks. I’ll implement init(coder:) and just call super to satisfy the complier

1
2
3
4
5
6
7
8
9
10
11
class CustomView: UIView {

    override init () {
        super.init();
        self.backgroundColor = UIColor.redColor()
    }

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

Oh crap. When I run this, I now get

fatal error: use of unimplemented initializer ‘init(frame:)’

Hmmm. So it looks like the implementation of UIView’s initializer init() calls out to the init(frame:) (which I did not inherit from UIView since I broke Rule 1). To fix this, I must implement init(frame:).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CustomView: UIView {

    override init () {
        super.init();
        self.backgroundColor = UIColor.redColor()
    }

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

    override init(frame: CGRect) {
        super.init(frame: frame)
    }
}

Since I always want the CustomView always to be red, then I should move that code to the other designated initializers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class CustomView: UIView {

    override init () {
        super.init();
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.backgroundColor = UIColor.redColor()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.redColor()
    }
}

Now at this point you would think I could go ahead and delete my implementation of init(), but in fact init() is a designated initializer inherited from NSObject. Despite the fact that UIView’s implementation of init() calls init(frame:), it is not a considered a convenience initializer. You can try this by deleting init(), and you should get a compile error when trying to do let customView = CustomView(). If it was a convenience initializer, it would have been inherited after we implemented both init(frame:) and init(coder:). Since UIView is implemented in Objective-C before Swift and these rules came along, it can break these rules of a designated initializer calling another designated initializer.

Finally

So basically when creating a simple subclass of UIView, it seems that if I wanted to do some setup at initialization I have to implement/override the following

  • init()
  • init(frame:)
  • init(coder:)

just so that I can do

1
let customView = CustomView()

These rules are to ensure safety around initialization, but it sure can be confusing at first why you have to jump through those hoops just to get something simple done.

Comments