Skip to main content
Version: 4.2

Koin Compiler Plugin

The Koin Compiler Plugin is the recommended approach for all new Kotlin 2.x projects. It's a native Kotlin compiler plugin that powers both DSL and Annotations with auto-wiring, compile-time safety, and cleaner syntax.

What is the Compiler Plugin?

The Koin Compiler Plugin is a native Kotlin Compiler Plugin (K2) - not KSP or annotation processing. It integrates directly with the Kotlin compiler to:

  • Auto-detect constructor parameters - No manual get() calls needed
  • Transform code at compile-time - Errors caught during build
  • Work with both DSL and Annotations - Your choice of style
  • Generate no visible files - Cleaner project structure

Why Use the Compiler Plugin?

1. Safer Code

The plugin auto-detects constructor dependencies, reducing manual wiring errors:

// Without Compiler Plugin - easy to make mistakes
val appModule = module {
single { UserService(get(), get(), get()) } // Hope you got the order right!
}

// With Compiler Plugin - auto-wired
val appModule = module {
single<UserService>() // Plugin detects all constructor parameters
}

2. Cleaner Syntax

Less boilerplate, more readable:

Classic DSLCompiler Plugin DSL
singleOf(::MyService)single<MyService>()
single { MyService(get(), get()) }single<MyService>()
factoryOf(::MyRepo)factory<MyRepo>()
viewModelOf(::MyVM)viewModel<MyVM>()
scopedOf(::MyPresenter)scoped<MyPresenter>()
workerOf(::MyWorker)worker<MyWorker>()

3. Compile-Time Analysis

The plugin analyzes your code during compilation:

  • Validates constructor parameters
  • Detects missing dependencies (verification coming soon)
  • Catches errors before runtime

4. DSL & Annotations - Both Equally Powerful

Use whichever style you prefer - the same plugin powers both with identical capabilities:

DSL Style:

val appModule = module {
single<Database>()
single<UserRepository>()
viewModel<UserViewModel>()
}

Annotation Style:

@Singleton
class Database

@Singleton
class UserRepository(private val database: Database)

@KoinViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel()

Getting Started

Setup

Add the Compiler Plugin to your project:

// settings.gradle.kts
plugins {
id("io.insert-koin.compiler.plugin") version "1.0.0" apply false
}

// app/build.gradle.kts
plugins {
id("io.insert-koin.compiler.plugin")
}

Using the Compiler Plugin DSL

Import from the compiler plugin package:

import org.koin.plugin.module.dsl.*
import org.koin.dsl.module

val appModule = module {
single<Database>()
single<ApiClient>()
single<UserRepository>()
viewModel<UserViewModel>()
}
note

The Compiler Plugin DSL is in org.koin.plugin.module.dsl. Classic DSL remains in org.koin.dsl.

Using Annotations

Annotations work the same as before:

@Singleton
class Database

@Singleton
class ApiClient

@Singleton
class UserRepository(
private val database: Database,
private val apiClient: ApiClient
)

@KoinViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel()

@Module
@ComponentScan("com.myapp")
class AppModule

How It Works

The Compiler Plugin operates in two phases:

1. FIR Phase (Analysis)

During the Frontend Intermediate Representation phase, the plugin:

  • Analyzes your module definitions
  • Detects constructor parameters
  • Validates dependency declarations

2. IR Phase (Transformation)

During the Intermediate Representation phase, the plugin:

  • Generates proper get() calls for each parameter
  • Handles qualifiers (@Named)
  • Handles injected parameters (@InjectedParam)
  • Handles nullable and Lazy types

What Gets Generated

When you write:

single<UserRepository>()

The plugin transforms it to:

single { UserRepository(get(), get()) }  // Parameters auto-detected

For more complex cases:

// Your code
@Singleton
class MyService(
val required: RequiredDep,
val optional: OptionalDep?,
@Named("special") val named: NamedDep,
val lazy: Lazy<LazyDep>,
@InjectedParam val param: String
)

The plugin generates proper handling for each parameter type:

  • Required: get()
  • Optional: getOrNull()
  • Named: get(named("special"))
  • Lazy: inject()
  • InjectedParam: params.get()

Compiler Plugin DSL Reference

Definition Types

import org.koin.plugin.module.dsl.*

val appModule = module {
// Singleton - one instance
single<MyService>()

// Factory - new instance each time
factory<MyPresenter>()

// Scoped - instance per scope
scope<MyActivity> {
scoped<ActivityPresenter>()
}

// ViewModel
viewModel<MyViewModel>()

// Worker (Android WorkManager)
worker<MyWorker>()
}

With Qualifiers

val appModule = module {
single<Database>(named("local"))
single<Database>(named("remote"))
}

With Parameters

Parameters passed at injection time:

class UserPresenter(
@InjectedParam val userId: String,
val repository: UserRepository // Auto-injected
)

val appModule = module {
factory<UserPresenter>()
}

// Usage
val presenter: UserPresenter = get { parametersOf("user123") }

Interface Binding

val appModule = module {
single<UserRepositoryImpl>() bind UserRepository::class

// Or multiple bindings
single<MyServiceImpl>() binds arrayOf(
ServiceA::class,
ServiceB::class
)
}

Annotations Reference

Definition Annotations

AnnotationDescription
@Singleton / @SingleSingle instance
@FactoryNew instance each time
@ScopedInstance per scope
@KoinViewModelAndroid ViewModel
@KoinWorkerAndroid WorkManager Worker

Parameter Annotations

AnnotationDescription
@Named("qualifier")Named qualifier
@InjectedParamRuntime parameter (via parametersOf())
@Property("key")Koin property value
@ProvidedExternal dependency (skip validation)

Module Annotations

AnnotationDescription
@ModuleDeclares a Koin module
@ComponentScan("package")Scan package for annotated classes
@ConfigurationAuto-discovered module

Comparison: Three Approaches

┌─────────────────────────────────────────────────────────────────────────┐
│ ⭐ COMPILER PLUGIN DSL (Recommended)
│ Package: org.koin.plugin.module.dsl │
│ ───────────────────────────────────────────────────────────────────── │
│ single<MyService>()
│ factory<MyRepo>()
│ viewModel<MyVM>()
│ │
│ ✓ Auto-detects dependencies │
│ ✓ Compile-time analysis │
│ ✓ Cleanest syntax │
├─────────────────────────────────────────────────────────────────────────┤
│ ✅ CLASSIC DSL (Fully Supported)
│ Package: org.koin.dsl │
│ ───────────────────────────────────────────────────────────────────── │
singleOf(::MyService)
│ single { MyService(get(), get()) }
viewModelOf(::MyVM)
│ │
│ ✓ Works with any Kotlin version │
│ ✓ Full control over wiring │
│ ✓ Can migrate to Plugin DSL when ready │
├─────────────────────────────────────────────────────────────────────────┤
│ ⚠️ KSP ANNOTATIONS (Deprecated)
│ ───────────────────────────────────────────────────────────────────── │
│ Uses koin-ksp-compiler │
│ Same annotations as Compiler Plugin │
│ │
│ ⚠ Migrate to Compiler Plugin │
│ ⚠ Will be removed in future version │
└─────────────────────────────────────────────────────────────────────────┘

Migration

From Classic DSL

If you're using classic DSL, migration is optional but recommended:

  1. Add the Compiler Plugin to Gradle
  2. Update imports to org.koin.plugin.module.dsl.*
  3. Replace singleOf(::Class) with single<Class>()
  4. Remove manual get() calls

See Migrating from DSL to Compiler Plugin.

From KSP Annotations

If you're using KSP, migration is recommended now:

  1. Update Kotlin to 2.x
  2. Replace koin-ksp-compiler with Compiler Plugin
  3. Your annotations stay the same - no code changes!
  4. Delete generated files

See Migrating from KSP to Compiler Plugin.

Requirements

  • Kotlin 2.x (K2 compiler)
  • Gradle 8.x+

Configuration Options

// build.gradle.kts
koinCompiler {
// Options will be documented here
}

Classic DSL: Still Fully Supported

The Compiler Plugin doesn't replace Classic DSL - it adds analysis and generation on top. Classic DSL remains fully supported:

// Still works perfectly
val appModule = module {
singleOf(::Database)
singleOf(::ApiClient)
single { CustomService(get(), get(), configValue) } // Custom logic
viewModelOf(::UserViewModel)
}

Use Classic DSL when you need:

  • Custom factory logic
  • getOrNull() for optional dependencies
  • Conditional instantiation
  • Backward compatibility with Kotlin 1.x

Next Steps