Koin (github.com/InsertKoinIO/koin (https://github.com/InsertKoinIO/koin 10,021 ), Apache 2.0, version 4.2.2 of 15 June 2026) takes the opposite approach. There is no code generation: you declare the graph in a Kotlin DSL, and Koin resolves it at run time from a registry of lambdas. It is plain Kotlin Multiplatform 25,990 code, so the same modules run on Android, iOS, desktop, the server, and even a JVM script:
@file:DependsOn("io.insert-koin:koin-core-jvm:4.2.2")
import org.koin.core.context.startKoin
import org.koin.core.module.dsl.*
import org.koin.dsl.module
interface BookRepository { fun titles(): List<String> }
class AssetBookRepository : BookRepository {
init { println("AssetBookRepository created") }
override fun titles() = listOf("Salt and Saffron", "Gardens in Glass")
}
class CatalogPresenter(val repo: BookRepository) // plain constructor injection
class CartStore
val koin = startKoin {
modules(module {
singleOf(::AssetBookRepository) { bind<BookRepository>() } // one instance, by interface
factoryOf(::CatalogPresenter) // a new one per request
})
}.koin
val (a, b) = koin.get<CatalogPresenter>() to koin.get<CatalogPresenter>()
println("same presenter: ${a === b}, same repo: ${a.repo === b.repo}, ${a.repo.titles()}")
try {
koin.get<CartStore>() // never declared
println("unreachable")
} catch (e: Exception) {
println("${e::class.simpleName}: ${e.message}")
}AssetBookRepository created same presenter: false, same repo: true, [Salt and Saffron, Gardens in Glass] NoDefinitionFoundException: No definition found for type 'Koin_main$CartStore' on scope '['_root_']'.. Check or add definition for type 'Koin_main$CartStore' in scope '_root_'.
singleOf registers one shared instance, exposed under the interface by bind; factoryOf builds a new object per request, like Hilt's unscoped default. The last line shows the trade-off: a missing declaration fails only when something asks for it, while the app runs. Koin answers with verify() in a unit test, and 4.2.1 added support for the Koin Compiler Plugin (then 1.0.0-RC1), which moves those checks into the build.
On Android, startKoin { androidContext(this@BookNestApp); modules(appModule) } runs in Application.onCreate(), the module adds viewModelOf(::CatalogViewModel), and koinViewModel() from koin-androidx-compose replaces hiltViewModel(). BookNest keeps Hilt.