0

I want to setup a variable using let so that its not optional, won't change over time and doesn't require messy unwrapping to access each time. I know I can legally set it, but only inside one of the init methods. The problem is that Swift requires you to override the init(coder aDecoder: NSCoder) initializer. However I've never had an app that ever has that initializer called. Its always the init(frame: CGRect) initializer that is called. So if I didn't have to override init(coder aDecoder: NSCoder) then I'd just do the self.imageView = UIImageView.new() in init(frame: CGRect) and I'd be done, but the compiler complains that its not set in the other init as well. I tried making a sharedInit() method that is called from both inits, but the compiler won't let me set the imageView from there since it's read only outside of the init methods. How are you supposed to accomplish this without doing all of your init twice directly in the init methods?

class SuperCoolView : UIView {

    let imageView: UIImageView

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

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

    func sharedInit() {
        self.imageView = UIImageView.new()
        // Other similar shared init
    }

}
Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
jamone
  • 17,253
  • 17
  • 63
  • 98
  • Swift 1.2 kinda fixes this issue (you can set the let variable anywhere but of course only once..). – Ron Mar 18 '15 at 00:06
  • I'm doing this in Xcode 6.3b3 so I'm using Swift 1.2, and I'm getting errors. – jamone Mar 18 '15 at 00:09
  • Possible duplicate of [How to implement two inits with same content without code duplication in Swift?](http://stackoverflow.com/q/24023412/1445366). See [this answer in particular](http://stackoverflow.com/a/24051352/1445366) - you can combine closure-based property initialization with a `commonInit()` function. – Aaron Brager Mar 18 '15 at 01:09

2 Answers2

3

I found this answer and liked it's approach. It fixed the problem, and is much cleaner IMHO. By implementing init(coder aDecoder: NSCoder) normally we would be keeping up the appearance that this view can be initialized from a nib/storyboard when it can't.

class SuperCoolView : UIView {

let imageView: UIImageView

required init(coder aDecoder: NSCoder) {
    fatalError("NSCoding not supported")
}

override init(frame: CGRect) {
    self.imageView = UIImageView.new()
    // Other stuff
    super.init(frame: frame)
}

}

Community
  • 1
  • 1
jamone
  • 17,253
  • 17
  • 63
  • 98
2

Just use a combination of lazy initialization and private 'set' to get:

class SuperCoolView : UIView {
  public private(set) lazy var imageView: UIImageView = {
    // In practice, do more here.  Access state in this instance.
    // Or, include more computation.
    UIImageView.new()
  }()

  // inits w/o setting imageView
}

The 'private set' makes this externally identical to a let; the lazy lets you initialize the value based on the other state in the SuperCoolView instance.

Note that if the initialization of imageView, and others, doesn't depend on state in the instance, then you can avoid the lazy and just do the initialization from the closure.

GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • Why not just use `let imageView = UIImageView()`? Or the closure-based `let` initialization in the answer I linked to? – Aaron Brager Mar 18 '15 at 03:16
  • I updated. Our updates crossed. Yes, if the assignment doesn't depend on complex computation or state in `SuperCoolView`, then you can just assign it. – GoZoner Mar 18 '15 at 03:20
  • I'm not a fan of using closures on property declarations. This makes reading the code later much more complex. I prefer that the top of the swift file just be the public methods/properties with as little implementation details as possible. I really miss header files for this purpose. – jamone Mar 18 '15 at 13:16
  • 1
    So you prefer 'named closures' (like `sharedInit`) over 'anonymous closures'? Your opinion will change grasshopper! :-) – GoZoner Mar 18 '15 at 13:51
  • No, I just prefer to place something like `sharedInit` down "hidden" in the implementation details of the class, not tied to the public property declaration. – jamone Mar 18 '15 at 13:57