21

I'm curious is there is anyway to call a method inside your init method that sets instance properties of the class. Essentially I just have a class that sub-classes UIView, adds some subviews in init, and some of those subviews are instance variables of the class.

class MyView: UIView {
    var collectionView: UICollectionView

    convenience init () {
        self.init(frame:CGRectZero)
    }

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

        addSubviews()
    }

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

        addSubviews()
    }

    func addSubviews (){
        self.collectionView = UICollectionView()

    }
}

Now the problem comes that I can't call super init before initializing my classes internal properties (Property 'self.collectionView' not inialized at super.init call), but I also can't call my custom method to initialize those variables prior to super.init, as it cannot use self prior to that initialization. I realize I could make the instance variable optional, but it seems less elegant, as I know it will always be initialized (and there are several more, this is just a simplified version). Is there any way to accomplish this without making all my instance variables optionals?

EDIT:

I think ultimately my question is why does swift dis-allow calling a method prior to calling super.init? What's the difference between:

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

final func addSubviews (){
    self.collectionView = UICollectionView()
}

and

override init (frame : CGRect) {
    self.collectionView = UICollectionView()
    super.init(frame : frame)
}
Kevin DiTraglia
  • 25,746
  • 19
  • 92
  • 138

1 Answers1

31

Form documentation:

Swift’s compiler performs four helpful safety-checks to make sure that two-phase initialization is completed without error:

Safety check 1 A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.

You need to set value for instance variable before you call super.init() And after this action you will access to call instance methods. In your case you can do this:

override init (frame : CGRect) {
    self.collectionView = UICollectionView()
    super.init(frame : frame)
    // Now you can call method
    self.someMethod()
}

ADD for question's EDIT:

You can't call method before super.init() call because of safety reasons. If you will do it then your method can use some properties which have not yet been initialized in the parent class

Kevin DiTraglia
  • 25,746
  • 19
  • 92
  • 138
Alexey Pichukov
  • 3,377
  • 2
  • 19
  • 22
  • 4
    Yeah the only problem I have with this, is the code would have to be duplicated between the coder init and the frame init. Pulling that into a separate method is ideal, but swift won't let you call a method before init. – Kevin DiTraglia Nov 17 '15 at 19:58
  • @KevinDiTraglia First of all you have already tried to duplicate initialization with twice calling this method. And second can't do it in swift because of two basic initialization rules: all properties must be introduced befor `super.init()` call and instance methods can be called only after `super.init()` call – Alexey Pichukov Nov 17 '15 at 20:12
  • 1
    I guess it makes sense that a method call would normally assume properties are set, but it cannot if it's being called from init. I just feel like this case is more common than the fringe case this nonsense is protecting me from where the parent initializer calls a method that is overridden, but oh well. I suppose I'll just continue to implicitly unwrap all my properties. – Kevin DiTraglia Nov 17 '15 at 20:25