Skip to content

Koin

Developer UtilitiesDependency InjectionKotlin

What it is

Koin is a pragmatic dependency injection framework for Kotlin using a DSL, with no code generation, no reflection and no annotation processing.

Declare modules with single, factory and viewModel builders, start Koin at application launch, then inject by type.

Installation

implementation("io.insert-koin:koin-core:4.0.0")

Getting started

The smallest useful thing you can do with it, and what each part means.

Modules and injection
val appModule = module {
    single<BookRepository> { SqlBookRepository(get()) }   // one instance
    single { HttpClient(CIO) }
    factory { BookValidator() }                            // new each time
    viewModel { BookListViewModel(get(), get()) }
}

startKoin { modules(appModule) }

// Injection
class BookService : KoinComponent {
    private val repo: BookRepository by inject()   // lazy
}

// Or by constructor, which is preferable.
class BookService(private val repo: BookRepository)
get() resolves the dependency by type at the point of construction. Constructor injection is better than the KoinComponent style, which hides the dependency from the signature.

Advanced usage

Where the library earns its place over a simpler alternative.

Qualifiers, scopes and verification
val networkModule = module {
    single(named("auth")) { HttpClient { /* with auth */ } }
    single(named("public")) { HttpClient() }

    scope<UserSession> {
        scoped { SessionCache() }   // lives as long as the scope
    }
}

val client: HttpClient by inject(named("auth"))

// Catch missing bindings in a test rather than at runtime.
class ModuleTest {
    @Test
    fun `modules resolve`() = checkModules { modules(appModule, networkModule) }
}
checkModules is essential with Koin. Because resolution happens at runtime, this test is what turns a missing binding from a production crash into a build failure.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

NoBeanDefFoundException
No binding for that type, or a qualifier mismatch. Add it to a module, and add checkModules to catch it earlier next time.
Koin has not been started
startKoin was not called, or was called after the first injection. Start it in Application.onCreate or at the top of main.

Best practices

  • Always add a checkModules test; it recovers most of what compile-time DI gives you.
  • Prefer constructor injection over the KoinComponent by inject() style.
  • Use named qualifiers when two bindings share a type.
  • Choose Hilt on Android when compile-time safety matters more than build speed.

Background

Why it exists, and what it was reacting to.

Koin trades compile-time verification for simplicity and build speed. Where Dagger and Hilt generate code, Koin resolves at runtime from a declarative module — much faster to build, at the cost of runtime failures for missing bindings.