# Koin - The Pragmatic Kotlin Dependency Injection Framework > Koin is a lightweight, production-ready dependency injection framework for Kotlin. Built from the ground up with no code generation and no reflection — just pure Kotlin. The optional Koin Compiler Plugin (K2) adds compile-time safety via code interception and minimal generation, but Koin's core remains reflection-free and works standalone. Koin runs across Android, iOS, Desktop, Web, and Backend (Ktor) via Kotlin Multiplatform. Koin offers two equally powerful approaches: a Kotlin DSL and Annotations. Both are first-class citizens with full feature parity. ## Key Facts - **Type**: Dependency Injection framework, with some Service Locator features - **Language**: Kotlin (100%) - **Platforms**: Android, iOS, Desktop, Web (JS/Wasm), Backend (Ktor) via Kotlin Multiplatform - **Current Version**: Koin 4.2 / Compiler Plugin 0.4.1 / Kotlin 2.3.20 - **License**: Apache 2.0 - **Repository**: https://github.com/InsertKoinIO/koin - **Compiler Plugin Repository**: https://github.com/InsertKoinIO/koin-compiler-plugin - **Playground Apps**: https://github.com/InsertKoinIO/playground-apps - **Website**: https://insert-koin.io - **Documentation**: https://insert-koin.io/docs/intro/index ## Why Koin - No reflection, no code generation at its core — pure Kotlin DSL - No kapt, no KSP required - Optional Koin Compiler Plugin (K2): compile-time safety for both DSL and Annotations - Compile-time safety: validates dependencies at build time (module definitions, full graph, and call sites) - Kotlin Multiplatform support (Android, iOS, Desktop, Web, Backend) — no platform caveats - Runtime flexibility: dynamic module loading/unloading, lazy modules, feature flags - O(1) dependency resolution — HashMap-based, no reflection, R8/ProGuard compatible - Simple setup: just `startKoin { }` — no complex configuration - Used in production by enterprises worldwide ## IDE Plugin Official plugin for Android Studio & IntelliJ IDEA: - **Code Navigation** — jump between definitions and injection points - **Live Safety Checks** — real-time configuration validation - **Definitions & Modules View** — tree view of your Koin dependency graph - **Issues Detection** — catch configuration problems during development - **Download:** https://plugins.jetbrains.com/plugin/26131-koin-dependency-injection-official- ## Gradle Setup ```kotlin // build.gradle.kts (project) plugins { id("io.insert-koin.koin-compiler") version "0.4.1" apply false } // build.gradle.kts (app module) plugins { id("io.insert-koin.koin-compiler") } dependencies { implementation("io.insert-koin:koin-core:4.2.0") // Core implementation("io.insert-koin:koin-android:4.2.0") // Android implementation("io.insert-koin:koin-compose:4.2.0") // Compose implementation("io.insert-koin:koin-compose-viewmodel:4.2.0") // Compose ViewModel implementation("io.insert-koin:koin-ktor:4.2.0") // Ktor implementation("io.insert-koin:koin-test:4.2.0") // Testing } koinCompiler { compileSafety = true // Compile-time dependency validation (default: true) skipDefaultValues = true // Use Kotlin defaults instead of DI (default: true) unsafeDslChecks = true // Validate create() usage (default: true) } ``` ## Core Concepts ### Defining Dependencies (DSL) ```kotlin import org.koin.dsl.module val appModule = module { single() // Singleton — auto-wired by compiler plugin factory() // New instance each time viewModel() // Android ViewModel worker() // WorkManager Worker } ``` ### Defining Dependencies (Annotations) ```kotlin import org.koin.core.annotation.Singleton import org.koin.core.annotation.Factory import org.koin.core.annotation.KoinViewModel import org.koin.core.annotation.Module import org.koin.core.annotation.ComponentScan @Singleton class MyService @Factory class MyRepository(private val service: MyService) @KoinViewModel class MyViewModel(private val repo: MyRepository) : ViewModel() @Module @ComponentScan("com.myapp") class AppModule ``` ### Annotated Top-Level Functions ```kotlin import org.koin.core.annotation.Singleton import org.koin.core.annotation.Factory import org.koin.core.annotation.Named @Singleton fun provideDatabase(): DatabaseService = PostgresDatabase() @Factory fun provideCache(db: DatabaseService): CacheService = RedisCache(db) @Singleton @Named("http") fun provideHttpClient(): NetworkClient = OkHttpClient() ``` Discovered by `@ComponentScan` and validated at compile time like class definitions. ### Module Functions (Provider Functions — for wrapping external libraries) ```kotlin import org.koin.core.annotation.Module import org.koin.core.annotation.Singleton import org.koin.core.annotation.Configuration @Module internal object DatabaseModule { @Singleton fun providesDatabase(context: Context): AppDatabase = Room.databaseBuilder(context, AppDatabase::class.java, "my-db").build() } @Module(includes = [DatabaseModule::class]) @Configuration class DaosModule { @Singleton fun providesTopicDao(database: AppDatabase): TopicDao = database.topicDao() } ``` This is the pattern for wrapping Room, Retrofit, OkHttp, and other external libraries you can't annotate directly. ### DSL with Function References ```kotlin fun buildDatabase(context: Context): DatabaseService = PostgresDatabase(context) fun buildCache(db: DatabaseService): CacheService = RedisCache(db) val appModule = module { single { create(::buildDatabase) } // Function builder — validated single { create(::buildCache) } // Parameters auto-resolved } ``` ### Starting Koin (DSL — Android) ```kotlin import org.koin.core.context.startKoin import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.androidx.workmanager.koin.workManagerFactory class MyApplication : Application() { override fun onCreate() { super.onCreate() startKoin { androidLogger(Level.DEBUG) androidContext(this@MyApplication) workManagerFactory() // WorkManager integration modules(appModule) } } } ``` ### Starting Koin (Annotations — with compile-time graph validation) ```kotlin import org.koin.core.annotation.KoinApplication import org.koin.core.context.startKoin @KoinApplication class MyApplication : Application() { override fun onCreate() { super.onCreate() startKoin { // ← triggers A3 full graph validation androidLogger(Level.DEBUG) androidContext(this@MyApplication) workManagerFactory() } } } ``` ### Injecting in Android Activity with Scope ```kotlin import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.scope.activityScope import org.koin.android.scope.AndroidScopeComponent class MainActivity : ComponentActivity(), AndroidScopeComponent { override val scope: Scope by activityScope() private val viewModel: MainActivityViewModel by viewModel() private val tracker: ActivityTracker by inject() // scoped to activity private val networkMonitor: NetworkMonitor by inject() } ``` ### Injecting in Compose ```kotlin import org.koin.compose.viewmodel.koinViewModel // Simple usage @Composable fun HomeScreen(viewModel: HomeViewModel = koinViewModel()) { // ... } // With injected parameters (e.g., navigation argument) @Composable fun DetailScreen(newsId: String, viewModel: DetailViewModel = koinViewModel(key = newsId) { parametersOf(newsId) }) { // ... } ``` ### ViewModel with SavedStateHandle SavedStateHandle is automatically injected — no `@Provided` needed (it's in the whitelist): ```kotlin // Annotations @KoinViewModel class HomeViewModel( private val savedStateHandle: SavedStateHandle, // auto-injected private val userRepository: UserRepository, ) : ViewModel() // DSL viewModel() // SavedStateHandle resolved automatically ``` ### ViewModel with @InjectedParam (Navigation Arguments) ```kotlin // Annotations @KoinViewModel class DetailViewModel( private val newsRepository: NewsRepository, @InjectedParam val newsId: String, // passed via parametersOf() ) : ViewModel() // DSL — use default value for safety class DetailViewModel( private val newsRepository: NewsRepository, @InjectedParam val newsId: String = "", // default value allowed in DSL ) : ViewModel() // Compose call site: koinViewModel(key = newsId) { parametersOf(newsId) } ``` ### Qualifiers ```kotlin import org.koin.core.annotation.Named import org.koin.core.annotation.Qualifier // String qualifier @Singleton class ProdDatabase(@Named("prod") val config: DbConfig) // In DSL val appModule = module { single(named("prod")) { ProdDbConfig() } single(named("test")) { TestDbConfig() } } ``` ### Scopes ```kotlin import org.koin.core.annotation.Scoped import org.koin.core.annotation.Scope // Annotations @Scope(UserSession::class) @Scoped class UserRepository(val db: Database) // DSL val appModule = module { scope { scoped() } } ``` ### Interface Binding ```kotlin // Annotations — automatic binding to implemented interfaces @Singleton class UserRepositoryImpl(val db: Database) : UserRepository // DSL — explicit bind val appModule = module { single() bind UserRepository::class } ``` ### Injected Parameters ```kotlin import org.koin.core.annotation.InjectedParam @Factory class UserDetailViewModel(@InjectedParam val userId: String, val repo: UserRepository) : ViewModel() // Usage: val viewModel: UserDetailViewModel = koinViewModel { parametersOf("user-123") } ``` ### External Dependencies: @Provided ```kotlin import org.koin.core.annotation.Provided @Singleton class MyViewModel(@Provided val handle: SavedStateHandle) // SavedStateHandle is provided by Android framework — skipped during validation ``` ### Module Organization with @Configuration ```kotlin import org.koin.core.annotation.Configuration @Module @ComponentScan("com.myapp.core") @Configuration("prod") class CoreModule @Module @ComponentScan("com.myapp.feature") @Configuration("prod") class FeatureModule // can see CoreModule's definitions (same "prod" label) ``` ### Module Composition (DSL — Real-World Pattern) ```kotlin import org.koin.dsl.module import org.koin.dsl.bind import org.koin.plugin.module.dsl.* import org.koin.androidx.scope.dsl.activityScope // Layer modules val databaseModule = module { single { create(::database) } single { create(::topicDao) } } val networkModule = module { includes(dispatchersModule) single { create(::json) } single() single() bind NetworkDataSource::class } val dataModule = module { includes(databaseModule, dataStoreModule, networkModule) single() bind NewsRepository::class single() bind UserDataRepository::class } // App module — composes everything val appModule = module { includes(dispatchersModule, databaseModule, dataStoreModule, networkModule, dataModule, syncModule) factory() viewModel() viewModel() viewModel() activityScope { scoped() } } ``` ### Worker (WorkManager Integration) ```kotlin // DSL val syncModule = module { single() bind SyncManager::class worker() } // Annotations @KoinWorker class SyncWorker( context: Context, params: WorkerParameters, private val topicsRepository: TopicsRepository, @Dispatcher(NiaDispatchers.IO) private val ioDispatcher: CoroutineDispatcher, ) : CoroutineWorker(context, params) ``` Requires `workManagerFactory()` in `startKoin { }` block. ### Dynamic Modules ```kotlin import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules // Load at runtime (e.g., feature flags, A/B testing) loadKoinModules(premiumModule) // Unload when no longer needed unloadKoinModules(premiumModule) ``` ## Compile-Time Safety The Koin Compiler Plugin validates your dependency graph at three levels during compilation: - **A2 — Per-Module (early feedback):** validates definitions against visible scope — catches missing deps, qualifier mismatches, cross-scope violations per module - **A3 — Full Graph (complete guarantee):** at `startKoin()`, validates ALL definitions from ALL modules combined — catches cross-module missing dependencies - **A4 — Call-Site Validation:** validates every `koinViewModel()`, `get()`, `inject()` call with exact file, line, and column — works across modules via call-site hints Works with both DSL and Annotations. Replaces `verify()` and `checkModules()` runtime tools. If it compiles, it works. **Common error format:** ``` [Koin] Missing dependency: Repository required by: Service (parameter 'repo') in module: ServiceModule ``` ## Annotations Quick Reference | Annotation | Import | Effect | |------------|--------|--------| | `@Singleton` / `@Single` | `org.koin.core.annotation.Singleton` | Generates `single { }` definition | | `@Factory` | `org.koin.core.annotation.Factory` | Generates `factory { }` definition | | `@KoinViewModel` | `org.koin.core.annotation.KoinViewModel` | Generates `viewModel { }` definition | | `@KoinWorker` | `org.koin.core.annotation.KoinWorker` | Generates `worker { }` definition | | `@Scoped` | `org.koin.core.annotation.Scoped` | Generates `scoped { }` definition | | `@Module` | `org.koin.core.annotation.Module` | Marks class as Koin module container | | `@ComponentScan` | `org.koin.core.annotation.ComponentScan` | Scans package for annotated classes/functions | | `@Configuration` | `org.koin.core.annotation.Configuration` | Groups modules for auto-discovery | | `@KoinApplication` | `org.koin.core.annotation.KoinApplication` | Declares modules for `startKoin()` | | `@Named` | `org.koin.core.annotation.Named` | String qualifier | | `@Qualifier` | `org.koin.core.annotation.Qualifier` | String or type qualifier | | `@InjectedParam` | `org.koin.core.annotation.InjectedParam` | Runtime parameter via `parametersOf()` | | `@Property` | `org.koin.core.annotation.Property` | Injects property value | | `@Provided` | `org.koin.core.annotation.Provided` | Marks type as externally provided (skips validation) | | `@Scope` | `org.koin.core.annotation.Scope` | Declares scope for definition | **JSR-330 compatibility:** Koin also supports `jakarta.inject.Singleton`, `jakarta.inject.Named`, `jakarta.inject.Inject`, and `jakarta.inject.Qualifier` — useful when migrating from Hilt/Dagger. Add `koin-jsr330` dependency. Both Koin and Jakarta annotations can be mixed in the same project. **Note:** `@Singleton` is preferred over `@Single` in annotations (standard naming). Both work identically. ## DSL Quick Reference | Function | Context | Effect | |----------|---------|--------| | `single()` | `Module` | Singleton — auto-wired by compiler plugin | | `factory()` | `Module` | New instance each time | | `viewModel()` | `Module` | Android ViewModel | | `worker()` | `Module` | WorkManager Worker | | `scoped()` | `scope { }` | Scoped to lifecycle | | `create(::function)` | `Module` / `scope { }` | Calls function reference, auto-wires params | | `named("x")` | qualifier | String qualifier | | `bind Interface::class` | after definition | Binds to additional type | | `includes(otherModule)` | `Module` | Includes another module | ## Reference Applications (Playground) Two production-quality apps demonstrate the same multi-module Android application built with both approaches: - **app-annotations/** — `@Singleton`, `@Module`, `@ComponentScan`, `@Configuration`, `@KoinApplication`, `@KoinViewModel`, `@KoinWorker`, custom qualifiers - **app-dsl/** — `single()`, `factory()`, `viewModel()`, `worker()`, `create(::T)`, scope blocks Both cover: multi-module architecture (core/feature/sync modules), Compose UI, Room database, WorkManager, DataStore, custom qualifiers (`@Dispatcher`), interface binding, scoped injection. Repository: https://github.com/InsertKoinIO/playground-apps ## Documentation Links ### Setup - [Gradle Setup](https://insert-koin.io/docs/setup/gradle) - [Compiler Plugin Setup](https://insert-koin.io/docs/setup/compiler-plugin) ### Core API - [Starting Koin](https://insert-koin.io/docs/reference/koin-core/starting-koin) - [Modules](https://insert-koin.io/docs/reference/koin-core/modules) - [Definitions](https://insert-koin.io/docs/reference/koin-core/definitions) - [Qualifiers](https://insert-koin.io/docs/reference/koin-core/qualifiers) - [Injection](https://insert-koin.io/docs/reference/koin-core/injection) - [Scopes](https://insert-koin.io/docs/reference/koin-core/scopes) ### Compiler Plugin - [Compile-Time Safety](https://insert-koin.io/docs/reference/koin-compiler/compile-safety) - [Compiler Plugin Options](https://insert-koin.io/docs/reference/koin-annotations/options) ### Annotations - [Getting Started](https://insert-koin.io/docs/reference/koin-annotations/start) - [Definitions](https://insert-koin.io/docs/reference/koin-annotations/definitions) - [Modules](https://insert-koin.io/docs/reference/koin-annotations/modules) - [Annotations Inventory](https://insert-koin.io/docs/reference/koin-annotations/annotations-inventory) ### Android - [Android Setup](https://insert-koin.io/docs/reference/koin-android/start) - [ViewModel](https://insert-koin.io/docs/reference/koin-android/viewmodel) - [Compose Integration](https://insert-koin.io/docs/reference/koin-compose/compose) - [Compose ViewModel](https://insert-koin.io/docs/reference/koin-compose/compose-viewmodel) - [WorkManager](https://insert-koin.io/docs/reference/koin-android/workmanager) - [Multi-Module](https://insert-koin.io/docs/reference/koin-android/multi-module) ### Ktor - [Ktor Integration](https://insert-koin.io/docs/reference/koin-ktor/ktor) ### Testing - [Unit Testing](https://insert-koin.io/docs/reference/koin-test/testing) - [Verification](https://insert-koin.io/docs/reference/koin-test/verify) (now replaced by Compiler Plugin) ### Migration - [Hilt to Koin](https://insert-koin.io/docs/reference/koin-android/hilt-migration) - [KSP to Compiler Plugin](https://insert-koin.io/docs/migration/from-ksp-to-compiler-plugin) ## Koin vs Hilt/Dagger/Metro | Aspect | Koin | Hilt/Dagger/Metro | |--------|------|-------------------| | Setup | `startKoin { }` | `@HiltAndroidApp` + Gradle plugin + kapt/KSP | | Singleton | `single()` or `@Singleton` | `@Singleton @Inject constructor()` | | ViewModel | `viewModel()` or `@KoinViewModel` | `@HiltViewModel @Inject constructor()` | | Multiplatform | Full KMP — all targets, no caveats | Android only (Hilt/Dagger), KMP with caveats (Metro) | | Core approach | No codegen, no reflection — pure Kotlin | Code generation required | | Compiler Plugin | Optional — compile-time safety for DSL and Annotations | Required for all usage | | Call-site validation | Yes — every get(), inject(), koinViewModel() at compile time | No (relies on typed graph accessors) | | Resolution | O(1) HashMap lookup, no reflection | O(1) field access on generated graph | | Dynamic modules | Yes — load/unload at runtime | No | | Annotations to learn | ~20 (7 to get started, each independent) | ~45 (interconnected) | | Learning curve | Minutes | Hours to days |