Skip to content

Instantly share code, notes, and snippets.

@krzysztofzablocki
Created November 17, 2015 14:14
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save krzysztofzablocki/6a1b6afab974f442f5ff to your computer and use it in GitHub Desktop.
Save krzysztofzablocki/6a1b6afab974f442f5ff to your computer and use it in GitHub Desktop.
class ApplicationController {
let coreDataStack: CoreDataStack
let mfpService = MFPService(username: x, password: y)
init(coreDataStack stack: CoreDataStack) {
coreDataStack = stack
let _ = integrator
let _ = rootFlowController
let _ = window
}
lazy var rootFlowController: FlowController = {
return SessionFlowController(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)
}
integrator.fetchEvery(interval: 5.hours)
integrator.fetch()
return integrator
}()
}
@kostiakoval
Copy link

The similar solution with using private class methods.
The only difference here is that you split declaration with functionality and pass parameters to functions explicitly, instead of accessing self.

It has a advantage that you don't access instance state in the setup functions, and they can be easily tested

struct X {
  let w: UIWindow
  let vc: UIViewController
}

class Foo3 {
  let controller: UIViewController
  let window: UIWindow
  let x: X

  init() {
    controller = Foo3.setupController()
    window = Foo3.setupWindow(controller)
    x = Foo3.setupX(window, vc: controller)
  }
}

private extension Foo3 {
  static func setupController() -> UIViewController {
    return UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()!
  }

  static func setupWindow(controller: UIViewController) -> UIWindow {
    let window = UIWindow(frame: UIScreen.mainScreen().bounds)
    window.backgroundColor = UIColor.whiteColor()
    window.makeKeyAndVisible()
    return window
  }

  static func setupX(w: UIWindow, vc: UIViewController) -> X {
    return X(w: w, vc: vc)
  }
}

@JessyCatterwaul
Copy link

class ApplicationController {
    let mfpService = MFPService(username: x, password: y)

    init(coreDataStack: CoreDataStack) {
        self.coreDataStack = coreDataStack
        typealias `Self` = ApplicationController
        rootFlowController = Self.RootFlowController(coreDataStack: coreDataStack)
        window = Self.Window(rootViewController: rootFlowController.rootViewController)
        integrator = Self.Integrator(coreDataStack: coreDataStack, mfpService: mfpService)
    }

    let coreDataStack: CoreDataStack

    let rootFlowController: FlowController
    private static func RootFlowController(coreDataStack coreDataStack: CoreDataStack)
    -> SessionFlowController {
        return SessionFlowController(coreDataStack: coreDataStack)
    }

    let window: UIWindow
    private static func Window(rootViewController rootViewController: UIViewController)
    -> UIWindow {
        let window = UIWindow(frame: UIScreen.mainScreen().bounds)
        window.backgroundColor = UIColor.whiteColor()
        window.rootViewController = rootViewController
        window.makeKeyAndVisible()
        return window
    }

    let integrator: IntegrationService
    private static func Integrator(
        coreDataStack coreDataStack: CoreDataStack,
        mfpService: MFPService
    ) -> IntegrationService {
        let coreDataProcessor = CoreDataReportProcessor(stack: coreDataStack)
        let integrator = IntegrationService(provider: mfpService) {
            coreDataProcessor.processReports($0)
        }
        integrator.fetchEvery(interval: 5.hours)
        integrator.fetch()
        return integrator
    }
}

@JessyCatterwaul
Copy link

Private initializers are another alternative.

class ApplicationController {
    let
        mfpService = MFPService(username: "x", password: "y"),

        coreDataStack: CoreDataStack,
        integrator: IntegrationService,
        rootFlowController: FlowController,
        window: UIWindow

    init(coreDataStack: CoreDataStack) {
        self.coreDataStack = coreDataStack
        integrator = IntegrationService(coreDataStack: coreDataStack, mfpService: mfpService)
        rootFlowController = SessionFlowController(coreDataStack: coreDataStack)
        window = UIWindow(rootViewController: rootFlowController.rootViewController)
    }
}

private extension IntegrationService {
    /// integrator
    convenience init(coreDataStack: CoreDataStack, mfpService: MFPService) {
        let coreDataProcessor = CoreDataReportProcessor(stack: coreDataStack)
        self.init(provider: mfpService) {
            coreDataProcessor.processReports($0)
        }
        fetchEvery(interval: 5.hours)
        fetch()
    }
}

private extension UIWindow {
    /// window
    convenience init(rootViewController: UIViewController) {
        self.init(frame: UIScreen.mainScreen().bounds)
        backgroundColor = UIColor.whiteColor()
        self.rootViewController = rootViewController
        makeKeyAndVisible()
    }
}

@JessyCatterwaul
Copy link

After considering this all today; I think that private (file-scope) functions in PascalCase might be the most self-documenting and Swift-ish way to do it. The private static functions require a little more code, and the private initializers could cause name conflicts. Thanks for giving me the idea to reassess.

    init(coreDataStack: CoreDataStack) {
        self.coreDataStack = coreDataStack
        rootFlowController = SessionFlowController(coreDataStack: coreDataStack)
        window = Window(rootViewController: rootFlowController.rootViewController)
        integrator = Integrator(coreDataStack: coreDataStack, mfpService: mfpService)
    }
}

private func Integrator(
    coreDataStack coreDataStack: CoreDataStack,
    mfpService: MFPService
) -> IntegrationService {
    let coreDataProcessor = CoreDataReportProcessor(stack: coreDataStack)
    let integrator = IntegrationService(provider: mfpService) {
        coreDataProcessor.processReports($0)
    }
    integrator.fetchEvery(interval: 5.hours)
    integrator.fetch()
    return integrator
}

private func Window(rootViewController rootViewController: UIViewController) -> UIWindow {
    let window = UIWindow(frame: UIScreen.mainScreen().bounds)
    window.backgroundColor = UIColor.whiteColor()
    window.rootViewController = rootViewController
    window.makeKeyAndVisible()
    return window
}

@JessyCatterwaul
Copy link

I can't stop. :*(

init(coreDataStack: CoreDataStack) {
    // This can't be a func; a func can't capture inputs without parameters.
    let Integrator: () -> IntegrationService = {[mfpService] in
        let coreDataProcessor = CoreDataReportProcessor(stack: coreDataStack)
        let integrator = IntegrationService(provider: mfpService) {
            coreDataProcessor.processReports($0)
        }
        integrator.fetchEvery(interval: 5.hours)
        integrator.fetch()
        return integrator
    }

    func Window(rootViewController rootViewController: UIViewController) -> UIWindow {
        let window = UIWindow(frame: UIScreen.mainScreen().bounds)
        window.backgroundColor = UIColor.whiteColor()
        window.rootViewController = rootViewController
        window.makeKeyAndVisible()
        return window
    }

    self.coreDataStack = coreDataStack
    integrator = Integrator()
    rootFlowController = SessionFlowController(coreDataStack: coreDataStack)
    window = Window(rootViewController: rootFlowController.rootViewController)
}

@JessyCatterwaul
Copy link

But is that any better than…?

init(coreDataStack: CoreDataStack) {
    self.coreDataStack = coreDataStack

    integrator = {[mfpService] in
        let coreDataProcessor = CoreDataReportProcessor(stack: coreDataStack)
        let integrator = IntegrationService(provider: mfpService) {
            coreDataProcessor.processReports($0)
        }
        integrator.fetchEvery(interval: 5.hours)
        integrator.fetch()
        return integrator
    }()

    rootFlowController = SessionFlowController(coreDataStack: coreDataStack)

    window = {[rootFlowController] in
        let window = UIWindow(frame: UIScreen.mainScreen().bounds)
        window.backgroundColor = UIColor.whiteColor()
        window.rootViewController = rootFlowController.rootViewController
        window.makeKeyAndVisible()
        return window
    }()
}

@ScottRobbins
Copy link

Got the link to this, thought I'd leave a question.

What about the option of not creating these objects inside this class, and injecting them in the init?

I've often found when I ran into issues like this, how the objects I was trying to create in the init were created didn't matter to the class, the class just needed a reference to them and it seemed like a convenient place to put the code.

I'm not really sure what your circumstances are for this, though, and perhaps you have a good reason for not liking that option.

@krzysztofzablocki
Copy link
Author

@scott I use composition and init injection almost everywhere, but there are situations where it doesn't make much sense, trying to find best way to deal with them :)

@RoyalIcing
Copy link

Private functions sounds like the most straight forward approach I would think. Who cares if it’s a static method or anything. Object oriented is only really about public interfaces. So just do something functional like Jessy’s https://gist.github.com/krzysztofzablocki/6a1b6afab974f442f5ff#gistcomment-1623209

Except I would call the functions initIntegrator or createIntegrator instead of using pascal case, as they look like their own initialisers and types instead of simple functions.

Nice work with this all by the way!

@onmyway133
Copy link

How about Option 2 - lazy var’s with side-effects + builders ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment