As
Kotlin Multiplatform
is becoming an increasingly popular choice for cross-platform development, developers face the task of selecting a suitable dependency-injection library for their KMP projects. We’re here to introduce
Koin
and
Kotlin-inject
, two libraries that take different approaches to solving this problem.
interface WeatherApiService {
@GET("weather")
suspend fun getWeatherData(@Query("city") city: String): WeatherData
// GetWeatherDataInteractor.kt
class GetWeatherDataInteractor(private val weatherApiService: WeatherApiService) : Interactors.GetWeatherData {
override suspend operator fun invoke(city: String): WeatherData {
return weatherApiService.getWeatherData(city)
// WeatherRepository.kt
class WeatherRepository(private val getWeatherInteractor: Interactors.GetWeatherData) : Repositories.Weather {
override suspend fun fetch(city: String): Weather {
return getWeatherInteractor(city).mapToLocal()
// GetWeatherUseCase.kt
class GetWeatherUseCase(private val weatherRepository: Repositories.Weather) : UseCases.GetWeather {
override suspend operator fun invoke(city: String): Weather {
return weatherRepository.fetch(city)
-
Kotlin-based dependency injection library that offers a user-friendly experience through its easy-to-read Kotlin DSL
-
Suitable for any Kotlin application, including Android, Multiplatform, or Backend development
-
Easily integrates with the ViewModel, Ktor, and Compose, which adds to Koin’s appeal across diverse developer domains
val interactorsModule = module {
includes(servicesModule)
factory<Interactors.GetWeatherData> {
GetWeatherDataInteractor(get())
// RepositoriesModule.kt
val repositoriesModule = module {
includes(interactorsModule)
single<Repositories.Weather> {
WeatherRepository(get())
// UseCasesModule.kt
val useCasesModule = module {
includes(repositoriesModule)
factory<UseCases.GetWeather> {
GetWeatherUseCase(get())
The
factory
function binds implementations to interfaces and the function
get()
helps Koin retrieve injected dependencies during runtime. In the code snippet below, we’re instructing Koin on how to build a
UseCases.GetWeather
instance. It uses the
get()
function to obtain the necessary
Repositories.Weather
dependency that
GetWeatherUseCase
requires:
-
Relatively new and simple Kotlin-based dependency-injection library
-
Powered by Kotlin’s robust features like
KSP
and lazy initialization, which simplify the process of providing dependencies
-
With the use of Kotlin typealias, it allows injecting multiple instances of the same type and even supports lambda injection
@Provides
protected fun provideWeatherApiService(): WeatherApiService =
WeatherServiceProvider.provide()
// InteractorsComponent.kt
interface InteractorsComponent : ServicesComponent {
@Provides
protected fun GetWeatherDataInteractor.bind(): Interactors.GetWeatherData = this
// RepositoriesComponent.kt
interface RepositoriesComponent : InteractorsComponent {
@ExampleScope
@Provides
protected fun WeatherRepository.bind(): Repositories.Weather = this
// UseCasesComponent.kt
interface UseCasesComponent : RepositoriesComponent {
@Provides
protected fun GetWeatherUseCase.bind(): UseCases.GetWeather = this
// ExampleComponent.kt
@ExampleScope
@Component
abstract class ExampleComponent : UseCasesComponent {
abstract val getWeatherUseCase: UseCases.GetWeather
interface RepositoriesComponent : InteractorsComponent {
// provide repository dependencies
interface UseCasesComponent : RepositoriesComponent {
// provide usecase dependencies
@ExampleScope
@Component
abstract class ExampleComponent : UseCasesComponent {
// Koin
// Startup time: 0.2475 ms
val exampleKoin = startKoin { modules(useCasesModule) }.koin
// Injection time: 0.0137 ms
val weatherUseCase = exampleKoin.get<UseCases.GetWeather>()
// Kotlin-inject
// Startup time: 0.0005 ms
val exampleComponent = ExampleComponent::class.create()
// Injection time: 0.0043 ms
val weatherUseCase = exampleComponent.getWeatherUseCase
In summary, while Koin offers quicker build times, it does come with a trade-off. The runtime dependency resolution impacts the overall performance,
causing Koin to lag significantly behind Kotlin-inject in this regard
. The difference is most noticeable during startup. Resolving the Koin dependency graph requires significantly more time compared to creating a Kotlin-inject component, which benefits from pre-generated boilerplate code.
Now, let’s delve into the libraries’ debugging capabilities. Both libraries excel in providing clear and informative error descriptions. However,
Kotlin-inject has the advantage of showing them at compile-time
, which allows you to correct your mistakes without ever running the application. This is not only a more convenient but also a much safer approach. On the other hand, Koin addresses this with somewhat of a workaround –
writing tests to validate your Koin setup
:
Koin has an active community of contributors
ensuring regular updates and enhancements. After all, they’ve paved the way for smooth integrations with Ktor, Compose, ViewModel, and KMP and will continue to provide support for future technologies. Alongside regular updates and initiatives, Koin’s engaged user community helps spot and resolve issues quickly. On the other hand,
Kotlin-inject is still establishing its presence
with a smaller contributor base. However, it’s gaining traction within the Kotlin development scene, hinting at a bright future for the community.
Implementing IoT Device Onboarding in Mobile Apps
Discover what challenges to expect when implementing IoT device onboarding in a mobile app and how to approach them best.
Karlo Čeh
Panda CSS – CSS-in-JS without Runtime Overhead
With its simplified approach to front-end styling, Panda CSS can help you streamline your development workflow and create efficient web applications.
Ivica Batinić
A Crash Course in Augmented Reality on iOS with ARKit
Since the release of
ARKit
in 2017., and especially the 2.0 announcement during the WWDC 2018 conference, I’ve been interested in what it can provide.
Goran Brlas
We use cookies to optimise and continuously improve our website for individual users like you. By clicking “Accept all”, you accept storing of cookies on your device. Find out more at our
Privacy Policy
Manage
Accept all