Swinject / SwinjectStoryboard

Swinject extension for automatic dependency injection via Storyboard
MIT License
268 stars 141 forks source link

Feature brainstorm: Inject arguments on `instantiateViewController` #61

Open oronbz opened 7 years ago

oronbz commented 7 years ago

Hi,

I really love the feature of Swinject where I can add additional arguments when I call resolve: Declaration:

func resolve<Service, Arg1>(
        _ serviceType: Service.Type,
        argument: Arg1) -> Service?

Usage: r.resolve(OfferSettingsViewModeling.self, argument: "Some Argument")

I wonder if we could implement a similar thing when instantiating view controllers from SwinjectStoryBoard: Something like: func instantiateViewController<Arg1>(withIdentifier identifier: String, arg1: Arg1) And use it like this:

let storyboard = SwinjectStoryboard.create(name: "Main", bundle: nil, container: container)
storyboard.instantiateViewController(withIdentifier "MainViewController", arg1: 123)

and then I can catch it on storyboardInitCompleted like this:

container.storyboardInitCompleted(MainViewController.self) { r, c, arg1 in
            c.viewModel = r.resolve(MainViewModeling.self, arg1: arg1)!
        }

I think this would be really helpful for use cases where you want to instantiate a view controller with a given context, mostly relevant in master/detail stories, where the detail view controller's view model need to be instantiated with an identifier regarding the chosen detail on the master view controller.

What do you think?

jakubvano commented 7 years ago

Hi @oronbz

Thanks for the suggestion πŸ‘ - I agree this would be a logical extension for SwinjectStoryboard API.

However, it currently does not exist, because storyboardInitCompleted is basically wrapper around Swinject's initCompleted and you don't have access to arguments inside initCompleted closures. So to implement this, you would need to either do some hacks or modify Swinject to suport arguments in initCompleted. Although latter would probably require significant redesign of Swinject internal implementation, it might be worthwhile since there are other usecases (e.g. circular dependencies) where you might want to have access to arguments inside initCompleted.

oronbz commented 7 years ago

Hey @jakubvano!

Great to hear you agree with me, now let's try to think how to tackle this with a none to minimal effect on Swinject.

One way that not necessarily involves arguments, is adding another parameter to SwinjectStoryboard.instantiateViewController method called something like: mutate which is paramter of type (UIViewController) -> (). and the instantiateViewController will call this callback after it instantiates the view controller but before it calls injectDependencies.

That way we can write a mutate function on the caller side that will be able to assign properties to the instantiated view controller before storyboardInitCompleted is called, and then we'll be able to use those properties as arguments to other resolves inside the factory method of storyboardInitCompleted.

This is the least obtrusive solution that I can think of that doesn't involve touching core Swinject.

What do you think? Have any other suggestions or feedback?

malkevych commented 7 years ago

You can use my solution that gives you pass up to 3 arguments

oronbz commented 7 years ago

@webslesar Looking good! Why not make a pull request?

IanKeen commented 6 years ago

In case people are still following along.. it doesn't appear like #62 is moving along. I've been using this to pass arguments to storyboard based VCs which requires no modification to swinject or swinject storyboard.

https://gist.github.com/IanKeen/93e9bbf203f84407e7873044d765b164

oronbz commented 6 years ago

Thanks @IanKeen I'll try that and let you know how it's working for us πŸ‘