Swift init()



With Swift strong typing and immutability, there are rules that prevent you from accesing variables until an object is fully initialized.

I do not like having a function do more than one thing, so I like to split my initializers into multiple functions, this becomes problematic.

Let’s say you have a class like:

class Foo {
  let coreDataStack: CoreDataStack
  let mfpService = MFPService()
  let integrator: IntegrationService
  let flowController: FlowController
  let window: UIWindow
}

This kind of class might require quite a few lines to initialize all the variables, so we’d like to split the initialization logic into functions, something like:

init(coreDataStack stack: CoreDataStack) {
  coreDataStack = stack
  integrator = setupIntegrator(stack, provider: mfpService)
  flowController = setupFlowController(stack)
  window = setupWindow(flowController.rootViewController)
}

So just a regular refactor… guess what, this won’t work:

Not cool.

I talked with few smart people on twitter and looked at some options, here are the few I thought it will be good to mention.

Let’s look at some options

Option 1 - implicitly unwraped optional var!

var integrator: IntegrationService!

This option sucks big time, the reason we have Swift is to write safer code.

Please do not write it like this.

Option 2 - lazy var’s with side-effects

We can use private lazy var’s, and have it execute side-effects

class Foo_lazy {
  let coreDataStack: CoreDataStack
  let mfpService = MFPService()
  
  init(coreDataStack stack: CoreDataStack) {
    coreDataStack = stack
    let _ = integrator
    let _ = rootFlowController
    let _ = window
  }
  
  lazy var rootFlowController: FlowController = {
    return FlowController(coreDataStack: self.coreDataStack)
  }()
  
  lazy var window: UIWindow = {
    let window = UIWindow(frame: UIScreen.mainScreen().bounds)
    window.backgroundColor = UIColor.whiteColor()
    window.rootViewController = self.rootFlowController.rootViewController
    window.makeKeyAndVisible()
    return window
  }()
  
  
  lazy var integrator: IntegrationService = {
    let coreDataProcessor = CoreDataReportProcessor(stack: self.coreDataStack)
    let integrator = IntegrationService(provider: self.mfpService) {
      coreDataProcessor.processReports($0)
    }
    return integrator
  }()
}

This solution allows us to split code and compiles fine, but it’s not so good because:

  • Variables are not really immutable, we can make it private so nothing outside class scope can modify them but still, not immutable.
  • I do not like triggering side effects like that, it feels very dirty

Option 3 - private static functions

We can define private static functions and use that to setup our variables (and we can also put them into private class extension):

class Foo_ext {
  let coreDataStack: CoreDataStack
  let mfpService = MFPService()
  let integrator: IntegrationService
  let flowController: FlowController
  let window: UIWindow
  
  init(coreDataStack stack: CoreDataStack) {
    coreDataStack = stack
    integrator = Foo_ext.SetupIntegrator(stack, provider: self.mfpService)
    flowController = Foo_ext.SetupFlowController(stack)
    window = Foo_ext.SetupWindow(self.flowController.rootViewController)
  }
}

private extension Foo_ext {
  static func SetupIntegrator(stack: CoreDataStack, provider: Provider) -> IntegrationService {
    let coreDataProcessor = CoreDataReportProcessor(stack: stack)
    let integrator = IntegrationService(provider: provider) {
      coreDataProcessor.processReports($0)
    }
    return integrator
  }
  
  static func SetupFlowController(stack: CoreDataStack) -> FlowController {
    return FlowController(coreDataStack: stack)
  }
  
  static func SetupWindow(controller: UIViewController) -> UIWindow {
    let window = UIWindow(frame: UIScreen.mainScreen().bounds)
    window.backgroundColor = UIColor.whiteColor()
    window.rootViewController = controller
    window.makeKeyAndVisible()
    return window
  }
}

This is by far my favorite option now:

  • Ensures immutable variables
  • Doesn’t need to use side-effects to trigger requested behaviour
  • clean and without any hack’s around the compiler

Conclusion

There were few other ideas that my friends on twitter suggested, you can take a look here

As I just started learning Swift, I must say I’m liking it more and more, especially since 2.0.

After 20 years of programming experience, it’s fun to feel like a rookie again, fortunately we have a great community that’s willing to share their knowledge.

Big shoutout to @jessyMeow and @KostiaKoval as they pointed out some better ways to deal with this than I originaly thought :)

If you have other solutions you like, please do let me know on Twitter as I’d love to learn about it!