Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling instance method during initialization in Swift

I am new to Swift and would like to initialize an object's member variable using an instance method like this:

class MyClass {
  var x: String
  var y: String

  func createY() -> String {
    self.y = self.x + "_test" // this computation could be much more complex
  }

  init(x: String) {
    self.x = x
    self.y = self.createY()
  }     
}

Basically, instead of inlining all the initialization code in init method, I want to extract the initialization code of y to a dedicated method createY and call this instance method createY in init. However, Swift compiler (Swift 1.2 compiler in Xcode 6.3 beta) complains:

use of 'self' in method call 'xxx' before super.init initialize self

Here 'xxx' is the name of the instance method (createY).

I can understand what Swift compiler is complaining and the potential problem it wants to address. However, I have no idea how to fix it. What should be the correct way in Swift to call other instance method of initialization code in init?

Currently, I use the following trick as work around but I don't think this is the idiomatic solution to this problem (and this workaround requires y to be declared using var instead of let which makes me feel uneasy too):

init(x: String) {
  self.x = x
  super.init()
  self.y = createY()
} 

Any comment is appreciated. Thanks.

like image 476
nybon Avatar asked Feb 21 '15 14:02

nybon


2 Answers

Convert createY() to a global or class function that accepts x as an argument and returns a y.

func createY(x: String) -> String {
    return x + "_test" // this computation could be much more complex
}

Then just call it normally from your init.

class MyClass {
  let x: String
  let y: String

  init(x: String) {
    self.x = x
    self.y = createY(x)
  }     
}
like image 146
John Estropia Avatar answered Oct 02 '22 13:10

John Estropia


In Swift 3, I've been using this pattern,

class MyClass {
  var x: String?
  private(set) lazy var y: String? = self.createY()

  init(x: String){ self.x = x }

  private func createY() -> String?
  {
    return "\(x ?? "nil") test"
  }
}

The secret sauce here is the use of private(set) lazy. This way, you can label your property a var. And lazy will delay initialization until after your init function has completed. Using private(set) only allows the functions inside this class to modify that property, including the lazy keyword, but not let public interfaces change it. Of course, if you want your interface to change your property, then you can also mark it internal (the default) or public. But you need to leave it marked a lazy var

like image 42
casademora Avatar answered Oct 02 '22 14:10

casademora