Search code examples
iostyphoon

Typhoon Storyboard: Inject an IBOutlet View to a Controller dependency


I have a storyboard that has a view in it connected to his controller using an outlet. In the same controller I want to inject an object that needs access to that view. Instead of passing that view manually to the object I would like to inject it automatically but I don't know how and If I can achieve that with the current code structure.

class LoadingViewController: UIViewController {
    @IBOutlet weak var loadingView: UIActivityIndicatorView!
    private(set) var loadingViewModel: LoadingViewModel! // Dependency Injection
}

// Assembly

dynamic func loadingViewController() -> AnyObject {
    return TyphoonDefinition.withClass(LoadingViewController.self) {
        (definition) in
        definition.injectProperty("loadingViewModel", with:self.loadingViewModel())
    }
}

dynamic func loadingViewModel() -> AnyObject {
    return TyphoonDefinition.withClass(LoadingViewModel.self) {
        (definition) in
        definition.injectProperty("loadingView", with:???) // I want loadingViewController.loadingView 
    }
}

I think it has something to do with run-time arguments and circular dependency


Solution

  • That's a good one. We have to consider the life-cycle between the Storyboard created objects and Typhoon.

    Have you tried something like:

    //The view controller 
    dynamic func loadingViewController() -> AnyObject {
        return TyphoonDefinition.withClass(LoadingViewController.self) {
            (definition) in
            definition.injectProperty("loadingViewModel",     
                with:self.loadingViewModel())
            definition.performAfterInjections("setLoadingViewModel", arguments: ) {
                (TyphoonMethod) in 
                method.injectParameterWith(self.loadingViewModel())
            }
        }
    }
    
    dynamic func view() -> AnyObject {
        return TyphoonDefinition.withFactory(self.loadingViewController(), 
            selector:"view")
    }
    
    dynamic func loadingViewModel() -> {
        return TyphoonDefinition.withClass(SomeClass.class) {
            (definition) in
            definition.injectProperty("view", with:self.view())
        }
    }
    
    • Creates a definition for the view, instructing Typhoon that it will be emitted from the loadingViewController
    • Creates a definition for the loadingViewModel that has view injected.
    • After the loadingViewController, and therefore view has been created, inject the loadingViewModel as the last step.

    I don't recall if the scope pool is cleared before calling performAfterInjections. If is is you might need to set the scope of loadingViewController to TyphoonScopeWeakSingleton instead of the default TyphoonScopeObjectGraph.

    Because of the interplay between Typhoon and Storyboards it might be just simpler to manually provide the instance in eg viewDidLoad. But can you give the above a try and get back to me?