2

Here is some code from a project that, as you see it here, worked fine in Swift 2.3. Having now upgraded the project to Swift 3.0, this is producing an infinite loop at self.init().

class MyView: UIView
{
    fileprivate let foo: String

    required init? ( coder aDecoder: NSCoder )
    {
        fatalError( "init( NSCoder ) has not been implemented" )
    }

    convenience init ()
    {
        self.init()
        foo = "bar"
    }
}

I am told this should have infinite looped in Swift 2.3 too. Fair enough, I believe it, all I am able to tell you is that it didn't, but I don't know why. The possible solutions suggested in this post - Initializing swift class in objective c project causes infinite loop - are not useful because:

  1. the convenience init() method in question really is a convenience init;

  2. self.init( frame: ) produces the buildtime error message Incorrect argument label in call (have 'frame:', expected 'coder:'), and;

  3. I have no instance of NSCoder to pass into self.init( coder: )

Community
  • 1
  • 1
Joseph Beuys' Mum
  • 2,395
  • 2
  • 24
  • 50
  • Possible duplicate of [Initializing swift class in objective c project causes infinite loop](http://stackoverflow.com/questions/32084877/initializing-swift-class-in-objective-c-project-causes-infinite-loop) – alexburtnik Oct 20 '16 at 20:25
  • 4
    Not sure why it wouldn't have done it in Swift 2.3 (assuming it was identical), but self.init() is calling convenience init() – GetSwifty Oct 20 '16 at 20:34
  • hi @alexburtnik, I did search for existing answers before posting, thanks for the link. – Joseph Beuys' Mum Oct 20 '16 at 20:39

2 Answers2

3

The convenience init should always delegate the initialization to a designated initializer for the superclass, in this case the UIView. Unfortunately the only solution I found is to do this:

class MyView: UIView {

    fileprivate let foo: String = "bar"

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

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}
Luca D'Alberti
  • 4,749
  • 3
  • 25
  • 45
  • Thanks @Luca-D'Alberti, I'll give that a go. – Joseph Beuys' Mum Oct 21 '16 at 10:42
  • So good news, that does work @Luca-D'Alberti, thank you very much. Bad news: this feels horribly hacky. I mean really, instantiating variables outside an init method?! Overriding methods only in order to super() call them?!?! Really?!?!?! So be it though, writing programming languages and compilers is extremely difficult I'm sure, so if this is what we have to do, this is what we have to do. /shrugs – Joseph Beuys' Mum Oct 21 '16 at 10:48
  • In your case you're instantiating a constant without any dynamic value. That's why I put the value inside the constant declaration: it's easier to read for you and faster to write. Since I've done it, there is no need to create that `convenience init`, but I kept it there for the sake of the question – Luca D'Alberti Oct 21 '16 at 11:17
  • Thanks @Luca-D'Alberti, it is useful as I'm doing some other bits in my `convenience init` too, that I omitted from the question. Thanks again. – Joseph Beuys' Mum Oct 21 '16 at 11:20
0

This is what I have decided to do instead:

override init ( frame: CGRect )
{
    foo = "bar"
    super.init( frame: frame )
}

I am not happy with this solution as it means passing in a useless frame that I have to override later with constraints. If anyone has a better solution please do suggest it. Thank you.

UPDATE

Thanks to @Luca-D'Alberti for providing the above "correct" answer.

Joseph Beuys' Mum
  • 2,395
  • 2
  • 24
  • 50