hmlongco / Resolver

Swift Ultralight Dependency Injection / Service Locator framework
MIT License
2.15k stars 190 forks source link

Dynamically injecting dependencies #122

Closed AppyMike closed 3 years ago

AppyMike commented 3 years ago

I have a reusable view controller SelectionViewController and it has a viewModel that is a protocol SelectionViewModel.

class SelectionViewController: UIViewController {

    private var viewModel: SelectionViewModel?

    convenience init(vm: SelectionViewModel) {
         self.init()
         self.viewModel = viewModel
    }

    ...
}

Throughout the app the SelectionViewController is reused and passed a different View Model that conforms to the SelectionViewModel protocol.

let vc = SelectionViewController(vm: TicketViewModel())
self.present(vc, animated: true, completion: nil)

Does resolver support dynamically injecting a dependency in this way?

hmlongco commented 3 years ago

Couple of ways to do that, starting with..

let vc = SelectionViewController(vm: Resolver.resolve(TicketViewModel.self))

Where you ask Resolver to give a fully resolved VM.

You could also use names to differentiate instances of the same protocol.

register(name: "ticket") { TicketViewModel() as SelectionViewModel }
class SelectionViewController: UIViewController {
   @LazyInected var viewModel: SelectionViewModel
let vc = SelectionViewController(vm: TicketViewModel())
vc.$viewModel.name = "ticket"
self.present(vc, animated: true, completion: nil)

Using Lazy lets you change the name prior to first use/resolution.

AppyMike commented 3 years ago

so the latter could work something like this?

register(name: "ticket") { TicketViewModel() as SelectionViewModel }

let vc = SelectionViewController(viewModelName: "ticket")
self.present(vc, animated: true, completion: nil)
class SelectionViewController: UIViewController {
   @LazyInected var viewModel: SelectionViewModel

   convenience init(viewModelName: String) {
         self.init()
         $viewModel.name = viewModelName
    }
}
AppyMike commented 3 years ago

with the second option, that would be easier to mock for testing right? just a simple change to register(name: "ticket") { MockTicketViewModel() as SelectionViewModel }

hmlongco commented 3 years ago

Correct.