Swinject: Circular dependency with ViewController

Created on 5 Oct 2016  路  6Comments  路  Source: Swinject/Swinject

Hello, I have the following case:

protocol ViewProtocol {
   func foo()
}

class AViewController: UIViewController, ViewProtocol {
   public var b: B?
}

class B {
   private let view: ViewProtocol

   init(view: ViewProtocol) {
      self.view = view
   }
}

Can you please tell me how should I resolve those 2 dependencies? I tried to follow the example from here but I couldn't manage to do it.
Note: AViewController is the initial ViewController in the main storyboard.

Thank you in advance,
Alin

question

Most helpful comment

Unfortunatelly, SwinjectStoryboard does not support circular dependencies - as stated here you cannot directly call resolve for view controllers initialised from storyboard - thus with your current setup resolver.resolve(AViewController.self)! will fail.

Also, with your current setup there will be retain cycle between B and AViewController - at least one of the references should be weak.

When I need circular dependency with UIViewController I typically use this setup:

protocol ViewProtocol: class {}

class ViewController: UIViewController, ViewProtocol {
    var presenter: Presenter?
}

class Presenter {
    weak var view: ViewProtocol?  // I choose this side of retain cycle to be weak, because `ViewController` is retained in UIWindow
}

I usually dont use SwinjectStoryboard, but if I were to, I would probably do it like this:

defaultContainer.registerForStoryboard(ViewController.self) { resolver, controller in
    let presetner = resolver.resolve(Presenter.self)!
    controller.presenter = presenter
    presenter.view = controller
}

defaultContainer.register(Presenter.self) { _ in Presenter() }

I hope this helps 馃槃

All 6 comments

Hi @alynmuntean,
Are you using SwinjectStoryboard for your AViewController, or creating it via init? Could you post the Swinject code you've tried and did not work?

Hello @jakubvano,
Yes, I am using SwinjectStoryboard for AViewController, which is also the initial view controller in the Storyboard. I don't have that code anymore because I did try many options and I ended up with one that I'm not very sure it is the good one. Could you please check the solution and maybe give me a feedback if it is ok or not?

extension SwinjectStoryboard {
    class func setup() {
        defaultContainer.registerForStoryboard(AViewController.self) { resolver, controller in
            let b = B(view: controller)
            controller.b = b
        }

        defaultContainer.register(B.self) { resolver in
            return resolver.resolve(AViewController.self)!.b!
        }
    }
}

Thank you!

Unfortunatelly, SwinjectStoryboard does not support circular dependencies - as stated here you cannot directly call resolve for view controllers initialised from storyboard - thus with your current setup resolver.resolve(AViewController.self)! will fail.

Also, with your current setup there will be retain cycle between B and AViewController - at least one of the references should be weak.

When I need circular dependency with UIViewController I typically use this setup:

protocol ViewProtocol: class {}

class ViewController: UIViewController, ViewProtocol {
    var presenter: Presenter?
}

class Presenter {
    weak var view: ViewProtocol?  // I choose this side of retain cycle to be weak, because `ViewController` is retained in UIWindow
}

I usually dont use SwinjectStoryboard, but if I were to, I would probably do it like this:

defaultContainer.registerForStoryboard(ViewController.self) { resolver, controller in
    let presetner = resolver.resolve(Presenter.self)!
    controller.presenter = presenter
    presenter.view = controller
}

defaultContainer.register(Presenter.self) { _ in Presenter() }

I hope this helps 馃槃

There are defined types in the example above. What about I don't want to define class type but only 'interface'. The main idea of DI to resolve dependencies at run time

class LoginViewController: UIViewController, LoginViewInput {    
    var output: LoginViewOutput?

    override func viewDidLoad() {
        super.viewDidLoad()
        output?.didTriggerViewDidLoad();
    }

    func say(message: String?) {
        print(message)
    }
}

class LoginPresenter: LoginViewOutput {
    weak var view: LoginViewInput?
    var presenter: LoginInteractorInput?

    func didTriggerViewDidLoad() {
        print("Did trigger View Did Load")
        view?.say(message: "Hello")
    }
}

@alynmuntean I will close this - if you have more questions, feel free to reopen this issue

@igorkotkovets I will respond to your question in SwinjectStoryboard #27, as it contains more info about the problem you are facing.

@jakubvano, sorry for my late response, the solution you provided works very well. Thank you! :)

Was this page helpful?
0 / 5 - 0 ratings