I have been researching various Dependency injection techniques as would like to introduce more of it into my app. I recently came across this article here
http://www.danielhall.io/a-swift-y-approach-to-dependency-injection
I quite like this approach as rather than doing my current approach of injecting viewModel and services into each new controller, it can be handled by a protocol. So I have setup a protocol like this
So first, I have created some viewModel protocols that looks like this
protocol ViewModel {
associatedtype Service
init (withService service: Service)
}
protocol ViewModelBased: class {
associatedtype ViewModelType
var viewModel: ViewModelType { get set }
}
So all viewModels that conform to viewModel have to have a service injected into it and it is up to the model to dictate which service is used. All classes that conform to ViewModelBased must contain a viewModel.
I have also created some more generics and protocols to allow controllers to be instantiated with a viewModel quite easily like this
let loginModel = LoginModel(withService: LoginService())
let controller = LoginController.instantiateController(with: loginModel)
This works fine but is rather cumbersome as every class that contains a viewModel requires this each time. So by researching the article Daniel hall created, I have come up with this
struct InjectionLoginModelMap {
static var loginModel = LoginModel(withService: LoginService())
}
protocol LoginModelInjected { }
extension LoginModelInjected {
var loginModel : LoginModel { get { return InjectionLoginModelMap.loginModel}}
}
I create one of these for each viewModel in the app. So instead of having to instantiate the viewModel myself each time, I just make sure my class conforms to the correct protocol each time. E.g.
class LoginController: UIViewController, LoginModelInjected {
This makes it easy to add multiple dependencies to a controller. It also means I no longer have to inject viewModel into controllers that need it as it is now handled by the protocol. So can initialise controller like this now
let controller = LoginController.instantiateController()
Then when I want to mock the services in unit testing, I can just call this
let loginService = MockLoginService()
let loginModel = LoginModel(withService: loginService)
InjectionLoginModelMap.loginModel = loginModel
This works quite well and is much less cumbersome than the previous method. My Concern however is that these models, once initialised will always be in memory. So even after I have finished with a controller and all its objects have been cleared from memory, the viewModel will remain even though it is no longer needed.
Was hoping for some advice on the best way to deal with this. I was considering maybe changing the InjectionLoginModelMap struct so viewModel is optional like so
struct InjectionLoginModelMap {
static var loginModel : LoginModel? = LoginModel(withService: LoginService())
}
protocol LoginModelInjected { }
extension LoginModelInjected {
var loginModel : LoginModel { get { return InjectionLoginModelMap.loginModel!}}
}
Then when I know I have finished with viewController and know that I no longer need access to that viewModel, I can just nillify it like so
InjectionLoginModelMap.loginModel = nil
However this too feels quite cumbersome and ugly. Would be very grateful if someone could give me some advice on best way to solve this. Also any other tips or advice on how others handle dependency injection would be much appreciated.
Thanks!