Skip to main content
Version: 4.2

Migrating from KSP to the Compiler Plugin

This guide moves a Koin Annotations project from the KSP processor (koin-ksp-compiler) to the Koin Compiler Plugin. Your annotated classes stay the same: only the Gradle configuration, the way you start Koin, and a few imports change. For what the Compiler Plugin is and how it works, see Koin Compiler Plugin.

What changes​

AspectKSP processorCompiler Plugin
ProcessingKSP, a separate step before compilationInside the Kotlin K2 compiler
Generated filesVisible in build/generated/kspNone, code is generated in the compiler
KMP setupOne KSP configuration per targetOne plugin for all targets
koin-annotations versionSeparate version (koin-ksp)Same version as koin-core
Starting Koinmodules(AppModule().module)startKoin<MyApp>()
Compile-time checksKOIN_CONFIG_CHECK optionOn by default, full graph and call sites
StatusDeprecatedActive development

Requirements​

  • Kotlin 2.3.20 or later (K2 compiler).
  • Gradle 8 or later.
  • Koin 4.2 or later.

Step 1: Update Kotlin​

// build.gradle.kts
plugins {
kotlin("jvm") version "2.3.20"
}

Step 2: Update the version catalog​

Before (KSP):

[versions]
koin = "4.0.0"
koin-ksp = "2.0.0"
ksp = "2.0.0-1.0.22"

[libraries]
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koin-ksp" }
koin-ksp-compiler = { module = "io.insert-koin:koin-ksp-compiler", version.ref = "koin-ksp" }

[plugins]
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }

After (Compiler Plugin):

[versions]
koin = "4.2.0"
koin-plugin = "1.2.1"

[libraries]
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koin" }

[plugins]
koin-compiler = { id = "io.insert-koin.compiler.plugin", version.ref = "koin-plugin" }

koin-annotations is now part of the main Koin project and uses the koin-core version. Check the latest plugin version in Compiler Plugin Setup.

Step 3: Update the build configuration​

Before (KSP):

plugins {
alias(libs.plugins.ksp)
}

dependencies {
implementation(libs.koin.core)
implementation(libs.koin.annotations)
ksp(libs.koin.ksp.compiler)
}

ksp {
arg("KOIN_CONFIG_CHECK", "true")
}

After (Compiler Plugin):

plugins {
alias(libs.plugins.koin.compiler)
}

dependencies {
implementation(libs.koin.core)
implementation(libs.koin.annotations)
}

// optional
koinCompiler {
userLogs = true // log detected components
}

Remove the ksp { } block: compile-time checks are on by default. All plugin options are in Compiler Plugin Options.

Kotlin Multiplatform​

With KSP, each target needs its own processor configuration:

dependencies {
add("kspAndroid", "io.insert-koin:koin-ksp-compiler:$koin_ksp_version")
add("kspIosX64", "io.insert-koin:koin-ksp-compiler:$koin_ksp_version")
add("kspIosArm64", "io.insert-koin:koin-ksp-compiler:$koin_ksp_version")
add("kspIosSimulatorArm64", "io.insert-koin:koin-ksp-compiler:$koin_ksp_version")
}

With the Compiler Plugin, apply the plugin once in the shared module and remove all ksp... entries:

// shared/build.gradle.kts
plugins {
kotlin("multiplatform")
alias(libs.plugins.koin.compiler)
}

kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.koin.core)
implementation(libs.koin.annotations)
}
}
}

Step 4: Update how Koin starts​

KSP generates a .module extension for each @Module class. The Compiler Plugin has no such extension: you declare a @KoinApplication class and start it with a typed API.

Before (KSP):

import org.koin.ksp.generated.*

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

fun main() {
startKoin {
modules(AppModule().module)
}
}

After (Compiler Plugin):

import org.koin.plugin.module.dsl.startKoin

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

@KoinApplication(modules = [AppModule::class])
class MyApp

fun main() {
startKoin<MyApp>()
}

On Android, pass the usual options in the lambda:

@KoinApplication(modules = [AppModule::class])
class MyApp

class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin<MyApp> {
androidContext(this@MyApplication)
}
}
}

The other typed APIs are koinApplication<T>(), koinConfiguration<T>() and withConfiguration<T>() inside an existing startKoin { } or test rule. To load a single module class, use module<AppModule>() inside a plain startKoin { }. See Starting Koin.

Step 5: Update imports​

@KoinViewModel moved to the core annotation package:

// before (KSP)
import org.koin.android.annotation.KoinViewModel

// after (Compiler Plugin)
import org.koin.core.annotation.KoinViewModel

Remove every import org.koin.ksp.generated.*.

Step 6: Clean and rebuild​

Delete the files generated by KSP, then rebuild:

rm -rf build/generated/ksp
./gradlew clean build

The first build runs the full compile-time check. A missing definition that KSP did not report now fails the build: see Compile-Time Safety for the error messages and for @Provided.

What stays the same​

Your annotated classes don't change:

@Singleton
class UserRepository(private val database: Database)

@Factory
class GetUserUseCase(private val repository: UserRepository)

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

Every annotation is listed in the Annotations Reference.

What you can use after migrating​

These features need the Compiler Plugin:

  • Top-level functions: @Singleton fun database(): Database is found by @ComponentScan like a class (see Definitions).
  • Module discovery: modules tagged @Configuration in any Gradle module are loaded by @KoinApplication without listing them.
  • Configuration labels: @Configuration("test") with @KoinApplication(configurations = ["test"]) loads only the modules with that label (see Modules).
  • Compiler Plugin DSL: if you also have DSL modules, you can switch them to the Compiler Plugin DSL:
Constructor / Classic DSLCompiler Plugin DSL
singleOf(::MyService)single<MyService>()
factoryOf(::MyRepository)factory<MyRepository>()
viewModelOf(::MyViewModel)viewModel<MyViewModel>()
scopedOf(::MyScoped)scoped<MyScoped>()
workerOf(::MyWorker)worker<MyWorker>()
single { build(get()) }single { create(::build) }

The Compiler Plugin DSL is in org.koin.plugin.module.dsl. The Constructor DSL stays in org.koin.core.module.dsl and the Classic DSL in org.koin.dsl. Both keep working and are checked by the plugin: you don't have to migrate them.

Troubleshooting​

The build fails after removing KSP​

Stale generated code is still on the classpath. Run ./gradlew clean, delete build/generated/ksp, invalidate the IDE caches, then rebuild.

Annotated classes are not detected​

Turn on plugin logs to see which classes are found:

koinCompiler {
userLogs = true
}

Check that the packages in @ComponentScan match the packages of your classes.

A dependency is missing at runtime​

  • Check that the module is listed in @KoinApplication(modules = [...]), or tagged @Configuration.
  • Check the @ComponentScan packages.
  • For a type created outside Koin, mark it @Provided (see Compile-Time Safety).

Checklist​

  • Update Kotlin to 2.3.20 or later
  • Update Koin to 4.2.0 or later
  • Remove the KSP plugin
  • Remove the koin-ksp-compiler dependency and the ksp { } block
  • Use the main Koin version for koin-annotations (io.insert-koin:koin-annotations:$koin_version)
  • Apply the Koin Compiler Plugin (io.insert-koin.compiler.plugin)
  • Change the @KoinViewModel import to org.koin.core.annotation
  • Create a @KoinApplication class and replace modules(X().module) with startKoin<MyApp>()
  • Remove import org.koin.ksp.generated.*
  • Optional: switch DSL modules to org.koin.plugin.module.dsl.* and single<X>()
  • Clean and rebuild (rm -rf build/generated/ksp && ./gradlew clean build)