Koin

Koin: A Lightweight Kotlin-First Alternative

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:

koin.main.kts: a Koin module, resolved at run timeKotlin
@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}")
}
Output
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.