top of page
  • Writer's picturePamela

An Investigation of Dependency Management Libraries for Kotlin Multiplatform Mobile: Koin

This is the second article in an article series that will discuss the dependency injection and service locator patterns, explain how they differ, and detail the most prominent Multiplatform Mobile libraries and approaches to implementing these patterns. This article introduces the first library: Koin. You can find the first article in the series here.

Koin is a well-known open-source Service Locator dependency management library that has support for Android, Ktor, and Multiplatform.

A Little Boilerplate

We start off with a little bit of boilerplate code running across all our articles. Recall that our example is that of a search box on a company intranet enabling us to search across staff members.

data class StaffMember(val name: String, val position: String)

interface StaffLister {
   fun findAllStaffMembers(): List<StaffMember>

class StaffListerImpl : StaffLister {
    override fun findAllStaffMembers(): List<StaffMember> {
        return listOf(
                "Pamela Hill", 
                "Developer Advocate"

class SearchBox(private val lister: StaffLister){
    fun findStaffMemberByName(name: String): StaffMember? {
        return lister.findAllStaffMembers().firstOrNull { 
            name == 


To initialize Koin, it is necessary to call the startKoin function with one or more modules of dependency bindings within the context of the application. The code below contains the definition of the application module and an initKoin function. The initKoin function then needs to be called during the right parts of the application lifecycle initialization.


import org.koin.core.KoinApplication
import org.koin.core.context.startKoin
import org.koin.core.module.Module
import org.koin.dsl.module

object Modules {
   val appModule = module {
       factory { StaffListerImpl() as StaffLister }
       factory { SearchBox (get()) }

fun initKoin(
   appModule: Module = Modules.appModule
): KoinApplication = startKoin {



class MyApplication : Application() {
   override fun onCreate() {



Note that we are using a bridge from the iOS application to the iOS shared code to the initKoin function, which is unnecessary in the Android application.


fun initKoinApp(){


struct iOSApp: App {
    init() {


In order to hook the user interface up to the search box implementation, it is necessary to inject the SearchBox into a user interface component. This is fairly straightforward for Android, but on iOS, we once again have to use a bridge from the iOS application to the iOS shared code, where the SearchBox is injected (notice also that the “bridge” is a KoinComponent).




class MainActivity : AppCompatActivity() {
    private val searchBox: SearchBox by inject()

    override fun onCreate(savedInstanceState: Bundle?) {

        val tv: TextView = findViewById(
        tv.text = searchBox
            .findStaffMemberByName("Pamela Hill").toString()



import org.koin.core.component.KoinComponent
import org.koin.core.component.inject

class SearchBoxHelper : KoinComponent {
    private val searchBox : SearchBox by inject()

    fun findByName(name: String) : String =  


struct ContentView: View {
    let staff = SearchBoxHelper().findByName(name: "Pamela Hill")

	var body: some View {


Koin is a lovely dependency management library for multiplatform development. I have developed a large-scale Android app with it, after finding that Dagger was too complicated for the team to understand (this was before Hilt). The team behind it is passionate and takes even harsh criticism from the community in their stride, taking it as constructive and using it to make the library even better. I have two examples of this:

Criticism 1: Since there is no compile-time check as with Dagger, it can lead to crashes when a needed dependency is omitted

While there is no way to make Koin a code-generating DI library, they took this criticism and introduced a way to check your module dependency graph during your unit testing cycle. This clever move allows you to make checking your modules part of your CI/CD pipelines and your app won't hit production with such a blaring unit test failure, assuming you remember to add all your modules.

class CheckModulesTest : KoinTest {

    fun verifyKoinApp() {
        koinApplication {
            modules(module1, module2)

Criticism 2: All the calls to get() for long dependency lists quickly gets out of hand

In the versions of Koin before 3.2, long dependency lists meant long argument lists of get() arguments being passed mindlessly to constructors. But in version 3.2, a new constructor DSL was introduced that eliminated this problem. For example:

module {
    factory { StaffListerImpl() as StaffLister }
    factory { SearchBox (get()) }

became instead:

module {
    factoryOf(::StaffListerImpl) bind StaffLister::class

Now, imagine the benefit if SearchBox had several dependencies!


The remainder of this series will focus on the actual libraries and approaches for dependency management in Multiplatform Mobile, and will feature articles on:

  • Kodein

  • Kotlin-Inject

  • Manual injection

Stay tuned!



“Multiplatform Injection” - Koin official documentation

1,123 views0 comments
bottom of page