You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/COMPILE_TIME_SAFETY.md
+77Lines changed: 77 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -225,6 +225,83 @@ class Other(@Property("missing.key") val value: String)
225
225
// WARNING — no @PropertyValue("missing.key") found
226
226
```
227
227
228
+
## Module Load Order and Overrides
229
+
230
+
Koin is **last-wins** at runtime: when two modules define the same type, the one loaded last takes precedence. The compiler plugin assembles the module list at the `@KoinApplication` root in this order:
// Load order: (@Configuration deps) → A → A.includes → B → B.includes → C → C.includes
260
+
// Winner among A/B/C: C (declared last)
261
+
```
262
+
263
+
If a module re-appears in both the explicit list and is also discovered via `@Configuration`, it is loaded once — at its **explicit position** — so the user's declaration order always controls override precedence.
264
+
265
+
**Escape hatch for fine-grained ordering**: list all participating modules explicitly in `@KoinApplication(modules = [...])` in the desired order. This bypasses classpath-dependent discovery order for `@Configuration` modules.
266
+
267
+
## Generic DSL Types
268
+
269
+
Runtime Koin resolves definitions on the **erased raw class** — type parameters are not part of the lookup key. Compile-safety honours that: a `get<Box<X>>()` call is validated against any `Box<*>` provider in the graph, and two `single<Box<A>>()` / `single<Box<B>>()` declarations collide on the same raw class.
270
+
271
+
```kotlin
272
+
val appModule = module {
273
+
single<Navigator<AppKey>>() // validated as Navigator (raw)
274
+
single<Navigator<MenuKey>>() // treated as the same definition
275
+
}
276
+
```
277
+
278
+
Validating on the raw class is also what makes iOS/Native builds work — emitting the generic type with its free parameter into hint functions used to crash the Kotlin/Native klib signature mangler.
279
+
280
+
### Discriminating generic instances — use `named<T>()`
281
+
282
+
When multiple instances of the same generic class must coexist, register a **concrete wrapper type** and key each instance with a type qualifier derived from the generic parameter. This is the pattern used internally by `koin-compose-navigation3`:
Runtime Koin matches on (raw class + qualifier), and the compile-safety validator respects qualifier matching — so the plugin sees these as two distinct definitions, as expected. Prefer `named<T>()` qualifier-on-concrete-type over `single<Box<X>>()` directly whenever you need to distinguish generic instantiations.
Copy file name to clipboardExpand all lines: docs/DEBUGGING.md
+26Lines changed: 26 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -969,6 +969,32 @@ val instanceExpression = if (includedModuleClass.isObject) {
969
969
970
970
**Prevention**: When actively developing the plugin, consider keeping `test-apps` in a separate IntelliJ window and refreshing Gradle after each `./install.sh`.
971
971
972
+
### 7.13 "'buildViewModel' is not on classpath. Add dependency: io.insert-koin:koin-core-viewmodel"
973
+
974
+
**Symptom**: Compile error at an `@KoinViewModel`-annotated class:
975
+
```
976
+
[Koin] @KoinViewModel definition 'com.app.MyViewModel' cannot be generated:
977
+
'buildViewModel' is not on classpath. Add dependency: io.insert-koin:koin-core-viewmodel
978
+
```
979
+
980
+
Same shape for `@KoinWorker` → `io.insert-koin:koin-android-workmanager`.
981
+
982
+
**Cause**: `@KoinViewModel` / `@KoinWorker` annotations live in `koin-annotations` (always available via `koin-core`), but the runtime DSL that backs them (`Module.buildViewModel` / `Module.buildWorker`) ships in a separate artifact. If you only have `koin-core` + `koin-annotations`, the plugin can't generate a working definition for those annotations.
983
+
984
+
Before the check existed (pre-RC2.3), the plugin silently skipped the definition; you got `NoDefinitionFoundException` at runtime. RC2.3+ fails loudly with the fix instruction.
985
+
986
+
**Fix**:
987
+
988
+
```kotlin
989
+
// build.gradle.kts
990
+
dependencies {
991
+
implementation("io.insert-koin:koin-core-viewmodel:4.2.1") // for @KoinViewModel
992
+
implementation("io.insert-koin:koin-android-workmanager:4.2.1") // for @KoinWorker (Android)
993
+
}
994
+
```
995
+
996
+
The error fires once per annotation type per compilation — if you see it for `@KoinViewModel`, all ViewModel definitions in that compilation are blocked until the artifact is added.
0 commit comments