koin Agent Skills & AI Rules

GitHub

Koin - pragmatic dependency injection for Kotlin & Kotlin Multiplatform. Koin DSL or Koin Annotations (JSR-330), compile safe with the Koin Compiler Plugin.

RAW Rules
2 Discovered Rules β€’ ~6,296 Estimated Tokens
Quick:
Loading skill content...

projects - LLM Index & Context Digest

projects/llms.txt root-instruction ~4,185 tokens
Raw
# Koin β€” Kotlin Dependency Injection Framework

> Pragmatic DI framework for Kotlin and Kotlin Multiplatform. Two styles, one container: Koin DSL or Koin Annotations (JSR-330 compatible). Compile safe in both styles with the Koin Compiler Plugin β€” no KSP, no kapt, no reflection at its core. Runs on all Kotlin Multiplatform targets.

Concise index: [llms.txt](https://github.com/InsertKoinIO/koin/blob/main/llms.txt). This file is the API cheat sheet.

## Key Facts

- **Version**: Koin 4.2.2, Koin Compiler Plugin 1.2.1, Kotlin 2.3.20 or later
- **Language**: Kotlin (100%)
- **Platforms**: Android, iOS, Desktop, Web (JS/Wasm), Backend (Ktor) via Kotlin Multiplatform
- **License**: Apache 2.0
- **Repository**: https://github.com/InsertKoinIO/koin
- **Website**: https://insert-koin.io
- **Documentation**: https://insert-koin.io/docs/intro/what-is-koin
- **Koin skills (AI agents)**: https://github.com/InsertKoinIO/koin-skills
- **Kotzilla MCP (runtime debugging)**: https://mcp.kotzilla.io/mcp

## IDE Plugin

Official plugin for Android Studio & IntelliJ IDEA β€” code navigation between definitions and injection points, live safety checks, dependency graph visualization.

**Download:** https://plugins.jetbrains.com/plugin/26131-koin-dependency-injection-official-

## Gradle Dependencies

```kotlin
// BOM (recommended)
plugins {
    id("io.insert-koin.compiler.plugin") version "1.2.1"     // Koin Compiler Plugin (recommended β€” compile-time safety)
}

implementation(platform("io.insert-koin:koin-bom:4.2.2"))   // BOM resolves every Koin artifact version
implementation("io.insert-koin:koin-core")                   // Core (all platforms)
implementation("io.insert-koin:koin-annotations")            // Annotations style (with the Compiler Plugin)
implementation("io.insert-koin:koin-android")                // Android (Context, ViewModel, Scope)
implementation("io.insert-koin:koin-compose")                // Compose integration
implementation("io.insert-koin:koin-compose-viewmodel")      // Compose ViewModel (koinViewModel)
implementation("io.insert-koin:koin-core-coroutines")        // Coroutines (lazy modules)
implementation("io.insert-koin:koin-ktor")                   // Ktor server
implementation("io.insert-koin:koin-test")                   // Testing
implementation("io.insert-koin:koin-test-junit5")            // JUnit 5 testing

// Never Koin KSP: no com.google.devtools.ksp for Koin, no koin-ksp-compiler, no koin-annotations:2.x pin
```

---

## Starting Koin

### Android

```kotlin
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
import org.koin.androidx.workmanager.koin.workManagerFactory

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidLogger()
            androidContext(this@MyApplication)
            workManagerFactory()              // if using WorkManager
            modules(appModule, featureModule)
        }
    }
}
```

### Annotations (with compile-time graph validation)

```kotlin
import org.koin.core.annotation.KoinApplication
import org.koin.plugin.module.dsl.startKoin   // reified startKoin<App> from the Compiler Plugin

@KoinApplication
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin<MyApplication> {    // triggers A3 full graph validation
            androidLogger()
            androidContext(this@MyApplication)
            workManagerFactory()
        }
    }
}
```

### Ktor

```kotlin
import org.koin.ktor.plugin.Koin

fun Application.module() {
    install(Koin) {
        modules(appModule)
    }
}
```

### KMP / Non-Android

```kotlin
import org.koin.core.context.startKoin

startKoin {
    modules(commonModule, platformModule)
}
```

---

## Defining Dependencies

### Compiler Plugin DSL (recommended)

Pick one style per project β€” Koin DSL or Koin Annotations β€” and don't mix them unless needed (then one style per module).

```kotlin
import org.koin.dsl.module
import org.koin.dsl.bind
import org.koin.plugin.module.dsl.*

val appModule = module {
    // Singleton β€” one instance for app lifetime
    single<Database>()
    single<UserRepository>()

    // Factory β€” new instance each time
    factory<GetUserUseCase>()

    // ViewModel β€” Android ViewModel lifecycle
    viewModel<UserViewModel>()

    // Worker β€” WorkManager
    worker<SyncWorker>()

    // Interface binding
    single<UserRepositoryImpl>() bind UserRepository::class

    // Multiple bindings
    single<MyServiceImpl>() binds arrayOf(ServiceA::class, ServiceB::class)

    // Module composition
    includes(networkModule, databaseModule)
}
```

### Compiler Plugin DSL with Function Builders (create)

```kotlin
import org.koin.dsl.module
import org.koin.plugin.module.dsl.create

// Builder functions β€” for wrapping external libraries (Room, Retrofit, etc.)
fun database(context: Context): AppDatabase =
    Room.databaseBuilder(context, AppDatabase::class.java, "my-db").build()

fun topicDao(db: AppDatabase): TopicDao = db.topicDao()

fun json(): Json = Json { ignoreUnknownKeys = true }

val databaseModule = module {
    single { create(::database) }    // parameters auto-resolved from DI
    single { create(::topicDao) }
    single { create(::json) }
}
```

### Constructor DSL and Classic DSL

Constructor DSL (`singleOf(::T)`) suits projects without the Compiler Plugin. Classic DSL (`single { T(get()) }`) is for custom expressions a constructor or function builder can't express. Both are also checked by the Compiler Plugin when it is applied.

```kotlin
import org.koin.dsl.module
import org.koin.core.module.dsl.*

val appModule = module {
    // Constructor DSL β€” constructor reference
    singleOf(::Database)
    singleOf(::UserRepository)
    factoryOf(::GetUserUseCase)
    viewModelOf(::UserViewModel)

    // Classic DSL β€” lambda (manual wiring)
    single { Database() }
    single { UserRepository(get()) }
    factory { GetUserUseCase(get()) }
    viewModel { UserViewModel(get()) }
}
```

### Annotations

```kotlin
import org.koin.core.annotation.*

@Singleton
class Database

@Singleton
class UserRepository(private val db: Database)

@Factory
class GetUserUseCase(private val repo: UserRepository)

@KoinViewModel
class UserViewModel(private val useCase: GetUserUseCase) : ViewModel()

// Module with component scan
@Module
@ComponentScan("com.myapp")
class AppModule

// Module with provider functions (for external libraries)
@Module
internal object DatabaseModule {
    @Singleton
    fun providesDatabase(context: Context): AppDatabase =
        Room.databaseBuilder(context, AppDatabase::class.java, "my-db").build()
}
```

### Annotated Top-Level Functions

```kotlin
import org.koin.core.annotation.Singleton
import org.koin.core.annotation.Factory

@Singleton
fun provideDatabase(context: Context): AppDatabase =
    Room.databaseBuilder(context, AppDatabase::class.java, "my-db").build()

@Factory
fun provideCache(db: AppDatabase): CacheService = CacheServiceImpl(db)
```

Discovered by `@ComponentScan` like annotated classes.

---

## Injecting Dependencies

### In Android Activity / Fragment

```kotlin
import org.koin.android.ext.android.inject
import org.koin.android.ext.android.get
import org.koin.androidx.viewmodel.ext.android.viewModel

class MainActivity : ComponentActivity() {
    // Lazy injection
    private val viewModel: UserViewModel by viewModel()
    private val service: MyService by inject()

    // Eager injection
    val eagerService: MyService = get()
}
```

### In Activity with Scope

```kotlin
import org.koin.android.scope.AndroidScopeComponent
import org.koin.androidx.scope.activityScope
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel

class MainActivity : ComponentActivity(), AndroidScopeComponent {
    override val scope: Scope by activityScope()

    private val viewModel: MainViewModel by viewModel()
    private val tracker: ActivityTracker by inject()   // scoped to activity lifecycle
}
```

### In Compose

```kotlin
import org.koin.compose.viewmodel.koinViewModel
import org.koin.compose.koinInject

@Composable
fun HomeScreen(viewModel: HomeViewModel = koinViewModel()) {
    val analytics: AnalyticsHelper = koinInject()
    // ...
}
```

### In Compose with Parameters (Navigation Arguments)

```kotlin
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.parameter.parametersOf

@Composable
fun DetailScreen(
    newsId: String,
    viewModel: DetailViewModel = koinViewModel(key = newsId) { parametersOf(newsId) }
) {
    // Each unique newsId gets its own ViewModel instance
}
```

### In Ktor

```kotlin
import org.koin.ktor.ext.inject

fun Route.userRoutes() {
    val service: UserService by inject()

    get("/users/{id}") {
        val user = service.getUser(call.parameters["id"]!!)
        call.respond(user)
    }
}
```

### Constructor Injection (Recommended for Business Logic)

```kotlin
// No Koin imports needed β€” dependencies resolved by the framework
class UserRepository(
    private val database: Database,
    private val networkClient: NetworkClient
)

class GetUserUseCase(
    private val repository: UserRepository
)
```

---

## Qualifiers

### @Named (String Qualifier)

```kotlin
import org.koin.core.annotation.Named
import org.koin.core.qualifier.named

// Definition
@Singleton @Named("local")
class LocalDatabase : Database

@Singleton @Named("remote")
class RemoteDatabase : Database

// Injection
@Singleton
class UserRepository(
    @Named("local") private val localDb: Database,
    @Named("remote") private val remoteDb: Database
)

// Compiler Plugin DSL β€” the qualifier stays on the class (@Named above), the definition stays plain
single<LocalDatabase>()
single<RemoteDatabase>()

// Classic DSL only
single<Database>(named("local")) { LocalDatabase() }
```

### Custom @Qualifier (with Parameters)

```kotlin
import org.koin.core.annotation.Qualifier

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class Dispatcher(val type: AppDispatchers)

enum class AppDispatchers { Default, IO }

// Provider
@Singleton @Dispatcher(AppDispatchers.IO)
fun ioDispatcher(): CoroutineDispatcher = Dispatchers.IO

// Consumer
@Singleton
class MyRepository(
    @Dispatcher(AppDispatchers.IO) private val dispatcher: CoroutineDispatcher
)
```

---

## Scopes

### DSL Scopes

```kotlin
import org.koin.dsl.module
import org.koin.plugin.module.dsl.scoped
import org.koin.androidx.scope.dsl.activityScope
import org.koin.androidx.scope.dsl.fragmentScope

val appModule = module {
    // Activity-scoped
    activityScope {
        scoped<ActivityTracker>()
    }

    // Fragment-scoped
    fragmentScope {
        scoped<FragmentPresenter>()
    }

    // Custom scope
    scope<UserSession> {
        scoped<UserRepository>()
        scoped<UserPreferences>()
    }
}
```

### Annotation Scopes

```kotlin
import org.koin.core.annotation.Scoped
import org.koin.core.annotation.Scope
import org.koin.android.annotation.ActivityScope
import org.koin.android.annotation.FragmentScope

@ActivityScope
class ActivityTracker(val activity: ComponentActivity)

@FragmentScope
class FragmentPresenter(val fragment: Fragment)

@Scope(UserSession::class)
@Scoped
class UserRepository(val db: Database)
```

---

## ViewModel

### With SavedStateHandle

```kotlin
// SavedStateHandle is auto-injected (whitelisted type)
@KoinViewModel
class HomeViewModel(
    private val savedStateHandle: SavedStateHandle,
    private val repository: UserRepository,
) : ViewModel()

// DSL
viewModel<HomeViewModel>()
```

### With @InjectedParam (Navigation Arguments)

```kotlin
import org.koin.core.annotation.InjectedParam

@KoinViewModel
class DetailViewModel(
    private val repository: NewsRepository,
    @InjectedParam val newsId: String,
) : ViewModel()

// Compose usage:
koinViewModel(key = newsId) { parametersOf(newsId) }
```

---

## WorkManager

```kotlin
import org.koin.android.annotation.KoinWorker

// Annotations
@KoinWorker
class SyncWorker(
    context: Context,
    params: WorkerParameters,
    private val repository: NewsRepository,
    @Dispatcher(AppDispatchers.IO) private val dispatcher: CoroutineDispatcher,
) : CoroutineWorker(context, params)

// DSL
val syncModule = module {
    single<WorkManagerSyncManager>() bind SyncManager::class
    worker<SyncWorker>()
}

// Bootstrap β€” add workManagerFactory()
startKoin {
    workManagerFactory()
    modules(appModule)
}
```

---

## Module Organization

### Annotations β€” Auto-Discovery

```kotlin
import org.koin.core.annotation.*

// Modules with @Configuration are auto-discovered by startKoin<T>
@Module @ComponentScan("com.myapp.core") @Configuration
class CoreModule

@Module @ComponentScan("com.myapp.feature") @Configuration
class FeatureModule

@Module(includes = [DatabaseModule::class]) @Configuration
class DataModule

// App entry point β€” assembles all @Configuration modules
@KoinApplication
class MyApplication : Application()
```

### DSL β€” Explicit Composition

```kotlin
val coreModule = module { single<Database>(); single<NetworkClient>() }
val featureModule = module { includes(coreModule); viewModel<HomeViewModel>() }

val appModule = module {
    includes(coreModule, featureModule, syncModule)
}

startKoin { modules(appModule) }
```

### @Configuration Labels (Environment-Specific)

```kotlin
@Module @ComponentScan("com.myapp.core") @Configuration("prod")
class ProdCoreModule

@Module @ComponentScan("com.myapp.core") @Configuration("test")
class TestCoreModule

// Modules with the same label see each other's definitions
```

---

## Dynamic Modules

```kotlin
import org.koin.core.context.loadKoinModules
import org.koin.core.context.unloadKoinModules

// Load at runtime (feature flags, A/B testing)
loadKoinModules(premiumModule)

// Unload when no longer needed
unloadKoinModules(premiumModule)
```

---

## Lazy Modules (Background Loading)

```kotlin
import org.koin.core.context.startKoin
import org.koin.core.lazyModules

startKoin {
    modules(criticalModule)
    lazyModules(heavyModule, analyticsModule)  // loaded in background
}
```

---

## Properties

```kotlin
import org.koin.core.annotation.Property

@Singleton
class ApiClient(
    @Property("api_url") val url: String,
    @Property("api_key") val key: String
)

// Provide properties
startKoin {
    properties(mapOf("api_url" to "https://api.example.com", "api_key" to "secret"))
}

// DSL
single { ApiClient(getProperty("api_url"), getProperty("api_key")) }
```

---

## Optional / Nullable Dependencies

```kotlin
// Nullable param β†’ resolved with getOrNull()
@Singleton
class MyService(
    val required: RequiredDep,
    val optional: OptionalDep?     // null if not in graph
)

// DSL
single<MyService>()  // compiler plugin handles getOrNull() automatically
```

---

## Lazy Injection

```kotlin
// Lazy<T> β†’ deferred creation
@Singleton
class MyService(
    val heavy: Lazy<HeavyDependency>   // created on first access
)
```

---

## List Injection

```kotlin
// List<T> β†’ collects all definitions of type T
@Singleton
class LoggerAggregator(
    val loggers: List<LoggerDataSource>   // all LoggerDataSource definitions
)
```

---

## Testing

```kotlin
import org.koin.test.KoinTest
import org.koin.test.inject

class MyTest : KoinTest {
    private val service: MyService by inject()

    @Test
    fun testService() {
        // service is resolved from Koin
    }
}
```

Graph verification is done at compile time by the Koin Compiler Plugin β€” don't write `checkModules()` / `verify()` tests (on their way to deprecation).

---

## Compile-Time Safety (with Koin Compiler Plugin)

The Koin Compiler Plugin validates your dependency graph at build time:

- **A2 β€” Per-Module:** validates definitions against visible scope per module
- **A3 β€” Full Graph:** validates ALL modules at `startKoin<T>()`
- **A4 β€” Call-Site:** validates every `get<T>()`, `inject<T>()`, `koinViewModel<T>()` call

Works with both DSL and Annotations. Replaces `verify()` / `checkModules()`. If it compiles, it works.

```kotlin
koinCompiler {
    compileSafety = true       // default
    skipDefaultValues = true   // default
    unsafeDslChecks = true     // default
}
```

See https://insert-koin.io/docs/reference/koin-compiler/compile-safety

---

## Architecture

- O(1) resolution via `HashMap.get()` β€” no reflection, no iteration
- `ConcurrentHashMap` (JVM) / `ConcurrentMutableMap` (Native)
- Fully compatible with R8/ProGuard
- Pure common Kotlin runtime β€” all KMP targets

## Project Structure

```
koin/projects/
β”œβ”€β”€ core/      # koin-core (engine), koin-core-viewmodel, koin-core-coroutines, koin-annotations, koin-test*
β”œβ”€β”€ android/   # koin-android, koin-androidx-workmanager, koin-androidx-startup, koin-dagger-bridge…
β”œβ”€β”€ compose/   # koin-compose, koin-compose-viewmodel, koin-compose-navigation3…
β”œβ”€β”€ ktor/      # koin-ktor, koin-logger-slf4j
β”œβ”€β”€ plugins/   # koin-gradle-plugin
└── bom/       # koin-bom
```

Building and contributing: https://github.com/InsertKoinIO/koin/blob/main/AGENTS.md

## Related Repositories

- **Compiler Plugin**: https://github.com/InsertKoinIO/koin-compiler-plugin
- **Koin skills (AI agents)**: https://github.com/InsertKoinIO/koin-skills β€” Claude Code: `/plugin marketplace add InsertKoinIO/koin-skills` then `/plugin install koin@koin-skills`. Other agents (Cursor, Codex, Copilot, Windsurf, Gemini…): `npx skills add InsertKoinIO/koin-skills` (Node.js 22+)
- **Kotzilla MCP**: https://mcp.kotzilla.io/mcp β€” Koin error diagnosis and runtime debugging (graph behavior, scopes, memory, performance)
- **Playground Apps**: https://github.com/InsertKoinIO/playground-apps
- **Website**: https://insert-koin.io