Group Group Group Group Group Group Group Group Group

raywenderlich.com Forums

Swift Tutorial: Initialization In Depth, Part 2/2

Launch your Swift skills to the next level as you continue your study of initialization to cover class initialization, subclasses, and convenience initializers.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/1219-swift-tutorial-initialization-in-depth-part-2-2
1 Like

This was a great series! Wondering if you had any thoughts about lazy initialization. It’s a style that is being considered to an upcoming update of the Ray Wenderlich Style Guide. https://github.com/raywenderlich/swift-style-guide/issues/88 Would love to get your expert opinion.

Hi René,

First, congratulations for a great article. My knowledge about initializers has improved atronomically.

Nevertheless, I have a question that is bothering me for a long time. Working with SpriteKit, I wanted to create my own Scene class, derived from SKScene. My DerivedScene has a property I want to initialize inside a convenience initializer that takes an .sks filename and the property value. So I wrote this:

class DerivedScene: SKScene {
    var localData: String
    
    override init(size: CGSize) {
        localData = ""
        super.init(size: size)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    convenience init?(fileNamed: String, localData: String) {
        self.init(fileNamed: fileNamed)  // Compiler error!
        self.localData = localData
    }
}

No matter what I try, I have found no way to use this convenience initializer init?(fileNamed:). Seems to me that, as this initializer is from SKNode, and that SKScene does not implement it, I will never be able to use it.

What would be the correct approach for this?

Thanks in advance,

Enrique.

Thanks Ray! I do, I use the closure initialization pattern all the time, I’m a big fan. I like it because it removes a lot of code from the initializer that is typically not interesting like setting properties and whatnot and places the initialization code at the declaration site. Overall this pattern makes code a lot easier to read. I think lazy makes sense when the initialization is expensive and might not be necessary. It sometimes feels like premature optimization to be honest though. My two cents.

Yeah, this is exactly where things get unfortunately unclear. Try this bit of code:

class DerivedScene: SKScene {
  var localData: String = ""

  override init(size: CGSize) {
    localData = ""
    super.init(size: size)
  }

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

  convenience init?(fileNamed: String) {
    self.init(fileNamed: fileNamed)
  }

  convenience init?(fileNamed: String, localData: String) {
    self.init(fileNamed: fileNamed)
    self.localData = localData
  }
}

It looks like the compiler cannot see the convenience initializer unless we re-declare it in our Swift class. The code that you posted should work according to all the rules. This is probably a bridging issue between Objective-C and Swift. Because Objective-C does not play by the same initialization rules, sometimes I’ve seen some bizarre behavior like in this case.

Hi René,
I like this 2 Part tutorial. I hope to see more like this.
I think Apple has change a bit on the swift initialization. On my iMac with Xcode Version 7.3 (7D175) the “failing designated initialization” of the following form works fine.

// Init #1c - Designated
init?(identifier: String, reusable: Bool) {
let identifierComponents = identifier.componentsSeparatedByString("-")
guard identifierComponents.count == 2 else {
return nil
}
self.reusable = reusable
self.model = identifierComponents[0]
self.serialNumber = identifierComponents[1]
}
//

Thanks Marcel! I’ve updated the content to note that this is only an issue with Swift 2.1 and earlier. Cheers.

1 Like

That is really useful tutorial.
Just one thing, it seems to me that one of the example initializers is not what it should be, more specifically this one:

// Init #3e - Convenience
convenience override init(model: String, serialNumber: String, reusable: Bool,
    encasingMaterial: String) {
  self.init(model: model, serialNumber: serialNumber,
    reusable: reusable, encasingMaterial: "Aluminum")
}

No reason to set default value of encasingMaterial, since we have it as parameter and liquidType is not initialized. It’s really interesting that the compiler doesn’t complain. If you try to instantiate an object with that particular initializer though…
I think you meant it to be like this:

// Init #3e - Convenience
    convenience override init(model: String, serialNumber: String, reusable: Bool, encasingMaterial: String) {
        self.init(model: model, serialNumber: serialNumber, reusable: reusable, encasingMaterial: encasingMaterial, liquidType: "LOX")
    }
1 Like

That’s what puzzle me too.I think you are right. tihi:smirk:

Yes I think this is an error. What happens when you call this is an infinite loop of the Init #3e calling itself. you can test it by adding a print (“Init #3E”) in the initializer then create an instance using that initializer

Thanks, this was helpful. I’m new to Swift, having some trouble adjusting to some of the new concepts. What if I have an object with a function for resetting state, like this:

class BeanCounter {
    var pintoBeansCount: Int
    var blackBeansCount: Int

    init() { pintoBeansCount = 0; blackBeansCount = 0 }
    func reset() -> Void { pintoBeansCount = 0; blackBeansCount = 0 }
}

How do people implement state-reset functions without duplicating the code in their initializers? Or is it considered un-Swifty to have an object that allows itself to be reset?

“There is one downside to overriding superclass designated initializers with subclass designated initializers. If the subclass designated initializer has logic, you can’t delegate to it from a convenience initializer.”

I don’t quite follow that part. In a playground, I made a class A with a designated initializer, then I made a class B with a designated initializer overriding the designated initializer in A. B’s designated initializer has if-else logic**. But my convenience initializer in B managed to delegate to B’s designated initializer just fine.

My code below:
var str = “Hello, playground”

class A {
let a: Int

// designated
init(a: Int) {
self.a = a
}
}

class B : A {
let b: Int

// designated
override init(a: Int) {
if (a > 3) {
b = 5
}
else {
b = 4
}
super.init(a: a)
}

convenience init() {
    self.init(a: 3)
}

}

let myB: B = B()
myB.a // 3
myB.b // 4

This tutorial is more than six months old, so questions regarding it are no longer supported. We will update it as soon as possible. Thank you! :]