In our application, we decided that our view models access their dependencies through a dependency search, rather than dependency injection. This means that view models simply pass in a container object that contains the necessary dependencies, and then “looks through” each dependency on this container object.
The main advantage of this is that all objects in the system can be declared in advance in the container definition, and it is very easy to bypass the container compared to dependencies of 70 or so dependencies that may be required.
Like any injection dependency scanner will tell you that dependency search is certainly its incomplete cousin, mainly because dependency search requires the object to understand the idea of the container (and usually it’s usually the framework that provides it), then how injection of dependencies keeps the object blissfully unaware of where its dependencies came from. However, in this case, I think the compromise is worth it. Note that in our architecture, these are just presentation models that make this compromise - all other objects, such as your “models” and “services”, still use DI.
It is also worth noting that many basic dependency search implementations have a container as a singleton, but this is not necessarily the case. In our application, we have several containers that simply group related dependencies with each other. This is especially important if different objects have different life cycles - some objects can live forever, while others may only need to live while a certain user activity is being performed. This is why the container is transferred from the view model to view the model - different view models can have different containers. It also facilitates unit testing, allowing you to transfer a container full of mock objects to the test representation model.
To provide some concreteness to the abstract answer otherwise, here is how one of our view models might look. We use the Swinject framework.
class SomeViewModel: NSObject { private let fooModel: FooModel private let barModel: BarModel init(container: Container) { fooModel = container.resolve(FooModel.self)! barModel = container.resolve(BarModel.self)! }
source share