An Investigation of Dependency Management Libraries for Kotlin Multiplatform Mobile
Kotlin Multiplatform Mobile (or simply Multiplatform Mobile) is a new SDK from JetBrains that allows iOS and Android developers to share code between the two platforms. The shared code is written in Kotlin. Typical use cases include: sharing networking, data storage, internal libraries, and algorithms, but the choice is yours!
As your Multiplatform Mobile application starts growing, it becomes necessary to refactor your code to improve its scalability. One possible such refactoring is to separate application components’ configuration from their use, and in so doing, make application components more maintainable, reusable, and testable. This is typically enabled using libraries following either the dependency injection or service locator patterns.
Our running example will be that of a search box on a company intranet page where a particular staff member can be located based on a search criteria. Figure 1 illustrates a naive way this could be implemented. The SearchBox class could use the StaffLister interface to get all the staff members of the company, and then try to locate all the staff members matching certain criteria. However, SearchBox is also configuring itself with the StaffListerImpl implementation of StaffLister. Perhaps the current StaffListerImpl looks up all staff members in a spreadsheet, but one day this could be replaced by StaffListerImpl2 where all staff members are listed by a web service connected to a database. Therefore, we will have to refactor the codebase to separate the configuration of the SearchBox class with StaffListerImpl from its use of the StaffLister interface to do its work.
This article series will discuss the dependency injection and service locator patterns, explain how they differ, and detail the most prominent Multiplatform Mobile libraries and approaches implementing these patterns. This article introduces these important dependency management patterns.
The Dependency Injection Pattern
The Dependency Injection (or DI) pattern is where an object (called the assembler) populates a field in a client class with an implementation of a specific interface. Figure 2 illustrates how this will look for our search box example.
A term that is often closely associated with DI is that of Inversion of Control. Here, SearchBox is no longer in control of the configuration and creation of the StaffListerImpl, it only needs to know that it will be provided with the correct instance as a field.
The Service Locator Pattern
The Service Locator (or SL) pattern is an alternative to the DI pattern. In this pattern, an object (called the service locator) knows how to get the implementations of dependencies, and a client class knows about the service locator and gets the appropriate dependency instances from it. Figure 3 illustrates how this will look for our search box example.
A note on service locator implementation: the service locator object can be implemented using the Singleton pattern for easy lookup, but this is not mandatory.
Comparing the Patterns
While these two patterns have the same purpose- to separate configuration from use- it’s important to understand the difference between them as there are certain pros and cons that need to be considered when choosing a DI/SL library. The SL pattern requires that the client knows about the locator object for it to make a request for the dependency to the locator. The DI pattern requires no such request, as the dependency is injected via an assembler object into the client.
The nature of the DI pattern makes it inherently difficult to understand and debug, as the injector might be difficult to trace via conventional methods. However, the SL pattern requires an explicit dependency on the service locator object. This complicates testing as the service locator object will need to be somehow mocked to provide testing dependencies. However, the primary concern has to do with creating code where the use is outside the control of the writer, where even the minimal assumption of knowledge of a service locator object is problematic.
The remainder of this series will focus on the actual libraries and approaches for dependency management in Multiplatform Mobile, and will feature articles on:
“Inversion of Control Containers and the Dependency Injection pattern” - Martin Fowler