Too Many Dependencies in iOS? Use the Composition Root Pattern

iOS architecture isn’t just about MVC, MVVM, M*

Ethan Keiser
Better Programming

--

Source: Undraw

If you ask an iOS engineer, “which architecture will you use to design X application?”, often they respond with MVC, MVVM, MVP, etc.

This post will explain why these popular acronyms do not fully answer the question and will showcase an alternative solution.

MVC, MVVM, MVP are the UI layer designs or UI Architectures and not system architectures.

They describe the flow of data and the separation of responsibilities within the UI layer. It does not answer any questions about navigation, networking, caching, business logic, etc.

Adding these responsibilities to the UI layer creates monolithic applications with massive dependency graphs. This leads to rewrites, untestable code, and high cost for change.

The solution is to break down the monolith into modular components and compose them together at the “Composition Root. For example, you want to create a feed application of images similar to Instagram.

A common UI architecture may look like this.

A user selects a FeedImageCell and expects to navigate to a FeedDetailedViewController.

The code above is a common strategy that requires view controllers to be responsible for navigation and creation of its children view controllers.

Now imagine your requirements change and you now need to implement the ability to log, retrieve resources from remote or local repositories, and support older operating systems.

You use URLSession and load the data at viewDidLoad as shown below:

When the user selects an item you check to determine which controller to navigate to and log it.

These minor changes required developers to modify the FeedViewController. This violates the Open/Close principle because the possible navigation routes are determined very early at compile time.

Even if we inject the dependencies to delay behaviors of the FeedViewController via construction injection, we still need to inject the dependencies of any child view controller it creates.

If the child view controller has children view controllers, we’d have to inject those responsibilities at the root view controller and cascade them down. This could continue indefinitely within large complex applications and create major problems with massive dependency graphs.

You can already see the dependency web forming around the FeedViewController by coupling it with LoggingFramework and URLSession. Any dependency its children have would also be its dependency.

Meet the Composition Root Pattern

The Composition Root pattern delays decision-making about how components interact and allows us to intercept and modify behaviors.

It lives in the Main module, a concept that is foreign to most iOS engineers.

Let’s take a look at how to compose components within the main module and break up the monolith.

Now, the FeedViewController is no longer responsible for the creation and navigation of its children.

The FeedComposer composes the FeedViewController and assembles it with all its dependencies. Using closures we can inject behavior without coupling UI module components with Networking or Logging modules.

The FeedNavigation handles routing and the Logging module is notified via its delegate.

Notice the flow of dependencies. Main is dependent on all other modules while no module knows about each other.

By creating modular code and composing components together at the composition root, we create virtual boundaries between modules.

Suppose we want our Feed application to run on the Apple Watch or Mac OS. The initial approach of coupling everything in the UI layers and calling it MVC/MVVM/M* would require a total rewrite.

With our new approach, we’d only need to rewrite the FeedUI module and connect the components at the composition root within Main. All the business logic, networking, logging will be reused. This is the power of modular design.

Thank you for reading. The full source code is available in the GitHub Repository

Want to Connect?You can reach out to me on Twitter.

--

--