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!

You've successfully subscribed to Krzysztof Zabłocki
Great! Next, complete checkout to get full access to all premium content.
Error! Could not sign up. invalid link.
Welcome back! You've successfully signed in.
Error! Could not sign in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.