MVVM-C Architecture with Dependency Injection + Testing
A complete example
I’ve been using MVVM-C for quite some time. I’m sure you (probably, I don’t know you) agree that by abstracting the navigation away from the main MVVM architecture code gives you the opportunity for testing. Great!
What if you have multiple dependencies? How are these going to be injected into your…wait.. where…what?
Making this clear is the goal of this article.
Difficulty: Beginner | Easy | Normal | Challenging
This article has been developed using Xcode 11.4.1, and Swift 5.2.2
- You will be expected to be aware how to make a Single View Application in Swift.
- Some experience of architecture patterns are required, whether MVC or MVVM.
- The principles of Dependency Injection are explained in this article, but some in-depth explanation always helps!
- You’ll need to be comfortable with protocols
Factory Pattern: A design pattern where an object is created without exposing the creation logic to the client. The factory pattern lets a class defer instantiation to subclasses.
MVVM-C helps with separation of concerns, and allows testing and implementation to be…much better than MVC. Now, nothing is perfect, but it is perfectly possible to have a variety of dependencies which can lead to large and unwieldy initializers.
One possible solution to this is use of the Factory Pattern, and the use of Dependency Injection to test the relevant classes. This particular article is written with the views programmatically defined, avoiding Storyboards in the entire project, and neatly overriding loadview().
With that understood, let’s get to it!
The idea of this article is to have a basic skeleton for the MVVM-C architecture. There are few around, particularly coded in Swift and even fewer making an attempt at testing.
You’re probably working in a shop where they demand 80% test coverage — and this article and the accompanying repo isn’t quite there (this example isn’t promised to be production ready), however it is a very good start for a developer to build on.
The coordinator has the following responsibilities:
- Initialize the network service
- Perform network calls
- Initialize the ViewModel with the fetched data (or instruct the ViewModel to handle a failed network call)
One of the best ways to explore the implementation is do download the files to your machine and take a look. Give me a nice star when you’re there (if you can).
I’ve created everything programmatically avoiding storyboards
This is at the heart of the matter, because the coordinator controls the flow of the App. If a
UIViewController wishes to move to another
UIViewController it must do so through the coordinator.
The main Coordinator is called
ProjectCoordinator that conforms to a RootCoordinator protocol and
AbstractCoordinator protocol. By using
protocols rather than base classes you do end up with a rather more Swifty solution — something recommended for Swift developers.
The coordinator protocols
Which are then put to use in out
The ProjectCoordinator concrete class
ProjectCoordinator has knowledge of just the factory class, which it uses to create the
InitialViewController instance which is then pushed on the
UINavigationController stack. The reference to
navigationController is weak to prevent a retain cycle as the
UIViewController instance has a reference to the
moveToDetail() function demonstrates how moving between
UIViewController instances could work, and in fact if we wanted to pass parameters or values from one
UIViewController to another (although questions should be raised about why you’re not doing this through the model) it could be done here.
The Dependency Factory class lets us inject services. This example has a ‘fake
HTTPManager based upon my basic http manager class at it’s heart, although I’m sure you have your own solution for making network calls (don’t feel like I’m bullying you into this).
The Factory protocols
For better testing this conforms to my
Factory protocol that enforces conforming classes to have a network manager and the minimum functions in order to create the
UIView instances and
The dependency factory concrete class
The dependency factory creates these artifacts that will be used to create the objects that our App requires.
The ViewModel classes
The view models know about the coordinator (but not about the factory). With this design, the view model can hit the coordinator for navigation, and make the transitions happen. Equally the view model has the dependency — the network manager.
The view model creates the model from here when the network model returns data from the Endpoint API.
The ViewController classes
UIViewController classes are responsible for the
UIView classes, and since in this implementation they have no dependencies these are created within the
sometimes choose to let the
UIViewController control navigation, and this means that my
UIViewController classes have visibility of the
Equally, the view controller has viability of the view model class, and it is the viewmodel classes that are used for network calls in this implementation.
Initialization from the storyboard is not currently supported.
The View classes
The views are rather dumb here, don’t have any dependencies and just set up their subviews. For the sake of completion, the
InitialView is shown here.
The Model classes
The example doesn’t have a particularly inspiring model. At all. I’ve created a model with just a single dataString.
You might well use this to store something like user details (obtained by an API), or whatever. The choice is yours.
Unfortunately there isn’t much to learn from this class, but for completion’s sake here is the code:
This article is not about testing specifically, but does contain tests and mocks partly because there is so little out there in existing articles.
To keep this article readable (or at least approaching readable) I’ve kept the implementation details of these tests to the repo. To access the code hop over to the repo and press download. You can then read through the files, but below is the explanation of the same.
These would require mocked
HTTPManagerProtocol. No problem, as
CoordinatorMock show we can create .
The views should not usually be unit tested. What are you going to check? The positions of the view in a container that doesn’t exist?
For completeness, if you wanted to do this the code is included in
We can mark a test function with throws, and any exception would cause the test to fail. This can be used in conjunction with a
guard statement a mock for
UINavigationController to ensure the correct View Controller is pushed onto the
UINavigationController (well, a mock of
UINavigationController ) stack.
It doesn’t make any sense to test the dependency factory, as it is this class that is replaced for the tests.
FactoryTests is simply empty.
I hope this article has helped you out, going through the relevant classes that enable you to create a MVVM-C implementation.
If you’ve been using MVC I appreciate this might seem like a change and, yes, well it is.
However having different methods of producing your App will only help in the long run, and I hope that this article has really helped you out.
Thanks for reading!
If you’ve any questions, comments or suggestions please hit me up on Twitter