# Technical Documentation: InsertKoinIO/koin

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/InsertKoinIO/koin)

### Overview (`README.md`)

[](https://kotlinlang.org)

[](https://github.com/InsertKoinIO/koin/blob/main/LICENSE.txt)
[](https://kotlinlang.slack.com/messages/koin/)


# What is KOIN? ✨
 
Koin is a pragmatic, lightweight dependency injection framework for Kotlin developers, developed by [Kotzilla](https://kotzilla.io) and open-source [contributors](https://github.com/InsertKoinIO/koin/graphs/contributors).

```
The pragmatic Kotlin & Kotlin Multiplatform Dependency Injection framework
- Simple, Powerful and now Compile-Safe -
```

## Compile-Safe Dependency Injection 🛡️

Koin is now **compile-safe** thanks to the [Koin Compiler Plugin](https://github.com/InsertKoinIO/koin-compiler-plugin). Your dependency graph is verified at compile time — missing bindings, broken definitions, and misconfigured modules are caught by the compiler instead of crashing at runtime.

- Catch missing or invalid dependencies before you run your app
- Keep Koin's concise, idiomatic DSL — no extra boilerplate
- Works across Kotlin & Kotlin Multiplatform targets

Get started with the [Koin Compiler Plugin](https://github.com/InsertKoinIO/koin-compiler-plugin).


## Setup & Current Version 📦

Follow the dedicated [setup page](https://insert-koin.io/docs/setup/koin) to setup Koin for your project.
Here are the currently available Koin versions:

- **Latest** Version : [](https://mvnrepository.com/artifact/io.insert-koin/koin-core)


## Developer Tools & Observability 🛠️

### Koin Skills — for AI Coding Agents

Official [Koin skills](https://github.com/InsertKoinIO/koin-skills) teach your coding agent to set up Koin, write Koin code, migrate from Hilt, Dagger, Toothpick, Kodein, Metro or older Koin, and diagnose Koin apps, following the Koin Compiler Plugin rules.

```
# Claude Code
/plugin marketplace add InsertKoinIO/koin-skills
/plugin install koin@koin-skills

# Cursor, Codex, Copilot and other agents
npx skills add InsertKoinIO/koin-skills
```

Both commands install from the [koin-skills repository](https://github.com/InsertKoinIO/koin-skills) on GitHub (`npx skills` needs Node.js 22+). See [AI Agents and Koin Skills](https://insert-koin.io/docs/tooling/ai-agents).

### Kotzilla Platform — KMP Crash Reporting & Observability

Free signup on [Kotzilla](https://kotzilla.io)

> The full observability platform for Kotlin Multiplatform apps:
> - **Full KMP Crash Reporting** — complete crash reports across Android, iOS and all KMP targets
> - **Screen Rendering Impact** — understand how each screen affects rendering and user experience
> - **Performance Monitoring** — track runtime performance, memory metrics, and Koin module behavior
>
> A console-based suite with connected cloud services that visualizes your Koin module structure, provides advanced debugging and tracing, and seamlessly integrates with Kotlin Multiplatform projects.

### Koin IDE Plugin — Your Koin Developer Assistant

Freely available on the [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/26131-koin-dependency-injection-official-/versions/stable)

> The perfect Koin developer assistant: native support for IntelliJ IDEA and Android Studio, with navigation, inspections, and productivity features dedicated to Koin across Kotlin and Kotlin Multiplatform projects.


## Community & Enterprise Support 💬

Koin is backed by an active open-source community and by Kotzilla for teams that need guaranteed, enterprise-grade support.

### Community Support 🌱

Free, open-source support from the Koin community:

- Chat with us on the Kotlin Slack [#koin](https://kotlinlang.slack.com/?redir=%2Fmessages%2Fkoin) channel
- Ask questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/koin) with the `koin` tag
- Found a bug or have a feature request? Open an issue on [GitHub](https://github.com/InsertKoinIO/koin/issues)
- Follow releases and announcements on the [Koin blog](https://blog.insert-koin.io) and [@insertkoin_io](https://twitter.com/insertkoin_io)

### Enterprise-Grade Support with Koin LTS 🏢

For teams with production and compliance requirements, [**Koin LTS**](https://kotzilla.io/koin-lts) delivers enterprise-grade support backed by Kotzilla:

- **SLA-backed updates** and guaranteed response times
- **Long-term maintenance** on stabilized Koin versions (e.g. Koin 3.5 LTS)
- **Security & compliance patches** aligned with enterprise requirements
- **Direct access to Kotzilla experts** — the team behind Koin
- **Priority bug fixes** and advisory support for architecture and migration

👉 Learn more and opt in at [kotzilla.io/koin-lts](https://kotzilla.io/koin-lts).

## Latest News & Resources 🌐
- Official Website: [insert-koin.io](https://insert-koin.io)
- Twitter: [@insertkoin_io](https://twitter.com/insertkoin_io)
- Blog: [Koin Developers](https://blog.insert-koin.io)

## Contributing 🛠

Want to help or share a proposal about Koin? Problem with a specific feature? 

- Open an issue to explain the issue you want to solve [Open an issue](https://github.com/InsertKoinIO/koin/issues)
- Come talk on slack [#koin-dev](https://kotlinlang.slack.com/?redir=%2Fmessages%2Fkoin-dev) channel
- After discussion to validate your ideas, you can open a PR or even a draft PR if the contribution is a big one [Current PRs](https://github.com/InsertKoinIO/koin/pulls)

Additional readings about basic setup: https://github.com/InsertKoinIO/koin/blob/master/CONTRIBUTING.adoc

### Contributors

Thank you all for your work! ❤️

<a href="https://github.com/InsertKoinIO/koin/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=InsertKoinIO/koin" />
</a>

## OpenCollective - Sponsorship ❤️

Support this project by becoming a sponsor and be displayed on the offcial website. [[Help us and Become a sponsor!](https://opencollective.com/koin#sponsor)]

---

### Intro Index (`docs/intro/index.md`)

---
title: Introduction
---

# Welcome to Koin

**The Pragmatic Kotlin Dependency Injection Framework - Simple AND Powerful**

Koin is a lightweight dependency injection framework for Kotlin developers. Whether you're building Android apps, Kotlin Multiplatform projects, backend services with Ktor, or any Kotlin application, Koin makes dependency injection straightforward and intuitive.

## Why Koin?

Koin was designed with a clear philosophy: **you shouldn't have to choose between simplicity and capability**. With Koin, you get both.

### DSL & Annotations - Choose What You Want

Koin is powerful with both approaches. Prefer a clean Kotlin DSL? Use it. Like annotations? Use them. Both are first-class citizens, equally powerful.

| Value | What it means |
|-------|---------------|
| **Productive** | Easy to learn, easy to write. Get DI working in minutes, not hours |
| **Developer-Friendly** | DSL or Annotations - your choice. Clear errors, easy debugging, best DX |
| **Scalable** | Powers large enterprise applications with complex dependency graphs |
| **Safe** | Compile-time safety with Koin Compiler Plugin |
| **Dynamic** | Runtime flexibility: load modules dynamically, lazy loading, feature flags |

## Where to Start?

Choose your path based on your experience level:

### New to Dependency Injection?

Start with the fundamentals:
- **[What is Dependency Injection?](/docs/intro/what-is-dependency-injection)** - Understand the core concepts

### Know DI, New to Koin?

Jump right into Koin:
- **[What is Koin?](/docs/intro/what-is-koin)** - Discover Koin's approach to DI
- **[Koin Compiler Plugin](/docs/intro/koin-compiler-plugin)** - The recommended, safer way to use Koin

### Coming from Hilt/Dagger?

See how Koin compares:
- **[Koin vs Hilt/Dagger](/docs/intro/koin-vs-hilt)** - Understand the differences and migration path

### Ready to Code?

- **[Setup Guide](/docs/setup/gradle)** - Add Koin to your project
- **[Tutorials](/docs/tutorials/your-first-app)** - Build your first Koin app
- **[Koin IDE Plugin](https://plugins.jetbrains.com/plugin/26131-koin-dependency-injection-official-)** - Install the official plugin for Android Studio & IntelliJ IDEA — code navigation, live safety checks, dependency graph visualization

## Koin's Approaches

Koin offers flexibility in how you define your dependencies:

| Approach | Status | Description |
|----------|--------|-------------|
| **Koin Compiler Plugin** (Kotlin 2.x) | Recommended | DSL: `single<MyService>()`, `factory<MyRepo>()`, `viewModel<MyVM>()`. |
| **Koin Compiler Plugin** (Kotlin 2.x) | Recommended | Annotations: `@Singleton`, `@Factory`, `@KoinViewModel`. Auto-detects dependencies, compile-time safety. |
| **Classic DSL** | Fully Supported | `singleOf(::MyService)`, `single { MyService(get()) }`. Works with any Kotlin version. Compiler Plugin adds safety on top when you're ready. |
| **KSP Processor** (`koin-ksp-compiler`) | Deprecated | Legacy processor for Koin Annotations. Migrate to Compiler Plugin — same annotations, native compiler integration. |

Learn more in [What is Koin?](/docs/intro/what-is-koin) and [Koin Compiler Plugin](/docs/intro/koin-compiler-plugin).

## Platform Support

Koin works everywhere Kotlin runs:

| Platform | Package | Status |
|----------|---------|--------|
| **Kotlin/JVM** | `koin-core` | ✅ Full support |
| **Android** | `koin-android` | ✅ Full support |
| **Compose (Android & Multiplatform)** | `koin-compose` | ✅ Full support |
| **iOS** | `koin-core` | ✅ Full support |
| **Desktop** | `koin-core` | ✅ Full support |
| **Web (JS/Wasm)** | `koin-core` | ✅ Full support |
| **Ktor** | `koin-ktor` | ✅ Full support |

## Quick Example

Here's a taste of what Koin looks like:

```kotlin
// Define your classes
class UserRepository(private val api: ApiService)
class UserViewModel(private val repository: UserRepository) : ViewModel()

// Define your module with Compiler Plugin DSL
val appModule = module {
    single<ApiService>()
    single<UserRepository>()
    viewModel<UserViewModel>()
}

// Start Koin
startKoin {
    modules(appModule)
}

// Inject in your Activity
class MainActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModel()
}
```

Or with annotations:

```kotlin
@Singleton
class UserRepository(private val api: ApiService)

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

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

Ready to get started? Head to the [Setup Guide](/docs/setup/gradle).

---

### Setup Index (`docs/setup/index.md`)

---
title: Setup & Versions
---

# Setting Up Koin

This guide covers everything you need to add Koin to your project.

## Quick Setup

Choose your platform to get started:

| Platform | Package | Guide |
|----------|---------|-------|
| **Kotlin/JVM** | `koin-core` | [Gradle Setup](/docs/setup/gradle#kotlin) |
| **Android** | `koin-android` | [Gradle Setup](/docs/setup/gradle#android) |
| **Android + Jetpack Compose** | `koin-android` + `koin-compose` | [Gradle Setup](/docs/setup/gradle#compose-android) |
| **Compose Multiplatform** | `koin-compose` | [Gradle Setup](/docs/setup/gradle#compose) |
| **Kotlin Multiplatform** | `koin-core` | [Gradle Setup](/docs/setup/gradle#kotlin-multiplatform) |
| **Ktor** | `koin-ktor` | [Gradle Setup](/docs/setup/gradle#ktor) |

## Recommended Setup: BOM + Compiler Plugin

For the best experience, we recommend:

1. **Use the Koin BOM** - Manages all Koin library versions
2. **Use the Koin Compiler Plugin** - Provides compile-time safety

See the **[Compiler Plugin Setup Guide](/docs/setup/compiler-plugin)** for detailed instructions.

## Setup Guides

### [Gradle Setup](/docs/setup/gradle)

Complete dependency configuration for all platforms:
- Koin BOM (recommended)
- Version catalogs
- Platform-specific packages
- Testing dependencies

### [Compiler Plugin Setup](/docs/setup/compiler-plugin)

Detailed guide for the Koin Compiler Plugin:
- Gradle plugin configuration
- Configuration options
- Kotlin version requirements
- Troubleshooting

### [KSP Processor Setup](/docs/setup/annotations-ksp) (Deprecated)

Legacy setup for `koin-ksp-compiler`, the KSP-based processor for Koin Annotations:
- ⚠️ `koin-ksp-compiler` is deprecated — migrate to the Koin Compiler Plugin
- Koin Annotations itself is **not** deprecated; `koin-annotations` is now part of the main Koin project
- Migration guide included

## Version Compatibility

| Koin Version | Kotlin Version | Koin Compiler Plugin |
|--------------|----------------|----------------------|
| 4.2.x | 2.3+ | ✅ Recommended |
| 4.1.x | 2.1/2.2+ | ⚠️ KSP Processor only |
| 4.0.x | 1.9/2.0+ | ⚠️ KSP Processor only |
| 3.5.x | 1.8+ | ❌ Not available |

## Current Version

- **Koin**: [](https://mvnrepository.com/artifact/io.insert-koin/koin-core)
- **Koin Compiler Plugin**: [](https://mvnrepository.com/artifact/io.insert-koin/koin-compiler-plugin)

Find all Koin packages on [Maven Central](https://central.sonatype.com/search?q=io.insert-koin+koin-core&sort=name).

## Next Steps

After setup:
- **[Core Concepts](/docs/reference/koin-core/starting-koin)** - Learn how to use Koin
- **[Tutorials](/docs/quickstart/kotlin)** - Build your first app
- **[Android Integration](/docs/reference/koin-android/start)** - Android-specific features

---

### Intro Koin Compiler Plugin (`docs/intro/koin-compiler-plugin.md`)

---
title: Koin Compiler Plugin
---

# 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:

```kotlin
// 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 DSL | Compiler 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 Safety

The Koin Compiler Plugin provides **compile-time dependency verification** for both DSL and Annotations:

- **A2 — Per-Module:** validates definitions against visible scope (early feedback)
- **A3 — Full Graph:** validates the complete assembled graph at `startKoin<T>()`
- **A4 — Call-Site:** validates every `get<T>()`, `inject<T>()`, `koinViewModel<T>()` call

If it compiles, every dependency and every injection call site is satisfied. This replaces `verify()` and `checkModules()` — no runtime test harness needed.

See [Compile-Time Safety](/docs/reference/koin-compiler/compile-safety) for full details.

### 4. DSL & Annotations - Both Equally Powerful

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

**DSL Style:**
```kotlin
val appModule = module {
    single<Database>()
    single<UserRepository>()
    viewModel<UserViewModel>()
}
```

:::info DSL + Parameter Annotations
When using DSL style, you still use **parameter annotations** on your classes to guide the plugin:

```kotlin
class UserPresenter(
    @InjectedParam val userId: String,      // Runtime parameter
    @Named("api") val client: ApiClient,    // Qualified dependency
    val repository: UserRepository          // Auto-resolved
)

val appModule = module {
    factory<UserPresenter>()  // Plugin reads annotations from the class
}
```

The DSL defines **where** dependencies are registered. Parameter annotations define **how** they are resolved.
:::

**Annotation Style:**
```kotlin
@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. 

:::info
    See the **[Compiler Plugin Setup Guide](/docs/setup/compiler-plugin)** for detailed instructions.
:::

### Using the Compiler Plugin DSL

Import from the compiler plugin package:

```kotlin
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:

```kotlin
@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:

```kotlin
single<UserRepository>()
```

The plugin transforms it to:

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

For more complex cases:

```kotlin
// 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

```kotlin
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>()
}
```

### Safe Instance Creation with `create()`

Use `create(::T)` inside a definition lambda to safely build an instance with auto-resolved constructor dependencies:

```kotlin
val appModule = module {
    single { create(::MyService) }
}
```

The compiler plugin transforms `create(::MyService)` into `MyService(get(), get(), ...)`, auto-wiring all constructor parameters.

### With Qualifiers

Use `@Named` on your classes to define qualifiers, and on parameters to specify which dependency to inject:

```kotlin
// Define implementations with @Named qualifier
@Named("local")
class LocalDatabase : Database

@Named("remote")
class RemoteDatabase : Database

// Use @Named on parameters to specify which one to inject
class SyncService(
    @Named("local") val localDb: Database,
    @Named("remote") val remoteDb: Database
)

// DSL - plugin reads @Named from classes and parameters
val appModule = module {
    single<LocalDatabase>()
    single<RemoteDatabase>()
    single<SyncService>()
}
```

You can also create custom qualifiers with `@Qualifier`:

```kotlin
@Qualifier
annotation class LocalDb

@Qualifier
annotation class RemoteDb

@LocalDb
class LocalDatabase : Database

@RemoteDb
class RemoteDatabase : Database

class SyncService(
    @LocalDb val localDb: Database,
    @RemoteDb val remoteDb: Database
)
```

### With Parameters

Use `@InjectedParam` on your class to mark parameters passed at injection time:

```kotlin
// Annotation on the class - tells the plugin how to handle this parameter
class UserPresenter(
    @InjectedParam val userId: String,    // Passed via parametersOf()
    val repository: UserRepository        // Auto-resolved by Koin
)

// DSL in module - tells Koin where to register
val appModule = module {
    factory<UserPresenter>()
}

// Usage - pass the runtime parameter
val presenter: UserPresenter = get { parametersOf("user123") }
```

### Interface Binding

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

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

## Annotations Reference

### Definition Annotations

| Annotation | Description |
|------------|-------------|
| `@Singleton` / `@Single` | Single instance |
| `@Factory` | New instance each time |
| `@Scoped` | Instance per scope |
| `@KoinViewModel` | Android ViewModel |
| `@KoinWorker` | Android WorkManager Worker |

### Parameter Annotations

| Annotation | Description |
|------------|-------------|
| `@Named("qualifier")` | Named qualifier |
| `@InjectedParam` | Runtime parameter (via `parametersOf()`) |
| `@Property("key")` | Koin property value |
| `@Provided` | External dependency (skip validation) |

### Module Annotations

| Annotation | Description |
|------------|-------------|
| `@Module` | Declares a Koin module |
| `@ComponentScan("package")` | Scan package for annotated classes |
| `@Configuration` | Auto-discovered module |

## Comparison: Approaches

| Approach | Status | Package | Syntax |
|----------|--------|---------|--------|
| **Compiler Plugin DSL** | Recommended | Already located in Koin **`org.koin.plugin.module.dsl`** | `single<MyService>()`, `factory<MyRepo>()`, `viewModel<MyVM>()` |
| **Compiler Plugin Annotations** | Recommended | Annotations available in **`koin-annotations`** | `@Singleton`, `@Factory`, `@KoinViewModel ` |
| **Classic DSL** | Fully Supported | `org.koin.dsl` | `singleOf(::MyService)`, `single { MyService(get()) }`, `viewModelOf(::MyVM)` |
| **KSP Processor** | Deprecated | `koin-ksp-compiler` | Legacy processor for Koin Annotations — same annotations, **Migrate to Compiler Plugin ⚠️** |

### Compiler Plugin DSL (Recommended)

- Auto-detects dependencies
- Compile-time analysis
- Cleanest syntax

### Compiler Plugin Annotations (Recommended)

- Auto-detects dependencies
- Compile-time analysis
- Familiar annotation style

### Classic DSL (Fully Supported)

- Works with any Kotlin version
- Full control over wiring
- Can migrate to Plugin DSL when ready

### KSP Processor `koin-ksp-compiler` (Deprecated)

- The `koin-annotations` library is **not deprecated** — it's now part of the Koin project
- Only the legacy KSP-based processor (`koin-ksp-compiler`) is deprecated
- Migrate to the Koin Compiler Plugin — your annotations stay the same
- `koin-ksp-compiler` will be removed in a future Koin 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 the [Compiler Plugin DSL reference](/docs/setup/compiler-plugin#dsl-style) for the compile-time safe syntax.

### From the KSP Processor (`koin-ksp-compiler`)

If you're using Koin Annotations with the legacy KSP processor, migration is recommended now:

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

See [Migrating from KSP to Compiler Plugin](/docs/migration/from-ksp-to-compiler-plugin).

## Requirements

- **Kotlin 2.x** (K2 compiler)
- Gradle 8.x+

## Configuration Options

```kotlin
// 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:

```kotlin
// 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

- **[Setup Guide](/docs/setup/compiler-plugin)** - Detailed setup instructions
- **[DSL Reference](/docs/reference/dsl-reference)** - Complete DSL documentation
- **[Annotations Reference](/docs/reference/annotations-reference)** - Complete annotations documentation
- **[Migration Guides](/docs/migration/from-ksp-to-compiler-plugin)** - Upgrade your project

---

### Intro Koin Vs Hilt (`docs/intro/koin-vs-hilt.md`)

---
title: Koin vs Hilt/Dagger
---

# Koin vs Hilt/Dagger

This page compares Koin with Hilt and Dagger to help you understand the differences and decide which framework fits your needs.

:::info
Koin supports both **DSL and Annotations** - choose what fits your team. Both are first-class citizens, equally powerful, and powered by the same Compiler Plugin. This comparison shows annotation examples for fair comparison with Hilt, but Koin's DSL offers equivalent functionality with even less boilerplate.
:::

## Philosophy Differences

| Aspect | Koin | Hilt/Dagger |
|--------|------|-------------|
| **Learning curve** | Minutes to learn | Hours/days to master |
| **Code complexity** | Simple DSL or annotations | Complex annotation rules |
| **Debugging** | Clear errors, no generated code maze | Generated code can be hard to trace |
| **Setup** | One plugin, minimal config | Multiple annotations, strict rules |
| **Compile safety** | ✅ With Compiler Plugin | ✅ Always |
| **Runtime flexibility** | ✅ Dynamic features | ❌ Static only |

## Annotation Comparison

Even annotations are simpler in Koin:

| Task | Koin | Hilt |
|------|------|------|
| **Singleton** | `@Singleton class MyService` | `@Singleton class MyService @Inject constructor(...)` |
| **Interface binding** | Automatic | Requires `@Binds` in abstract module |
| **Component scanning** | `@ComponentScan("package")` | Not available |
| **Module discovery** | `@Configuration` - auto-discovered | Manual `@InstallIn` per module |
| **Provide 3rd party** | `@Singleton fun provide()` | `@Provides` in `@Module` + `@InstallIn` |
| **ViewModel** | `@KoinViewModel class MyVM` | `@HiltViewModel class MyVM @Inject constructor` |

## Code Comparison

### Simple Singleton

**Koin:**
```kotlin
@Singleton
class MyRepository(val api: ApiService)

@Module
@ComponentScan("com.app")
class AppModule
```

**Hilt:**
```kotlin
@Singleton
class MyRepository @Inject constructor(val api: ApiService)

@Module
@InstallIn(SingletonComponent::class)
abstract class AppModule {
    @Binds
    abstract fun bindRepository(impl: MyRepository): Repository
}
```

### Interface Binding

**Koin - Automatic:**
```kotlin
@Singleton
class UserRepositoryImpl(val db: Database) : UserRepository

// That's it! Koin automatically binds to UserRepository interface
```

**Hilt - Requires explicit binding:**
```kotlin
@Singleton
class UserRepositoryImpl @Inject constructor(val db: Database) : UserRepository

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
    @Binds
    abstract fun bindUserRepository(impl: UserRepositoryImpl): UserRepository
}
```

### Multi-Module Apps

**Koin - Modules auto-discovered:**
```kotlin
// feature/auth/AuthModule.kt
@Module
@ComponentScan
@Configuration  // Auto-discovered!
class AuthModule

// feature/profile/ProfileModule.kt
@Module
@ComponentScan
@Configuration  // Auto-discovered!
class ProfileModule

// app/MyApp.kt
@KoinApplication  // No need to list modules
class MyApp
```

**Hilt - Must manually install each module:**
```kotlin
// feature/auth/AuthModule.kt
@Module
@InstallIn(SingletonComponent::class)
class AuthModule { ... }

// feature/profile/ProfileModule.kt
@Module
@InstallIn(SingletonComponent::class)
class ProfileModule { ... }

// app/MyApp.kt
@HiltAndroidApp
class MyApp  // Still need correct @InstallIn everywhere
```

### ViewModel

**Koin:**
```kotlin
@KoinViewModel
class UserViewModel(
    private val repository: UserRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel()

// In Activity/Fragment
val viewModel: UserViewModel by viewModel()

// In Compose
val viewModel: UserViewModel = koinViewModel()
```

**Hilt:**
```kotlin
@HiltViewModel
class UserViewModel @Inject constructor(
    private val repository: UserRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel()

// In Activity/Fragment
val viewModel: UserViewModel by viewModels()

// In Compose
val viewModel: UserViewModel = hiltViewModel()
```

### Providing Third-Party Libraries

**Koin:**
```kotlin
@Module
class NetworkModule {
    @Singleton
    fun provideRetrofit(): Retrofit = Retrofit.Builder()
        .baseUrl("https://api.example.com")
        .build()

    @Singleton
    fun provideApi(retrofit: Retrofit): ApiService =
        retrofit.create(ApiService::class.java)
}
```

**Hilt:**
```kotlin
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit = Retrofit.Builder()
        .baseUrl("https://api.example.com")
        .build()

    @Provides
    @Singleton
    fun provideApi(retrofit: Retrofit): ApiService =
        retrofit.create(ApiService::class.java)
}
```

## Dynamic Features: Koin's Unique Advantage

Koin is **runtime-based but performant and compile-safe**. This enables dynamic features that Hilt cannot offer:

| Dynamic Feature | Koin | Hilt |
|-----------------|------|------|
| Load modules at runtime | ✅ `loadKoinModules()` | ❌ Not possible |
| Unload modules | ✅ `unloadKoinModules()` | ❌ Not possible |
| Lazy background loading | ✅ `lazyModules()` | ❌ Not possible |
| Feature flag injection | ✅ Easy | ⚠️ Complex workarounds |
| Plugin architectures | ✅ Natural fit | ❌ Very difficult |
| A/B test implementations | ✅ Runtime swap | ⚠️ Compile-time only |
| Dynamic configuration | ✅ Supported | ❌ No, Must recompile |

### Example: Dynamic Module Loading

```kotlin
// KOIN - Dynamic module loading
if (userHasPremium) {
    loadKoinModules(premiumFeatureModule)
}

// Later, if subscription expires
unloadKoinModules(premiumFeatureModule)

// Lazy loading for faster startup
startKoin {
    modules(coreModule)
    lazyModules(
        analyticsModule,  // Loaded in background
        heavyFeatureModule
    )
}
```

**This is impossible with Hilt** - all dependencies are wired at compile time.

### Example: Feature Flags

```kotlin
// KOIN - Switch implementations at runtime
val featureModule = module {
    if (FeatureFlags.useNewApi) {
        single<ApiService> { NewApiService() }
    } else {
        single<ApiService> { LegacyApiService() }
    }
}

// Or dynamically
fun updateApiImplementation(useNew: Boolean) {
    unloadKoinModules(apiModule)
    loadKoinModules(if (useNew) newApiModule else legacyApiModule)
}
```

## Setup Comparison

### Koin Setup

See the **[Compiler Plugin Setup Guide](/docs/setup/compiler-plugin)** for detailed instructions.

### Hilt Setup

```kotlin
// settings.gradle.kts
plugins {
    id("com.google.dagger.hilt.android") version "2.x" apply false
}

// app/build.gradle.kts
plugins {
    id("com.google.devtools.ksp")
    id("dagger.hilt.android.plugin")
}

dependencies {
    implementation("com.google.dagger:hilt-android:2.x")
    ksp("com.google.dagger:hilt-compiler:2.x")
}
```

## Error Messages

### Koin

```
org.koin.core.error.NoBeanDefFoundException:
No definition found for class 'com.app.UserRepository'.
Check your module definitions.
```

Clear, points directly to the issue.

### Hilt/Dagger

```
error: [Dagger/MissingBinding] com.app.UserRepository cannot be provided
without an @Inject constructor or an @Provides-annotated method.
com.app.UserRepository is injected at
    com.app.UserService(repository)
com.app.UserService is injected at
    com.app.UserActivity.service
com.app.UserActivity is injected at
    dagger.hilt.android.internal.managers.ActivityComponentManager.inject
```

Longer, requires understanding the component graph.

## When to Choose Each

### Choose Koin When:

- You value **productivity and simplicity**
- You need **runtime flexibility** (dynamic modules, feature flags)
- You're building **Kotlin Multiplatform** apps
- Your team wants to **learn quickly**
- You prefer **less boilerplate**
- You want **easier debugging**

### Choose Hilt When:

- Your team **already knows Dagger**
- You need **Google-first ecosystem** compatibility
- You require **Dagger's specific features**

## Migration from Hilt to Koin

If you're considering migration:

### Concept Mapping

| Hilt | Koin |
|------|------|
| `@HiltAndroidApp` | `@KoinApplication` and `startKoin<T> { }` |
| `@AndroidEntryPoint` | `by inject()` |
| `@HiltViewModel` with `by viewModels()` | `@KoinViewModel` with `by viewModel()` |
| `@Inject constructor` | Just constructor (auto-detected) |
| `@Binds` | Automatic or `bind` |
| `@InstallIn(SingletonComponent)` | `@Configuration` |
| `@Provides` on a function | `@Factory` on a function |

### Gradual Migration

You can migrate incrementally:

1. Add Koin to your project
2. Migrate one feature module at a time
3. Both DI frameworks can coexist during transition (Koin can scan target packages with `@ComponentScan`)
4. Remove Hilt when migration is complete

See [Migrating from Hilt](/docs/migration/from-hilt) for detailed steps.

## Summary

**Koin: Simple AND Powerful**

- **Compile-time safety** like Hilt (with Compiler Plugin)
- **DSL or Annotations** - both equally powerful, your choice
- **Simplicity and productivity** that Hilt can't match
- **Dynamic runtime features** that Hilt can't do

You don't have to choose between safety and simplicity. With Koin, you get both.

## Next Steps

- **[What is Koin?](/docs/intro/what-is-koin)** - Learn more about Koin
- **[Setup Guide](/docs/setup/gradle)** - Add Koin to your project
- **[Migrating from Hilt](/docs/migration/from-hilt)** - Step-by-step migration guide

---

### Intro What Is Dependency Injection (`docs/intro/what-is-dependency-injection.md`)

---
title: What is Dependency Injection?
---

# What is Dependency Injection?

Dependency Injection (DI) is a design pattern where objects receive their dependencies from external sources rather than creating them internally. This promotes loose coupling, better testability, and cleaner code architecture.

## What is a Dependency?

A dependency is any object that another object needs to function. For example, a `Car` depends on an `Engine` to drive.

### Without Dependency Injection

```kotlin
class Engine {
    fun start() {
        println("Engine starting...")
    }
}

class Car {
    private val engine = Engine()  // Car creates its own engine

    fun drive() {
        engine.start()
        println("Car is driving")
    }
}
```

**Problems with this approach:**
- `Car` is tightly coupled to a specific `Engine` implementation
- Difficult to test `Car` independently
- Hard to swap engine types (electric, diesel, etc.)
- `Car` controls the lifecycle of `Engine`

### With Dependency Injection

```kotlin
class Car(private val engine: Engine) {  // Engine is injected
    fun drive() {
        engine.start()
        println("Car is driving")
    }
}

// Now we can easily provide different engines
val gasolineCar = Car(GasEngine())
val electricCar = Car(ElectricEngine())
```

**Benefits:**
- `Car` doesn't know how `Engine` is created
- Easy to test with mock engines
- Flexible - can swap implementations
- Clear dependencies visible in constructor

## Three Ways to Provide Dependencies

### 1. Constructor Injection (Recommended)

Dependencies are passed through the constructor:

```kotlin
class UserRepository(
    private val database: Database,
    private val apiClient: ApiClient
) {
    fun getUser(id: String): User {
        return database.query(id) ?: apiClient.fetchUser(id)
    }
}
```

**Advantages:**
- Dependencies are explicit and required
- Immutable (using `val`)
- Easy to test
- Clear dependency graph

**With Koin:**

```kotlin
val appModule = module {
    single<Database>()
    single<ApiClient>()
    single<UserRepository>()  // Koin auto-wires dependencies
}
```

:::info
Constructor injection is the **preferred approach** in Koin. It makes your code testable without requiring Koin in unit tests.
:::

### 2. Field Injection

Dependencies are injected into class properties:

```kotlin
class UserActivity : AppCompatActivity() {
    // Lazy injection - instance created when first accessed
    private val viewModel: UserViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel.loadUser()  // ViewModel instance created here
    }
}
```

**When to use:**
- Android framework classes (Activity, Fragment, Service) where you don't control construction
- When constructor injection isn't possible

**With Koin:**

```kotlin
// Lazy injection
val presenter: Presenter by inject()

// Eager injection
val presenter: Presenter = get()
```

### 3. Method Injection

Dependencies are passed through methods (less common):

```kotlin
class ReportGenerator {
    fun generateReport(data: DataSource) {
        // Use data to generate report
    }
}
```

**When to use:**
- Optional dependencies
- Dependencies that change during object lifetime
- Callback patterns

## Manual vs Automated Dependency Injection

### The Problem with Manual DI

As applications grow, managing dependencies manually becomes complex:

```kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Manually creating the entire dependency graph
        val database = Database()
        val apiClient = ApiClient()
        val userRepository = UserRepository(database, apiClient)
        val authRepository = AuthRepository(database, apiClient)
        val userService = UserService(userRepository, authRepository)
        val viewModel = UserViewModel(userService)

        // Finally can use viewModel...
    }
}
```

**Problems:**
- Repetitive code across Activities/Fragments
- Easy to make mistakes in dependency order
- Hard to maintain as the app grows
- Difficult to manage lifecycles (singletons, scoped objects)
- No centralized configuration

### The Container Pattern (Manual Approach)

Developers often create a container to centralize object creation:

```kotlin
object AppContainer {
    private val database by lazy { Database() }
    private val apiClient by lazy { ApiClient() }

    val userRepository by lazy { UserRepository(database, apiClient) }
    val authRepository by lazy { AuthRepository(database, apiClient) }

    fun createUserViewModel() = UserViewModel(
        UserService(userRepository, authRepository)
    )
}

// Usage
class MainActivity : AppCompatActivity() {
    private val viewModel = AppContainer.createUserViewModel()
}
```

**Still has issues:**
- Manual wiring of dependencies
- No automatic lifecycle management
- Global state (singleton container)
- Still repetitive for complex graphs

### How Koin Solves This

Koin provides automated dependency resolution with your choice of **DSL or Annotations**:

```kotlin
// Define dependencies once
val appModule = module {
    single<Database>()
    single<ApiClient>()
    single<UserRepository>()
    single<AuthRepository>()
    single<UserService>()
    viewModel<UserViewModel>()
}

// Start Koin once
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            modules(appModule)
        }
    }
}

// Use anywhere - Koin handles the entire dependency graph
class MainActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModel()
    // That's it! Koin creates UserViewModel and all its dependencies
}
```

**Koin advantages:**
- Declarative dependency configuration
- Automatic dependency resolution
- Lifecycle management (singleton, factory, scoped)
- Type-safe injection
- Easy testing and module replacement

## Automated DI Solutions

There are different approaches to automated dependency injection:

| Approach | Examples | How it works |
|----------|----------|--------------|
| **Reflection-based** | (older frameworks) | Uses reflection at runtime |
| **Code generation** | Dagger, Hilt | Generates code at compile time (annotation processing) |
| **Compiler plugins** | Koin Compiler Plugin | Native compiler integration for DSL & Annotations |
| **DSL-based** | Koin (classic) | Runtime DSL configuration |

**Koin's approach - DSL & Annotations, both equally powerful:**
- **DSL style:** Clean Kotlin DSL configuration (`single<MyService>()`, `viewModel<MyVM>()`)
- **Annotation style:** Familiar annotations (`@Singleton`, `@KoinViewModel`)
- Both powered by the same Compiler Plugin for compile-time safety
- No reflection, lightweight
- Choose the style that fits your team

## Service Locator vs Dependency Injection

It's important to understand the difference:

### Service Locator Pattern

Components actively request dependencies from a registry:

```kotlin
class UserService : KoinComponent {
    private val repository: UserRepository by inject()  // "Pulling" dependency
}
```

### Dependency Injection Pattern

Dependencies are provided from outside:

```kotlin
class UserService(
    private val repository: UserRepository  // "Pushed" into component
)
```

### Comparison

| Aspect | Service Locator | Dependency Injection |
|--------|----------------|---------------------|
| Dependency visibility | Hidden inside class | Explicit in constructor |
| Testing | Requires framework | Easy - pass test doubles |
| Coupling | Depends on container | Depends on interfaces |
| Usage in Koin | `get()`, `by inject()` | Constructor with Koin module |
| Best for | Android framework classes | Business logic, services |

### Best Practices with Koin

1. **Prefer Constructor Injection** for business logic:

```kotlin
// Good - testable without Koin
class UserViewModel(private val userService: UserService) : ViewModel()

val appModule = module {
    viewModel<UserViewModel>()  // Koin resolves dependencies
}
```

2. **Use Service Locator** only when necessary:

```kotlin
// Acceptable - Activity construction controlled by Android
class UserActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModel()
}
```

3. **Avoid `KoinComponent` in business logic:**

```kotlin
// Bad - hard to test
class UserService : KoinComponent {
    private val repository: UserRepository = get()
}

// Good - explicit dependencies
class UserService(private val repository: UserRepository)
```

## Benefits of Dependency Injection

### 1. Testability

Without DI, testing is difficult:

```kotlin
class UserService {
    private val repository = UserRepository()  // Can't mock!
}
```

With DI, testing is straightforward:

```kotlin
class UserService(private val repository: UserRepository)

@Test
fun testGetUser() {
    val mockRepository = mockk<UserRepository>()
    val service = UserService(mockRepository)  // Full control

    every { mockRepository.findUser("123") } returns testUser
    assertEquals(testUser, service.getUser("123"))
}
```

### 2. Flexibility

Easily swap implementations:

```kotlin
val appModule = module {
    single<EmailService> { GmailService() }  // Production
}

val testModule = module {
    single<EmailService> { MockEmailService() }  // Testing
}
```

### 3. Code Organization

Centralized dependency configuration:

```kotlin
val dataModule = module {
    single<Database>()
    single<ApiClient>()
}

val domainModule = module {
    single<UserRepository>()
    single<AuthRepository>()
}

val presentationModule = module {
    viewModel<UserViewModel>()
}

startKoin {
    modules(dataModule, domainModule, presentationModule)
}
```

### 4. Lifecycle Management

Koin handles object lifecycles:

```kotlin
val appModule = module {
    single<Database>()       // One instance for entire app
    factory<Presenter>()     // New instance each time
    scoped<SessionData>()    // Instance per scope
}
```

## Summary

Dependency Injection is a powerful pattern that:
- **Decouples** components from their dependencies
- **Improves testability** by allowing dependency replacement
- **Simplifies maintenance** with centralized configuration
- **Scales better** than manual dependency management

Koin makes DI in Kotlin simple by:
- Offering **two equally powerful styles**: DSL or Annotations - your choice
- Supporting both **constructor injection** (recommended) and **field injection** (when needed)
- Providing **compile-time safety** with the Compiler Plugin
- Requiring **zero reflection** - pure Kotlin

## Next Steps

- **[What is Koin?](/docs/intro/what-is-koin)** - Learn about Koin's approach
- **[Koin Compiler Plugin](/docs/intro/koin-compiler-plugin)** - The recommended, safer approach
- **[Setup Guide](/docs/setup/gradle)** - Add Koin to your project

---

### Intro What Is Koin Annotations (`docs/intro/what-is-koin-annotations.md`)

---
title: What is Koin Annotations?
---

# What is Koin Annotations?

### Familiar Annotation Style — Part of the Main Koin Project

**Koin Annotations** is the annotation-based way to define your dependencies in Koin. If you prefer the style of `@Singleton`, `@Factory`, `@KoinViewModel` over a Kotlin DSL, this is for you.

It is **part of the main Koin project** — same GitHub repository, same release cycle, same Koin version, same maintainers. Not a side project, not a community fork, not a separate framework. It's processed by the **Koin Compiler Plugin** for compile-time safety, just like the DSL.

## In a Nutshell

```kotlin
@Singleton
class UserRepository(private val api: ApiService)

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

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

That's the whole idea: annotate your classes, declare a module, the Koin Compiler Plugin wires the rest at build time.

## Part of the Main Koin Project

The `koin-annotations` library is **part of the main Koin project**. It lives in the same repository, ships under the **same Koin version** as `koin-core`, follows the same release cycle, and is covered by the Koin BOM:

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-core")
    implementation("io.insert-koin:koin-annotations") // same Koin version, same BOM
}
```

What this means in practice:

- **Not deprecated** — annotations are a first-class, fully supported style
- **Not a separate product** — there's no "Koin Annotations" project to track on its own
- **Versions stay in sync** — `koin-core` and `koin-annotations` always match
- **Full feature parity with the DSL** — anything you can do with the DSL, you can do with annotations

## Now Powered by the Koin Compiler Plugin

Koin Annotations is processed by the **Koin Compiler Plugin** — a native **Kotlin Compiler Plugin (K2)** that integrates directly with the Kotlin compiler. No KSP, no generated files to commit, no extra processing step.

What you get:

- **Auto-wiring** — constructor parameters are detected and resolved automatically
- **Compile-time safety** — missing dependencies, qualifier mismatches, and bad bindings are caught at build time
- **Simpler KMP setup** — no per-target KSP configuration
- **Same annotations** — `@Singleton`, `@Factory`, `@KoinViewModel`, `@Module`, `@ComponentScan`, `@Named`, `@InjectedParam`, etc.

See [Koin Compiler Plugin](/docs/intro/koin-compiler-plugin) for the full picture of how it works and what it generates.

## `koin-ksp-compiler` is Deprecated

:::warning
The legacy KSP processor `koin-ksp-compiler` is **deprecated** and will be removed in a future Koin version.
:::

The annotations themselves are **not** deprecated — only the KSP-based processor that used to handle them. Migration is mechanical:

- **Same annotations** — your `@Singleton`, `@Module`, `@ComponentScan` code stays exactly the same
- **Drop the KSP plugin** — replace with the Koin Compiler Plugin
- **Delete generated files** — the Compiler Plugin doesn't produce visible generated sources

See [Migrating from KSP to Compiler Plugin](/docs/migration/from-ksp-to-compiler-plugin) for the step-by-step.

## When to Choose Annotations

Both annotations and the DSL are first-class. Pick annotations if:

- You're coming from Hilt, Dagger, or Spring and want a familiar style
- You prefer co-locating the definition with the class it describes
- Your team standardizes on annotation-based configuration

Pick the DSL if you prefer a Kotlin-native, code-only style. You can also **mix both** in the same project — they're processed by the same Compiler Plugin.

## Next Steps

- **[Koin Compiler Plugin](/docs/intro/koin-compiler-plugin)** — How the plugin powers your annotations
- **[Annotations Reference](/docs/reference/koin-annotations/start)** — Full annotation catalog and patterns
- **[Migrating from KSP to Compiler Plugin](/docs/migration/from-ksp-to-compiler-plugin)** — Upgrade path from `koin-ksp-compiler`
- **[What is Koin?](/docs/intro/what-is-koin)** — The bigger picture

---

### Intro What Is Koin (`docs/intro/what-is-koin.md`)

---
title: What is Koin?
---

# What is Koin?

### The Pragmatic Kotlin Dependency Injection Framework - Simple AND Powerful

Koin is a lightweight dependency injection framework designed specifically for Kotlin. Unlike traditional DI frameworks that rely on code generation or reflection, Koin offers two equally powerful approaches: a clean **Kotlin DSL** and intuitive **Annotations**. Choose what fits your team - both are first-class citizens.

## Koin's Core Values

| Value | What it means |
|-------|---------------|
| **Productive** | Easy to learn, easy to write. Get DI working in minutes, not hours |
| **Developer-Friendly** | DSL or Annotations - your choice. Clear errors, easy debugging, best DX |
| **Scalable** | Powers large enterprise applications with complex dependency graphs |
| **Safe** | Compile-time safety with Koin Compiler Plugin |
| **Dynamic** | Runtime flexibility: load modules dynamically, lazy loading, feature flags |

## Why Developers Love Koin

- **Learn in minutes** - No complex concepts, intuitive DSL and simple annotations
- **Write less code** - DSL or Annotations, Compiler Plugin auto-wires dependencies
- **Choose your style** - DSL for Kotlin purists, Annotations for familiar patterns - both equally powerful
- **Debug easily** - Clear error messages, no generated code to trace through
- **Scale confidently** - Used in production by enterprises worldwide
- **Stay safe** - Compile-time verification catches errors before runtime
- **Stay flexible** - Runtime-based but performant. Dynamic modules, lazy loading, feature flags
- **IDE support** - Official plugin for Android Studio & IntelliJ IDEA — navigate definitions, live safety checks, graph visualization

## Two Styles, One Framework - Both Equally Powerful

Koin supports two styles of defining dependencies. Both are first-class citizens with full feature parity. Choose what fits your team:

### DSL Style

Define dependencies using Kotlin DSL syntax:

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

### Annotation Style

Define dependencies using annotations:

```kotlin
@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()
```

Both styles are processed by the **Koin Compiler Plugin** for compile-time safety.

## Koin's Annotations Are Simpler

If you've used Hilt or Dagger, you'll notice Koin annotations require less ceremony:

| Task | Koin | Hilt |
|------|------|------|
| **Singleton** | `@Singleton class MyService` | `@Singleton class MyService @Inject constructor(...)` |
| **Interface binding** | Automatic (just implement the interface) | Requires `@Binds` in abstract module |
| **Component scanning** | `@ComponentScan("package")` | Not available |
| **Module discovery** | `@Configuration` - auto-discovered | Manual `@InstallIn` per module |

**Example comparison:**

```kotlin
// KOIN - That's it!
@Singleton
class MyRepository(val api: ApiService)

@Module
@ComponentScan("com.app")
class AppModule
```

```kotlin
// HILT - More ceremony
@Singleton
class MyRepository @Inject constructor(val api: ApiService)

@Module
@InstallIn(SingletonComponent::class)
abstract class AppModule {
    @Binds
    abstract fun bindRepository(impl: MyRepository): Repository
}
```

## Powered by Koin Compiler Plugin

The **Koin Compiler Plugin** is the recommended way to use Koin for all new projects:

- **Native Kotlin Compiler Plugin (K2)** - Not KSP, direct compiler integration
- **Auto-detects constructor parameters** - Less manual wiring
- **Compile-time safety** - Errors caught during build
- **Works with both DSL and Annotations** - Your choice
- **Simple setup** - One Gradle plugin

### Cleaner Syntax with Compiler Plugin

| Classic DSL | Compiler Plugin DSL |
|-------------|---------------------|
| `singleOf(::MyService)` | `single<MyService>()` |
| `single { MyService(get(), get()) }` | `single<MyService>()` |
| `factoryOf(::MyRepo)` | `factory<MyRepo>()` |
| `viewModelOf(::MyVM)` | `viewModel<MyVM>()` |

Learn more in [Koin Compiler Plugin](/docs/intro/koin-compiler-plugin).

## Classic DSL (Fully Supported)

The classic DSL remains fully supported for all Kotlin versions:

```kotlin
val appModule = module {
    singleOf(::Database)
    singleOf(::ApiClient)
    singleOf(::UserRepository)
    viewModelOf(::UserViewModel)
}
```

Or with explicit wiring:

```kotlin
val appModule = module {
    single { Database() }
    single { ApiClient() }
    single { UserRepository(get(), get()) }
    viewModel { UserViewModel(get()) }
}
```

:::info
Classic DSL is not deprecated. Koin works perfectly with it. The Compiler Plugin adds compile-time analysis on top when you're ready to migrate.
:::

## Koin Annotations is now part of the Koin project

The `koin-annotations` library — `@Singleton`, `@Factory`, `@KoinViewModel`, `@Module`, `@ComponentScan`, and the rest — ships under the main Koin version and is fully supported. It is **not** deprecated.

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-core")
    implementation("io.insert-koin:koin-annotations") // same Koin version
}
```

Your annotations are processed by the **Koin Compiler Plugin** — see [Koin Compiler Plugin](/docs/intro/koin-compiler-plugin) and the [Annotations Reference](/docs/reference/koin-annotations/start).

## Koin KSP Compiler is deprecated in favor of Koin Compiler Plugin

:::info
The legacy KSP processor `koin-ksp-compiler` is **deprecated** and will be removed in a future Koin version. The replacement is the **Koin Compiler Plugin** — native K2 compiler integration, no generated files, simpler KMP setup.
:::

If you're using Koin Annotations with `koin-ksp-compiler`, migrate to the Compiler Plugin:

- **Same annotations** — No code changes needed
- **Better processing** — Native compiler integration, no generated files
- **Simpler setup** — No KSP configuration

See [Migrating from KSP to Compiler Plugin](/docs/migration/from-ksp-to-compiler-plugin).

## Runtime + Compile Safe = Best of Both Worlds

Koin is **runtime-based but performant and compile-safe**. This unique combination enables:

**Compile-time safety** (with Compiler Plugin):
- Validates your dependency graph during build
- Auto-detects constructor parameters
- Catches missing dependencies before runtime

**Runtime flexibility** (that compile-time-only frameworks can't offer):
- Dynamic module loading/unloading
- Lazy module loading (background)
- Feature flag driven injection
- Plugin architectures
- A/B testing with different implementations

```kotlin
// Dynamic module loading - impossible with Hilt
if (featureEnabled) {
    loadKoinModules(premiumFeatureModule)
}

// Later, if feature disabled
unloadKoinModules(premiumFeatureModule)
```

## Who is Koin For?

Koin is ideal for:

- **Teams who value productivity** - Less boilerplate, faster development
- **Android developers** wanting cleaner DI than Hilt/Dagger
- **Kotlin Multiplatform projects** - Android, iOS, Desktop, Web, Backend
- **Enterprise projects** that need to scale
- **Anyone who believes DI shouldn't be complicated**

## Next Steps

- **[What is Dependency Injection?](/docs/intro/what-is-dependency-injection)** - Learn DI fundamentals
- **[Koin Compiler Plugin](/docs/intro/koin-compiler-plugin)** - The recommended approach
- **[Setup Guide](/docs/setup/gradle)** - Add Koin to your project
- **[Tutorials](/docs/tutorials/your-first-app)** - Build your first app with Koin

---

### Migration From Ksp To Compiler Plugin (`docs/migration/from-ksp-to-compiler-plugin.md`)

---
title: Migrating Koin Annotations from KSP to Compiler Plugin
---

# Migrating Koin Annotations: KSP to Compiler Plugin

This guide helps you migrate your Koin Annotations project from KSP-based processing to the new Koin Compiler Plugin.

:::info Good News!
**Your annotations stay exactly the same.** Only the build configuration and Koin startup code change.
:::

## What's Different?

| Aspect | KSP Processing | Compiler Plugin |
|--------|----------------|-----------------|
| **Processing** | KSP (separate step) | K2 Compiler (integrated) |
| **Generated files** | Visible in `build/generated/ksp` | None - inline transformations |
| **Build speed** | Slower | Faster |
| **KMP setup** | Per-platform KSP configuration | Single plugin application |
| **Koin startup** | `modules(AppModule().module)` | `startKoin<MyApp>()` |
| **Future support** | Deprecated | Active development |

## Requirements

- **Kotlin 2.3.20+** (K2 compiler required)
- **Gradle 8.x+**

## Migration Steps

### Step 1: Update Kotlin Version

The Compiler Plugin requires Kotlin 2.3.20+:

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

### Step 2: Update Version Catalog

**Before (KSP):**
```toml
[versions]
koin = "4.0.0"
koin-ksp = "2.0.0"  # Separate versioning for KSP annotations
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):**
```toml
[versions]
koin = "4.2.0"
koin-plugin = "1.0.0"

[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" }
```

:::note
`koin-annotations` is now part of the main Koin project and uses the same version as `koin-core`.
:::

### Step 3: Update Build Configuration

**Before (KSP):**
```kotlin
// build.gradle.kts
plugins {
    alias(libs.plugins.ksp)
}

dependencies {
    implementation(libs.koin.core)
    implementation(libs.koin.annotations)  // KSP version (separate versioning)
    ksp(libs.koin.ksp.compiler)
}

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

**After (Compiler Plugin):**
```kotlin
// build.gradle.kts
plugins {
    alias(libs.plugins.koin.compiler)
}

dependencies {
    implementation(libs.koin.core)
    implementation(libs.koin.annotations)  // Same version as koin-core
}

// Optional configuration
koinCompiler {
    userLogs = true  // Log component detection
}
```

### Step 4: Update Koin Startup

This is the main code change. The KSP approach uses generated `.module` extensions, while the Compiler Plugin uses `@KoinApplication` with typed APIs.

**Before (KSP):**
```kotlin
import org.koin.ksp.generated.*  // Generated extensions

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

fun main() {
    startKoin {
        modules(AppModule().module)  // Generated .module extension
    }
}
```

**After (Compiler Plugin):**

```kotlin
// No generated imports needed

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

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

fun main() {
    startKoin<MyApp>()  // Typed API
}
```

#### Android Example

**Before (KSP):**
```kotlin
import org.koin.ksp.generated.*

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyApplication)
            modules(AppModule().module)
        }
    }
}
```

**After (Compiler Plugin):**
```kotlin
@KoinApplication(modules = [AppModule::class])
class MyApp

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

### Step 5: Clean Up

Remove KSP-generated files and rebuild:

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

## Annotations Stay the Same

All your annotated classes remain unchanged:

```kotlin
// No changes needed!
@Singleton
class UserRepository(private val database: Database)

@Factory
class GetUserUseCase(private val repository: UserRepository)

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

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

All annotations work identically. See **[Annotations Reference](/docs/reference/koin-annotations/definitions)** for the complete list.

### Import Change: `@KoinViewModel`

The `@KoinViewModel` annotation package has changed:

```kotlin
// Before (KSP)
import org.koin.android.annotation.KoinViewModel

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

### Top-Level Function Definitions (New)

The Compiler Plugin supports annotations on top-level functions, discovered by `@ComponentScan`:

```kotlin
@Singleton
fun provideDatabase(): DatabaseService = PostgresDatabase()

@Factory
fun provideCache(db: DatabaseService): CacheService = RedisCache(db)

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

Function return type determines the binding type. Function parameters are injected as dependencies.

## DSL Syntax Changes

If you use Koin DSL modules alongside annotations, the Compiler Plugin introduces a cleaner syntax:

| KSP / Classic Style | Compiler Plugin Style |
|---------------------|----------------------|
| `singleOf(::MyService)` | `single<MyService>()` |
| `factoryOf(::MyRepo)` | `factory<MyRepo>()` |
| `viewModelOf(::MyVM)` | `viewModel<MyVM>()` |
| `scopedOf(::MyScoped)` | `scoped<MyScoped>()` |
| `workerOf(::MyWorker)` | `worker<MyWorker>()` |
| `single { fn(get()) }` | `single { create(::fn) }` |

```kotlin
// Before
val myModule = module {
    singleOf(::MyService)
    factoryOf(::MyRepository)
    viewModelOf(::MyViewModel)
}

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

val myModule = module {
    single<MyService>()
    factory<MyRepository>()
    viewModel<MyViewModel>()
}

// Function builders — for external libraries (Room, Retrofit, etc.)
fun createDatabase(context: Context): AppDatabase =
    Room.databaseBuilder(context, AppDatabase::class.java, "db").build()

val dbModule = module {
    single { create(::createDatabase) }
}
```

:::note
The Compiler Plugin DSL is in package **`org.koin.plugin.module.dsl`**. Classic DSL remains in `org.koin.dsl`.
:::

## Cross-Module Discovery

Use `@Configuration` for automatic module discovery across Gradle modules:

```kotlin
// In feature module
@Module
@ComponentScan
@Configuration
class FeatureModule

// In app module - FeatureModule is auto-discovered
@KoinApplication
object MyApp

startKoin<MyApp>()  // FeatureModule automatically included
```

## KMP Migration

The Compiler Plugin greatly simplifies KMP setup.

**Before (KSP) - Per-platform configuration:**
```kotlin
// shared/build.gradle.kts
plugins {
    kotlin("multiplatform")
    id("com.google.devtools.ksp")
}

kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation("io.insert-koin:koin-core:$koin_version")
            implementation("io.insert-koin:koin-annotations:$koin_ksp_version")  // Separate version
        }
    }
}

dependencies {
    // KSP compiler required for each platform
    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")
}
```

**After (Compiler Plugin) - Single plugin:**
```kotlin
// shared/build.gradle.kts
plugins {
    kotlin("multiplatform")
    alias(libs.plugins.koin.compiler)
}

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

## Typed Startup APIs

The Compiler Plugin provides typed APIs: `startKoin<T>()`, `koinApplication<T>()`, `koinConfiguration<T>()`.

See **[Starting with Annotations](/docs/reference/koin-annotations/start)** for details.

## Configuration Labels (New)

The Compiler Plugin adds configuration labels for conditional module loading.

See **[Modules - Configuration](/docs/reference/koin-annotations/modules)** for details.

## Compiler Plugin Options

See **[Compiler Plugin Options](/docs/reference/koin-annotations/options)** for all configuration options.

## Troubleshooting

### Build fails after removing KSP

1. `./gradlew clean`
2. `rm -rf build/generated/ksp`
3. Invalidate IDE caches
4. Rebuild

### Annotations not detected

Enable logging:
```kotlin
koinCompiler {
    userLogs = true
}
```

### Missing dependencies at runtime

1. Check `@ComponentScan` packages
2. Verify modules in `@KoinApplication(modules = [...])`
3. Use `@Provided` for external dependencies

## Migration Checklist

- [ ] Update Kotlin to 2.3.20+
- [ ] Update Koin to 4.2.0+
- [ ] Remove KSP plugin
- [ ] Remove `koin-ksp-compiler` dependency
- [ ] Update `koin-annotations` to main Koin version (`io.insert-koin:koin-annotations:$koin_version`)
- [ ] Add Koin Compiler Plugin (`io.insert-koin.compiler.plugin`)
- [ ] Update `@KoinViewModel` import to `org.koin.core.annotation`
- [ ] Create `@KoinApplication` class and replace `modules(X().module)` with `startKoin<MyApp>()`
- [ ] Update DSL imports to `org.koin.plugin.module.dsl.*` if using DSL modules
- [ ] Update DSL syntax: `singleOf(::X)` → `single<X>()`
- [ ] Remove `import org.koin.ksp.generated.*`
- [ ] Clean and rebuild (`rm -rf build/generated/ksp && ./gradlew clean build`)

## See Also

- **[Compiler Plugin Setup](/docs/setup/compiler-plugin)** - Complete setup guide
- **[Annotations Reference](/docs/reference/koin-annotations/start)** - All annotations
- **[KSP Processor Setup (Deprecated)](/docs/setup/annotations-ksp)** — Legacy `koin-ksp-compiler` reference

---

### Quickstart Android Annotations (`docs/quickstart/android-annotations.md`)

---
title: Android & Annotations
---

> This tutorial lets you write an Android application and use Koin dependency injection to retrieve your components.
> You need around __10 min__ to do the tutorial.

:::note
update - 2024-10-21
:::

## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/android-annotations)
:::

## Gradle Setup

Let's configure the KSP Plugin like this, and the following dependencies:

```groovy
plugins {
    alias(libs.plugins.ksp)
}

dependencies {
    // ...

    implementation(libs.koin.annotations)
    ksp(libs.koin.ksp)
}

// Compile time check
ksp {
    arg("KOIN_CONFIG_CHECK","true")
}
```

:::note
See `libs.versions.toml` for current versions
:::

## Application Overview

The idea of the application is to manage a list of users, and display it in our `MainActivity` class with a Presenter or a ViewModel:

> Users -> UserRepository -> UserService -> (Presenter or ViewModel) -> MainActivity

## The "User" Data

We will manage a collection of Users. Here is the data class:

```kotlin
data class User(val name: String, val email: String)
```

We create a "Repository" component to manage the list of users (add users or find one by name). Here below, the `UserRepository` interface and its implementation:

```kotlin
interface UserRepository {
    fun findUserOrNull(name: String): User?
    fun addUsers(users: List<User>)
}

@Singleton
class UserRepositoryImpl : UserRepository {

    private val _users = arrayListOf<User>()

    override fun findUserOrNull(name: String): User? {
        return _users.firstOrNull { it.name == name }
    }

    override fun addUsers(users: List<User>) {
        _users.addAll(users)
    }
}
```

## The UserService Component

Let's write a service component to manage user operations:

```kotlin
interface UserService {
    fun getUserOrNull(name: String): User?
    fun loadUsers()
    fun prepareHelloMessage(user: User?): String
}

@Singleton
class UserServiceImpl(
    private val userRepository: UserRepository
) : UserService {

    init {
        loadUsers()
    }

    override fun getUserOrNull(name: String): User? = userRepository.findUserOrNull(name)

    override fun loadUsers() {
        userRepository.addUsers(listOf(
            User("Alice", "alice@example.com"),
            User("Bob", "bob@example.com"),
            User("Charlie", "charlie@example.com")
        ))
    }

    override fun prepareHelloMessage(user: User?): String {
        return user?.let { "Hello '${user.name}' (${user.email})! 👋" } ?: "❌ User not found"
    }
}
```

## The Koin module

Let's declare a `AppModule` module class like below:

```kotlin
@Module
@ComponentScan("org.koin.sample")
@Configuration
class AppModule
```

* `@Module` - Declares this class as a Koin module
* `@ComponentScan("org.koin.sample")` - Automatically scans and registers all Koin definitions in the `"org.koin.sample"` package
* `@Configuration` - Enables automatic module discovery when used with `@KoinApplication`

With component scanning enabled, we simply add annotations to our classes:

```kotlin
@Singleton
class UserRepositoryImpl : UserRepository {
    // ...
}

@Singleton
class UserServiceImpl(private val userRepository: UserRepository) : UserService {
    // ...
}
```

The `@Singleton` annotation declares these classes as singletons in Koin.

## Displaying User with Presenter

Let's write a presenter component to display a user:

```kotlin
@Factory
class UserPresenter(private val userService: UserService) {

    fun sayHello(name: String): String {
        val user = userService.getUserOrNull(name)
        val message = userService.prepareHelloMessage(user)
        return "[UserPresenter] $message"
    }
}
```

> UserService is referenced in UserPresenter's constructor

We declare `UserPresenter` with the `@Factory` annotation, to create a new instance each time it's requested (avoids memory leaks with Android lifecycle):

```kotlin
@Factory
class UserPresenter(private val userService: UserService) {
    // ...
}
```

## Injecting Dependencies in Android

The `UserPresenter` component will be created, resolving the `UserService` instance with it. To get it into our Activity, let's inject it with the `by inject()` delegate function: 

```kotlin
class MainActivity : AppCompatActivity() {

    private val presenter: UserPresenter by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        //...
    }
}
```

That's it, your app is ready.

:::info
The `by inject()` function allows us to retrieve Koin instances, in Android components runtime (Activity, fragment, Service...)
:::

## Start Koin

We need to start Koin with our Android application. With the `@KoinApplication` annotation, Koin automatically discovers and loads all modules marked with `@Configuration`:

```kotlin
import org.koin.android.ext.koin.androidContext
import org.koin.core.annotation.KoinApplication
import org.koin.ksp.generated.*

@KoinApplication
class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidContext(this@MainApplication)
        }
    }
}
```

**Key Points:**
* `@KoinApplication` - Automatically discovers all modules annotated with `@Module` and `@Configuration`
* No need to manually call `modules(AppModule().module)` - modules are loaded automatically!
* The `import org.koin.ksp.generated.*` import is required for generated Koin content
* You only need to configure Android-specific settings like `androidContext`

:::info
The `@KoinApplication` annotation works with `@Configuration` on your module to automatically discover and load all dependencies at compile time via KSP.
:::

## Displaying User with ViewModel

Let's write a ViewModel component to display a user:

```kotlin
@KoinViewModel
class UserViewModel(private val userService: UserService) : ViewModel() {

    fun sayHello(name: String): String {
        val user = userService.getUserOrNull(name)
        val message = userService.prepareHelloMessage(user)
        return "[UserViewModel] $message"
    }
}
```

> UserService is referenced in UserViewModel's constructor

The `UserViewModel` is tagged with `@KoinViewModel` annotation to declare the Koin ViewModel definition. This ensures proper lifecycle management and avoids memory leaks.


## Injecting ViewModel in Android

The `UserViewModel` component will be created, resolving the `UserService` instance with it. To get it into our Activity, let's inject it with the `by viewModel()` delegate function: 

```kotlin
class MainActivity : AppCompatActivity() {

    private val viewModel: UserViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        //...
    }
}
```

## Compile Time Checks

Koin Annotations allows to check your Koin configuration at compile time. This is available by using the following Gradle option:

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

:::tip
This KSP-based option has been replaced by the **Koin Compiler Plugin** which provides native compile-time safety. See [Compile-Time Safety](/docs/reference/koin-compiler/compile-safety) and [Compiler Plugin Setup](/docs/setup/compiler-plugin).
:::

---

### Quickstart Android Compose (`docs/quickstart/android-compose.md`)

---
title: Android - Jetpack Compose
---

> This tutorial lets you write an Android application with Jetpack Compose UI and use Koin dependency injection to retrieve your components.
> You need around __10 min__ to do the tutorial.

:::note
update - 2024-11-28
:::

## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/android-compose)
:::

## Gradle Setup

Add the Koin Android and Koin Compose dependencies like below:

```groovy
dependencies {

    // Koin for Android
    implementation("io.insert-koin:koin-android:$koin_version")
    // Koin for Jetpack Compose
    implementation("io.insert-koin:koin-androidx-compose:$koin_version")
}
```

## Application Overview

The idea of the application is to manage a list of users, and display it in our `MainActivity` class with a ViewModel and Jetpack Compose UI:

> Users -> UserRepository -> UserService -> UserViewModel -> MainActivity (Compose UI)

## The "User" Data

We will manage a collection of Users. Here is the data class:

```kotlin
data class User(val name: String, val email: String)
```

We create a "Repository" component to manage the list of users (add users or find one by name). Here below, the `UserRepository` interface and its implementation:

```kotlin
interface UserRepository {
    fun findUserOrNull(name: String): User?
    fun addUsers(users: List<User>)
}

class UserRepositoryImpl : UserRepository {

    private val _users = arrayListOf<User>()

    override fun findUserOrNull(name: String): User? {
        return _users.firstOrNull { it.name == name }
    }

    override fun addUsers(users: List<User>) {
        _users.addAll(users)
    }
}
```

## The UserService Component

Let's write a service component to manage user operations:

```kotlin
interface UserService {
    fun getUserOrNull(name: String): User?
    fun loadUsers()
    fun prepareHelloMessage(user: User?): String
}

class UserServiceImpl(
    private val userRepository: UserRepository
) : UserService {

    override fun getUserOrNull(name: String): User? = userRepository.findUserOrNull(name)

    override fun loadUsers() {
        userRepository.addUsers(listOf(
            User("Alice", "alice@example.com"),
            User("Bob", "bob@example.com"),
            User("Charlie", "charlie@example.com")
        ))
    }

    override fun prepareHelloMessage(user: User?): String {
        return user?.let { "Hello '${user.name}' (${user.email})! 👋" } ?: "❌ User not found"
    }
}
```

## The Koin module

Use the `module` function to declare a Koin module. A Koin module is the place where we define all our components to be injected.

```kotlin
val appModule = module {

}
```

Let's declare our components. We want singletons of `UserRepository` and `UserService`:

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
}
```

:::info
This tutorial uses the **Koin Compiler Plugin DSL** (`single<T>()`, `viewModel<T>()`) which provides auto-wiring at compile time. See [Compiler Plugin Setup](/docs/setup/compiler-plugin) for configuration.
:::

## Displaying User with ViewModel

Let's write a ViewModel component to display a user:

```kotlin
class UserViewModel(private val userService: UserService) : ViewModel() {

    fun sayHello(name: String): String {
        val user = userService.getUserOrNull(name)
        val message = userService.prepareHelloMessage(user)
        return "[UserViewModel] $message"
    }
}
```

> UserService is referenced in UserViewModel's constructor

We declare `UserViewModel` in our Koin module. We declare it as a `viewModel` definition, to not keep any instance in memory (avoid any leak with Android lifecycle):

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
    viewModel<UserViewModel>()
}
```

## Injecting ViewModel in Jetpack Compose

With Jetpack Compose, we use `ComponentActivity` instead of `AppCompatActivity`, and we build our UI with composable functions instead of XML layouts.

The `UserViewModel` component will be created, resolving the `UserService` instance with it. To get it into our Compose UI, we use the `koinViewModel()` function:

```
/* Detailed source-code truncated for AI context efficiency. */
```

That's it, your Compose app is ready!

:::info
The `koinViewModel()` function retrieves a ViewModel instance from Koin and automatically binds it to the Compose lifecycle. This is the Compose-specific way to inject ViewModels, replacing the `by viewModel()` delegate used in traditional Android Views.
:::

### Key Compose Concepts

- **ComponentActivity**: Base class for Compose apps (instead of AppCompatActivity)
- **setContent**: Sets the Composable content as the activity's UI
- **@Composable**: Functions that build UI declaratively
- **remember & mutableStateOf**: Compose state management for reactive UI updates
- **koinViewModel()**: Koin's Compose integration for ViewModel injection

## Start Koin

We need to start Koin with our Android application. Just call the `startKoin()` function in the application's main entry point, our `MainApplication` class:

```kotlin
class MainApplication : Application(){
    override fun onCreate() {
        super.onCreate()

        startKoin{
            androidLogger()
            androidContext(this@MainApplication)
            modules(appModule)
        }
    }
}
```

:::info
The `modules()` function in `startKoin` load the given list of modules
:::

## Koin module: DSL comparison

Here is the Koin module declaration using **Classic DSL** (manual wiring):

```kotlin
val appModule = module {
    single<UserRepository> { UserRepositoryImpl() }
    single<UserService> { UserServiceImpl(get()) }
    viewModel { UserViewModel(get()) }
}
```

With **Compiler Plugin DSL** (auto-wiring at compile time):

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
    viewModel<UserViewModel>()
}
```

:::tip
The Compiler Plugin DSL requires the [Koin Compiler Plugin](/docs/setup/compiler-plugin). It provides compile-time dependency resolution and cleaner syntax.
:::


## Compose vs XML Views

This tutorial demonstrates the same functionality as the [Android ViewModel tutorial](./android-viewmodel.md), but using Jetpack Compose instead of XML layouts:

| Aspect | XML Views | Jetpack Compose |
|--------|-----------|-----------------|
| Activity Base | `AppCompatActivity` | `ComponentActivity` |
| UI Definition | XML layout files | `@Composable` functions |
| ViewModel Injection | `by viewModel()` delegate | `koinViewModel()` function |
| State Management | LiveData/StateFlow | `remember` + `mutableStateOf` |
| UI Updates | View binding + observers | Automatic recomposition |

:::tip
For a version using Koin Annotations with Compose, see the [Compose Multiplatform Annotations tutorial](./compose-multiplatform-annotations.md).
:::

---

### Quickstart Android Viewmodel (`docs/quickstart/android-viewmodel.md`)

---
title: Android - ViewModel
---

> This tutorial lets you write an Android application and use Koin dependency injection to retrieve your components.
> You need around __10 min__ to do the tutorial.

:::note
update - 2024-10-21
:::

## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/android)
:::

## Gradle Setup

Add the Koin Android dependency like below:

```groovy
dependencies {

    // Koin for Android
    implementation("io.insert-koin:koin-android:$koin_version")
}
```

## Application Overview

The idea of the application is to manage a list of users, and display it in our `MainActivity` class with a Presenter or a ViewModel:

> Users -> UserRepository -> UserService -> (Presenter or ViewModel) -> MainActivity

## The "User" Data

We will manage a collection of Users. Here is the data class:

```kotlin
data class User(val name: String, val email: String)
```

We create a "Repository" component to manage the list of users (add users or find one by name). Here below, the `UserRepository` interface and its implementation:

```kotlin
interface UserRepository {
    fun findUserOrNull(name: String): User?
    fun addUsers(users: List<User>)
}

class UserRepositoryImpl : UserRepository {

    private val _users = arrayListOf<User>()

    override fun findUserOrNull(name: String): User? {
        return _users.firstOrNull { it.name == name }
    }

    override fun addUsers(users: List<User>) {
        _users.addAll(users)
    }
}
```

## The UserService Component

Let's write a service component to manage user operations:

```kotlin
interface UserService {
    fun getUserOrNull(name: String): User?
    fun loadUsers()
    fun prepareHelloMessage(user: User?): String
}

class UserServiceImpl(
    private val userRepository: UserRepository
) : UserService {

    override fun getUserOrNull(name: String): User? = userRepository.findUserOrNull(name)

    override fun loadUsers() {
        userRepository.addUsers(listOf(
            User("Alice", "alice@example.com"),
            User("Bob", "bob@example.com"),
            User("Charlie", "charlie@example.com")
        ))
    }

    override fun prepareHelloMessage(user: User?): String {
        return user?.let { "Hello '${user.name}' (${user.email})! 👋" } ?: "❌ User not found"
    }
}
```

## The Koin module

Use the `module` function to declare a Koin module. A Koin module is the place where we define all our components to be injected.

```kotlin
val appModule = module {

}
```

Let's declare our components. We want singletons of `UserRepository` and `UserService`:

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
}
```

:::info
This tutorial uses the **Koin Compiler Plugin DSL** (`single<T>()`, `viewModel<T>()`) which provides auto-wiring at compile time. See [Compiler Plugin Setup](/docs/setup/compiler-plugin) for configuration.
:::

## Displaying User with ViewModel

Let's write a ViewModel component to display a user:

```kotlin
class UserViewModel(private val userService: UserService) : ViewModel() {

    fun sayHello(name: String): String {
        val user = userService.getUserOrNull(name)
        val message = userService.prepareHelloMessage(user)
        return "[UserViewModel] $message"
    }
}
```

> UserService is referenced in UserViewModel's constructor

We declare `UserViewModel` in our Koin module. We declare it as a `viewModel` definition, to not keep any instance in memory (avoid any leak with Android lifecycle):

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
    viewModel<UserViewModel>()
}
```


## Injecting ViewModel in Android

The `UserViewModel` component will be created, resolving the `UserService` instance with it. To get it into our Activity, let's inject it with the `by viewModel()` delegate function: 

```kotlin
class MainActivity : AppCompatActivity() {

    private val viewModel: UserViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        //...
    }
}
```

That's it, your app is ready.

:::info
The `by viewModel()` function allows us to retrieve a ViewModel instances, create the associated ViewModel Factory for you and bind it to the lifecycle
:::

## Start Koin

We need to start Koin with our Android application. Just call the `startKoin()` function in the application's main entry point, our `MainApplication` class:

```kotlin
class MainApplication : Application(){
    override fun onCreate() {
        super.onCreate()
        
        startKoin{
            androidLogger()
            androidContext(this@MainApplication)
            modules(appModule)
        }
    }
}
```

:::info
The `modules()` function in `startKoin` load the given list of modules
:::

## Koin module: DSL comparison

Here is the Koin module declaration using **Classic DSL** (manual wiring):

```kotlin
val appModule = module {
    single<UserRepository> { UserRepositoryImpl() }
    single<UserService> { UserServiceImpl(get()) }
    viewModel { UserViewModel(get()) }
}
```

With **Compiler Plugin DSL** (auto-wiring at compile time):

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
    viewModel<UserViewModel>()
}
```

:::tip
The Compiler Plugin DSL requires the [Koin Compiler Plugin](/docs/setup/compiler-plugin). It provides compile-time dependency resolution and cleaner syntax.
:::

---

### Quickstart Android (`docs/quickstart/android.md`)

---
title: Android
---

> This tutorial lets you write an Android application and use Koin dependency injection to retrieve your components.
> You need around __10 min__ to do the tutorial.

:::note
update - 2024-10-21
:::

## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/android)
:::

## Gradle Setup

Add the Koin Android dependency like below:

```groovy
dependencies {

    // Koin for Android
    implementation("io.insert-koin:koin-android:$koin_version")
}
```

## Application Overview

The idea of the application is to manage a list of users, and display it in our `MainActivity` class with a Presenter or a ViewModel:

> Users -> UserRepository -> UserService -> (Presenter or ViewModel) -> MainActivity

## The "User" Data

We will manage a collection of Users. Here is the data class:

```kotlin
data class User(val name: String, val email: String)
```

We create a "Repository" component to manage the list of users (add users or find one by name). Here below, the `UserRepository` interface and its implementation:

```kotlin
interface UserRepository {
    fun findUserOrNull(name: String): User?
    fun addUsers(users: List<User>)
}

class UserRepositoryImpl : UserRepository {

    private val _users = arrayListOf<User>()

    override fun findUserOrNull(name: String): User? {
        return _users.firstOrNull { it.name == name }
    }

    override fun addUsers(users: List<User>) {
        _users.addAll(users)
    }
}
```

## The UserService Component

Let's write a service component to manage user operations:

```kotlin
interface UserService {
    fun getUserOrNull(name: String): User?
    fun loadUsers()
    fun prepareHelloMessage(user: User?): String
}

class UserServiceImpl(
    private val userRepository: UserRepository
) : UserService {

    override fun getUserOrNull(name: String): User? = userRepository.findUserOrNull(name)

    override fun loadUsers() {
        userRepository.addUsers(listOf(
            User("Alice", "alice@example.com"),
            User("Bob", "bob@example.com"),
            User("Charlie", "charlie@example.com")
        ))
    }

    override fun prepareHelloMessage(user: User?): String {
        return user?.let { "Hello '${user.name}' (${user.email})! 👋" } ?: "❌ User not found"
    }
}
```

## The Koin module

Use the `module` function to declare a Koin module. A Koin module is the place where we define all our components to be injected.

```kotlin
val appModule = module {

}
```

Let's declare our components. We want singletons of `UserRepository` and `UserService`:

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
}
```

:::info
This tutorial uses the **Koin Compiler Plugin DSL** (`single<T>()`, `factory<T>()`) which provides auto-wiring at compile time. See [Compiler Plugin Setup](/docs/setup/compiler-plugin) for configuration.
:::

## Displaying User with Presenter

Let's write a presenter component to display a user:

```kotlin
class UserPresenter(private val userService: UserService) {

    fun sayHello(name: String): String {
        val user = userService.getUserOrNull(name)
        val message = userService.prepareHelloMessage(user)
        return "[UserPresenter] $message"
    }
}
```

> UserService is referenced in UserPresenter's constructor

We declare `UserPresenter` in our Koin module. We declare it as a `factory` definition, to not keep any instance in memory (avoid any leak with Android lifecycle):

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
    factory<UserPresenter>()
}
```

## Injecting Dependencies in Android

The `UserPresenter` component will be created, resolving the `UserService` instance with it. To get it into our Activity, let's inject it with the `by inject()` delegate function: 

```kotlin
class MainActivity : AppCompatActivity() {

    private val presenter: UserPresenter by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        //...
    }
}
```

That's it, your app is ready.

:::info
The `by inject()` function allows us to retrieve Koin instances, in Android components runtime (Activity, fragment, Service...)
:::

## Start Koin

We need to start Koin with our Android application. Just call the `startKoin()` function in the application's main entry point, our `MainApplication` class:

```kotlin
class MainApplication : Application(){
    override fun onCreate() {
        super.onCreate()
        
        startKoin{
            androidLogger()
            androidContext(this@MainApplication)
            modules(appModule)
        }
    }
}
```

:::info
The `modules()` function in `startKoin` load the given list of modules
:::

## Koin module: DSL comparison

Here is the Koin module declaration using **Classic DSL** (manual wiring):

```kotlin
val appModule = module {
    single<UserRepository> { UserRepositoryImpl() }
    single<UserService> { UserServiceImpl(get()) }
    factory { UserPresenter(get()) }
}
```

With **Compiler Plugin DSL** (auto-wiring at compile time):

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
    factory<UserPresenter>()
}
```

:::tip
The Compiler Plugin DSL requires the [Koin Compiler Plugin](/docs/setup/compiler-plugin). It provides compile-time dependency resolution and cleaner syntax.
:::

---

### Quickstart Cmp (`docs/quickstart/cmp.md`)

---
title: Compose Multiplatform - Shared UI
---

> This tutorial demonstrates a Compose Multiplatform application that displays museum art from The Metropolitan Museum of Art Collection API. It uses Koin for dependency injection across Android and iOS platforms with shared UI.
> You need around __20 min__ to complete the tutorial.

:::note
update - 2024-11-12
:::

:::tip
Looking for the **annotations version** of this tutorial? Check out [Compose Multiplatform & Annotations](./compose-multiplatform-annotations.md) which uses Koin Annotations for compile-time verification and automatic module discovery.
:::

## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/compose)
:::

## Application Overview

The application fetches museum art objects from a remote API and displays them in a list. Users can tap on an item to see detailed information:

`MuseumAPI -> MuseumStorage -> MuseumRepository -> ViewModels -> Compose UI`

**Technologies used:**
- Compose Multiplatform for shared UI (Android & iOS)
- Ktor for HTTP networking
- Koin for dependency injection
- Kotlin Coroutines & Flow for async operations
- Navigation Compose for routing

## The Data Layer

> All the common/shared code is located in `composeApp` Gradle project

### MuseumObject Model

The museum art object data class:

```kotlin
@Serializable
data class MuseumObject(
    val objectID: Int,
    val title: String,
    val artistDisplayName: String,
    val medium: String,
    val dimensions: String,
    val objectURL: String,
    val objectDate: String,
    val primaryImage: String,
    val primaryImageSmall: String,
    val repository: String,
    val department: String,
    val creditLine: String,
)
```

### MuseumApi - Network Layer

We create an API interface to fetch data from The Metropolitan Museum of Art API:

```kotlin
interface MuseumApi {
    suspend fun getData(): List<MuseumObject>
}

class KtorMuseumApi(private val client: HttpClient) : MuseumApi {
    private companion object {
        const val API_URL = "https://raw.githubusercontent.com/Kotlin/KMP-App-Template/main/list.json"
    }

    override suspend fun getData(): List<MuseumObject> {
        return try {
            client.get(API_URL).body()
        } catch (e: Exception) {
            if (e is CancellationException) throw e
            e.printStackTrace()
            emptyList()
        }
    }
}
```

### MuseumStorage - Local Caching

We create a storage interface to cache museum objects locally:

```kotlin
interface MuseumStorage {
    suspend fun saveObjects(newObjects: List<MuseumObject>)
    fun getObjectById(objectId: Int): Flow<MuseumObject?>
    fun getObjects(): Flow<List<MuseumObject>>
}

class InMemoryMuseumStorage : MuseumStorage {
    private val storedObjects = MutableStateFlow(emptyList<MuseumObject>())

    override suspend fun saveObjects(newObjects: List<MuseumObject>) {
        storedObjects.value = newObjects
    }

    override fun getObjectById(objectId: Int): Flow<MuseumObject?> {
        return storedObjects.map { objects ->
            objects.find { it.objectID == objectId }
        }
    }

    override fun getObjects(): Flow<List<MuseumObject>> = storedObjects
}
```

### MuseumRepository

The repository coordinates between the API and storage:

```kotlin
class MuseumRepository(
    private val museumApi: MuseumApi,
    private val museumStorage: MuseumStorage,
) {
    private val scope = CoroutineScope(SupervisorJob())

    init {
        initialize()
    }

    fun initialize() {
        scope.launch {
            refresh()
        }
    }

    suspend fun refresh() {
        museumStorage.saveObjects(museumApi.getData())
    }

    fun getObjects(): Flow<List<MuseumObject>> = museumStorage.getObjects()

    fun getObjectById(objectId: Int): Flow<MuseumObject?> = museumStorage.getObjectById(objectId)
}
```

## The Shared Koin Modules

Use the `module` function to declare Koin modules. We organize our dependencies into separate modules for better structure.

:::info
This tutorial uses the **Koin Compiler Plugin DSL** (`single<T>()`, `viewModel<T>()`) which provides auto-wiring at compile time. See [Compiler Plugin Setup](/docs/setup/compiler-plugin) for configuration.
:::

### Data Module

```kotlin
val dataModule = module {
    // HttpClient for Ktor
    single { create(::buildClient) }

    // API, Storage, and Repository
    single<KtorMuseumApi>() bind MuseumApi::class
    single<InMemoryMuseumStorage>() bind MuseumStorage::class
    single<MuseumRepository>() withOptions { createdAtStart() }
}

private fun buildClient(): HttpClient {
    val json = Json { ignoreUnknownKeys = true }
    return HttpClient {
        install(ContentNegotiation) {
            json(json, contentType = ContentType.Any)
        }
    }
}
```

### ViewModel Module

Let's create ViewModels for our two screens:

```kotlin
// List screen ViewModel
class ListViewModel(museumRepository: MuseumRepository) : ViewModel() {
    val objects: StateFlow<List<MuseumObject>> =
        museumRepository.getObjects()
            .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
}

// Detail screen ViewModel
class DetailViewModel(private val museumRepository: MuseumRepository) : ViewModel() {
    fun getObject(objectId: Int): StateFlow<MuseumObject?> {
        return museumRepository.getObjectById(objectId)
            .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
    }
}
```

Declare them in the ViewModel module:

```kotlin
val viewModelModule = module {
    viewModel<ListViewModel>()
    viewModel<DetailViewModel>()
}
```

### Platform-Specific Module

For platform-specific components (Android vs iOS):

```kotlin
val nativeComponentModule = module {
    single<NativeComponent>()
}
```

### Main App Module

Combine all modules:

```kotlin
val appModule = module {
    includes(dataModule, viewModelModule, nativeComponentModule)
}
```

:::note
The Koin modules are organized and can be initialized from both Android and iOS using the `initKoin()` function.
:::


## Native Component

For platform-specific information (Android vs iOS), we use an expect/actual pattern:

```kotlin
// commonMain
interface NativeComponent {
    fun getInfo(): String
}

// androidMain
class NativeComponent {
    fun getInfo(): String = "Android ${android.os.Build.VERSION.SDK_INT}"
}

// iosMain
class NativeComponent {
    fun getInfo(): String = "iOS ${UIDevice.currentDevice.systemVersion}"
}
```

## Injecting ViewModels in Compose

> All the common Compose app is located in `commonMain` from `composeApp` Gradle module

The ViewModels are injected using `koinViewModel()` in Compose:

```kotlin
@Composable
fun App() {
    MaterialTheme(
        colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()
    ) {
        Surface {
            val navController: NavHostController = rememberNavController()
            NavHost(navController = navController, startDestination = ListDestination) {
                composable<ListDestination> {
                    val vm = koinViewModel<ListViewModel>()
                    ListScreen(viewModel = vm, navigateToDetails = { objectId ->
                        navController.navigate(DetailDestination(objectId))
                    })
                }
                composable<DetailDestination> { backStackEntry ->
                    val vm = koinViewModel<DetailViewModel>()
                    DetailScreen(
                        objectId = backStackEntry.toRoute<DetailDestination>().objectId,
                        viewModel = vm,
                        navigateBack = { navController.popBackStack() }
                    )
                }
            }
        }
    }
}
```

:::info
The `koinViewModel()` function retrieves ViewModel instances and binds them to the Compose lifecycle.
:::

## Starting Koin

Initialize Koin with the `initKoin()` function:

```kotlin
fun initKoin(configuration: KoinAppDeclaration? = null) {
    startKoin {
        includes(configuration)
        modules(appModule)
    }

    val platformInfo = KoinPlatform.getKoin().get<NativeComponent>().getInfo()
    println("Running on: $platformInfo")
}
```

### Android Setup

In Android, Koin is initialized from the main Activity or Application class:

```kotlin
// Call from Android entry point
initKoin()
```

### iOS Setup

> All the iOS app is located in `iosApp` folder

In iOS, initialize Koin from the SwiftUI App entry point:

```swift
@main
struct iOSApp: App {
    init() {
        KoinKt.doInitKoin()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
```

The Compose UI is started with:

```kotlin
fun MainViewController() = ComposeUIViewController { App() }
```

---

### Quickstart Compose Multiplatform Annotations (`docs/quickstart/compose-multiplatform-annotations.md`)

---
title: Compose Multiplatform & Annotations - Shared UI
---

> This tutorial demonstrates a Compose Multiplatform application that displays museum art from The Metropolitan Museum of Art Collection API. It uses Koin Annotations for dependency injection across Android and iOS platforms with shared UI.
> You need around __20 min__ to complete the tutorial.

:::note
update - 2024-11-12
:::

## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/compose-annotations)
:::

## Gradle Setup

First, add the Koin annotations dependencies:

```kotlin
plugins {
    id("com.google.devtools.ksp") version kspVersion
}

dependencies {
    // Koin for Compose Multiplatform
    implementation("io.insert-koin:koin-core:$koin_version")
    implementation("io.insert-koin:koin-compose-viewmodel:$koin_version")

    // Koin Annotations
    implementation("io.insert-koin:koin-annotations:$koin_annotations_version")
    ksp("io.insert-koin:koin-ksp-compiler:$koin_annotations_version")
}
```

## Application Overview

The application fetches museum art objects from a remote API and displays them in a list. Users can tap on an item to see detailed information:

`MuseumAPI -> MuseumStorage -> MuseumRepository -> ViewModels -> Compose UI`

**Technologies used:**
- Compose Multiplatform for shared UI (Android & iOS)
- Ktor for HTTP networking
- Koin Annotations for dependency injection
- Kotlin Coroutines & Flow for async operations
- Navigation Compose for routing

## The Data Layer

> All the common/shared code is located in `composeApp` Gradle project

### MuseumObject Model

The museum art object data class:

```kotlin
@Serializable
data class MuseumObject(
    val objectID: Int,
    val title: String,
    val artistDisplayName: String,
    val medium: String,
    val dimensions: String,
    val objectURL: String,
    val objectDate: String,
    val primaryImage: String,
    val primaryImageSmall: String,
    val repository: String,
    val department: String,
    val creditLine: String,
)
```

### MuseumApi - Network Layer

We create an API interface to fetch data:

```kotlin
interface MuseumApi {
    suspend fun getData(): List<MuseumObject>
}

@Single
class KtorMuseumApi(private val client: HttpClient) : MuseumApi {
    override suspend fun getData(): List<MuseumObject> {
        return try {
            client.get(API_URL).body()
        } catch (e: Exception) {
            if (e is CancellationException) throw e
            e.printStackTrace()
            emptyList()
        }
    }
}
```

### MuseumStorage - Local Caching

```kotlin
interface MuseumStorage {
    suspend fun saveObjects(newObjects: List<MuseumObject>)
    fun getObjectById(objectId: Int): Flow<MuseumObject?>
    fun getObjects(): Flow<List<MuseumObject>>
}

@Single
class InMemoryMuseumStorage : MuseumStorage {
    private val storedObjects = MutableStateFlow(emptyList<MuseumObject>())

    override suspend fun saveObjects(newObjects: List<MuseumObject>) {
        storedObjects.value = newObjects
    }

    override fun getObjectById(objectId: Int): Flow<MuseumObject?> {
        return storedObjects.map { objects ->
            objects.find { it.objectID == objectId }
        }
    }

    override fun getObjects(): Flow<List<MuseumObject>> = storedObjects
}
```

### MuseumRepository

The repository coordinates between the API and storage:

```kotlin
@Single(createdAtStart = true)
class MuseumRepository(
    private val museumApi: MuseumApi,
    private val museumStorage: MuseumStorage,
) {
    private val scope = CoroutineScope(SupervisorJob())

    init {
        initialize()
    }

    fun initialize() {
        scope.launch {
            refresh()
        }
    }

    suspend fun refresh() {
        museumStorage.saveObjects(museumApi.getData())
    }

    fun getObjects(): Flow<List<MuseumObject>> = museumStorage.getObjects()

    fun getObjectById(objectId: Int): Flow<MuseumObject?> = museumStorage.getObjectById(objectId)
}
```

:::note
The `@Single(createdAtStart = true)` annotation ensures the repository is created when Koin starts, triggering the data fetch immediately.
:::

## The Koin Modules

We organize our dependencies into separate modules:

### Data Module

```kotlin
@Module
@ComponentScan
class DataModule {

    @Single
    fun providesHttpClient(): HttpClient {
        val json = Json { ignoreUnknownKeys = true }
        return HttpClient {
            install(ContentNegotiation) {
                json(json, contentType = ContentType.Any)
            }
        }
    }
}
```

The `@ComponentScan` annotation automatically discovers all `@Single` annotated classes in this package (MuseumApi, MuseumStorage, MuseumRepository).

### ViewModel Module

Let's create ViewModels for our two screens:

```kotlin
// List screen ViewModel
@KoinViewModel
class ListViewModel(museumRepository: MuseumRepository) : ViewModel() {
    val objects: StateFlow<List<MuseumObject>> =
        museumRepository.getObjects()
            .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
}

// Detail screen ViewModel
@KoinViewModel
class DetailViewModel(private val museumRepository: MuseumRepository) : ViewModel() {
    fun getObject(objectId: Int): StateFlow<MuseumObject?> {
        return museumRepository.getObjectById(objectId)
            .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
    }
}
```

Declare them in a module:

```kotlin
@ComponentScan
@Module
class ViewModelModule
```

The `@KoinViewModel` annotation automatically registers these as ViewModel definitions, and `@ComponentScan` discovers them.

### Platform-Specific Module

For platform-specific components (Android vs iOS):

```kotlin
@ComponentScan
@Module
class PlatformComponentModule
```

### Main App Module

Combine all modules:

```kotlin
@Configuration
@Module(includes = [DataModule::class, ViewModelModule::class, PlatformComponentModule::class])
class AppModule
```

* `@Configuration` - Enables automatic module discovery with `@KoinApplication`
* `@Module(includes = [...])` - Declares which modules to include

## Koin Application Object

Create a `@KoinApplication` object:

```kotlin
@KoinApplication
object KoinApp

fun initKoin(configuration: KoinAppDeclaration? = null) {
    KoinApp.startKoin {
        includes(configuration)
    }

    val platformInfo = KoinPlatform.getKoin().get<PlatformComponent>().getInfo()
    println("Running on: $platformInfo")
}
```

The `@KoinApplication` annotation generates a `startKoin()` extension function that automatically loads all modules.

## Injecting ViewModels in Compose

> All the common Compose app is located in `commonMain` from `composeApp` Gradle module

The ViewModels are injected using `koinViewModel()` in Compose:

```kotlin
@Composable
fun App() {
    MaterialTheme(
        colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()
    ) {
        Surface {
            val navController: NavHostController = rememberNavController()
            NavHost(navController = navController, startDestination = ListDestination) {
                composable<ListDestination> {
                    val vm = koinViewModel<ListViewModel>()
                    ListScreen(viewModel = vm, navigateToDetails = { objectId ->
                        navController.navigate(DetailDestination(objectId))
                    })
                }
                composable<DetailDestination> { backStackEntry ->
                    val vm = koinViewModel<DetailViewModel>()
                    DetailScreen(
                        objectId = backStackEntry.toRoute<DetailDestination>().objectId,
                        viewModel = vm,
                        navigateBack = { navController.popBackStack() }
                    )
                }
            }
        }
    }
}
```

:::info
The `koinViewModel()` function retrieves ViewModel instances automatically registered via `@KoinViewModel`.
:::

## Starting Koin

### Android Setup

In Android, Koin is initialized from the main entry point:

```kotlin
// Call from Android entry point
initKoin()
```

### iOS Setup

> All the iOS app is located in `iosApp` folder

In iOS, initialize Koin from the SwiftUI App entry point:

```swift
@main
struct iOSApp: App {
    init() {
        KoinKt.doInitKoin()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
```

The Compose UI is started with:

```kotlin
fun MainViewController() = ComposeUIViewController { App() }
```

## Annotations vs Compiler Plugin DSL

Here's how the annotations approach compares to the Compiler Plugin DSL:

**With Annotations (compose-annotations/):**
```kotlin
@Configuration
@Module(includes = [DataModule::class, ViewModelModule::class])
class AppModule

@Single
class MuseumRepository(api: MuseumApi, storage: MuseumStorage)

@KoinViewModel
class ListViewModel(repository: MuseumRepository) : ViewModel()

// Start Koin
KoinApp.startKoin()
```

**Compiler Plugin DSL (compose/):**
```kotlin
val appModule = module {
    includes(dataModule, viewModelModule)
}

val dataModule = module {
    single<MuseumRepository>() withOptions { createdAtStart() }
}

val viewModelModule = module {
    viewModel<ListViewModel>()
}

// Start Koin
startKoin { modules(appModule) }
```

Both approaches achieve the same result:
- **Annotations**: Compile-time verification via KSP, automatic module discovery
- **Compiler Plugin DSL**: Auto-wiring at compile time, cleaner `single<T>()` syntax

---

### Quickstart Junit Test (`docs/quickstart/junit-test.md`)

---
title: JUnit Tests
---

> This tutorial lets you test a Kotlin application and use Koin inject and retrieve your components.

:::note
update - 2025-01-28
:::

## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/kotlin)
:::

## Gradle Setup

First, add the Koin dependency like below:

```groovy
dependencies {
    // Koin testing tools
    testImplementation "io.insert-koin:koin-test:$koin_version"
    // Needed JUnit version
    testImplementation "io.insert-koin:koin-test-junit4:$koin_version"
}
```

## Declared dependencies

We reuse the `koin-core` getting-started project, to use the koin module:

```kotlin
val appModule = module {
    single<UserApplication>()
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
}
```

## Verifying your Modules

:::tip
The Koin Compiler Plugin now provides compile-time dependency validation, catching missing dependencies at build time without writing test code. See [Compile-Time Safety](/docs/reference/koin-compiler/compile-safety).
:::

If you're not using the compiler plugin, you can verify modules at runtime. The `verify()` function performs a dry-run check to ensure all dependencies can be resolved:

```kotlin
class ModuleVerificationTest : AutoCloseKoinTest() {

    @Test
    fun verifyModules() {
        appModule.verify()
    }
}
```

This test will fail if any dependency definitions are invalid or if any required dependencies are missing.

## Writing Tests with KoinTestRule

To write tests that inject dependencies, extend `KoinTest` and use `KoinTestRule`:

```kotlin
class UserAppTest : KoinTest {

    val userService by inject<UserService>()
    val userRepository by inject<UserRepository>()

    @get:Rule
    val koinTestRule = KoinTestRule.create {
        printLogger()
        modules(appModule)
    }

    @Test
    fun `test user service`() {
        // Load users via service
        userService.loadUsers()

        // Verify user can be found
        val user = userService.getUserOrNull("Alice")
        assertNotNull(user)
        assertEquals("Alice", user?.name)
    }
}
```

> We use the `KoinTestRule` to start/stop our Koin context for each test

## Mocking Dependencies

You can mock dependencies in your tests using `declareMock`. This replaces the real implementation with a mock:

```kotlin
class UserMockTest : KoinTest {

    @get:Rule
    val koinTestRule = KoinTestRule.create {
        printLogger(Level.DEBUG)
        modules(appModule)
    }

    @get:Rule
    val mockProvider = MockProviderRule.create { clazz ->
        Mockito.mock(clazz.java)
    }

    @Test
    fun `mock test`() {
        // Declare a mock for UserRepository
        val repository = declareMock<UserRepository> {
            given(findUserOrNull(anyString())).willReturn(
                User("Mock", "mock@example.com")
            )
        }

        // Use the application with mocked repository
        getKoin().get<UserApplication>().sayHello("Mock")

        // Verify the mock was called
        Mockito.verify(repository, times(1)).findUserOrNull(anyString())
    }
}
```

The `MockProviderRule` configures Mockito as the mocking framework, and `declareMock` replaces the real `UserRepository` with a mock that returns controlled data.

## Key Testing Concepts

| Concept | Description |
|---------|-------------|
| `KoinTest` | Interface to extend for Koin testing support |
| `AutoCloseKoinTest` | Auto-closes Koin after each test |
| `KoinTestRule` | JUnit rule to start/stop Koin context |
| `MockProviderRule` | Configures the mocking framework |
| `verify()` | Validates module configuration without running |
| `declareMock<T>()` | Replaces a definition with a mock |
| `by inject<T>()` | Lazily injects a dependency in tests |

## See Also

- **[Testing Reference](/docs/reference/koin-test/testing)** - Complete testing documentation
- **[Module Verification](/docs/reference/koin-test/verify)** - verify() and checkModules() details

---

### Quickstart Kotlin Annotations (`docs/quickstart/kotlin-annotations.md`)

---
title: Kotlin & Annotations
---

> This tutorial lets you write a Kotlin application and use Koin dependency injection with annotations to retrieve your components.
> You need around __10 min__ to do the tutorial.

:::note
update - 2024-11-12
:::

## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/kotlin-annotations)
:::

## Setup

First, check that the Koin annotations dependencies are added like below:

```groovy
plugins {
    id("com.google.devtools.ksp") version kspVersion
}

dependencies {
    // Koin for Kotlin apps
    implementation("io.insert-koin:koin-core:$koin_version")

    // Koin Annotations
    implementation("io.insert-koin:koin-annotations:$koin_annotations_version")
    ksp("io.insert-koin:koin-ksp-compiler:$koin_annotations_version")
}
```

## Application Overview

The idea of the application is to manage a list of users, and display it in our `UserApplication` class:

> Users -> UserRepository -> UserService -> UserApplication

## The "User" Data

We will manage a collection of Users. Here is the data class:

```kotlin
data class User(val name: String, val email: String)
```

We create a "Repository" component to manage the list of users (add users or find one by name). Here below, the `UserRepository` interface and its implementation:

```kotlin
interface UserRepository {
    fun findUserOrNull(name: String): User?
    fun addUsers(users: List<User>)
}

@Singleton
class UserRepositoryImpl : UserRepository {

    private val _users = arrayListOf<User>()

    override fun findUserOrNull(name: String): User? {
        return _users.firstOrNull { it.name == name }
    }

    override fun addUsers(users: List<User>) {
        _users.addAll(users)
    }
}
```

:::note
This project uses Koin's `@Singleton` annotation (from `org.koin.core.annotation`) to declare singleton components.
:::

## The Koin module

Use the `@Module` annotation to declare a Koin module:

```kotlin
@Module
@ComponentScan("org.koin.sample")
@Configuration
class AppModule
```

* `@Module` - Declares this as a Koin module
* `@ComponentScan("org.koin.sample")` - Scans and registers annotated classes from the package
* `@Configuration` - Enables automatic module discovery with `@KoinApplication`

Let's declare our components by adding the `@Singleton` annotation:

```kotlin
@Singleton
class UserRepositoryImpl : UserRepository {
    // ...
}
```

## The UserService Component

Let's write the UserService component to manage user operations:

```kotlin
interface UserService {
    fun getUserOrNull(name: String): User?
    fun loadUsers()
    fun prepareHelloMessage(user: User?): String
}

@Singleton
class UserServiceImpl(
    private val userRepository: UserRepository
) : UserService {

    override fun getUserOrNull(name: String): User? = userRepository.findUserOrNull(name)

    override fun loadUsers() {
        userRepository.addUsers(listOf(
            User("Alice", "alice@example.com"),
            User("Bob", "bob@example.com"),
            User("Charlie", "charlie@example.com")
        ))
    }

    override fun prepareHelloMessage(user: User?): String {
        return user?.let { "Hello '${user.name}' (${user.email})! 👋" } ?: "❌ User not found"
    }
}
```

> UserRepository is referenced in UserServiceImpl's constructor

We declare `UserService` with the `@Singleton` annotation.

## The UserApplication

The `UserApplication` class uses constructor injection to receive the `UserService`:

```kotlin
@Singleton
class UserApplication(
    private val userService: UserService
) {

    init {
        userService.loadUsers()
    }

    fun sayHello(name: String) {
        val user = userService.getUserOrNull(name)
        val message = userService.prepareHelloMessage(user)
        println(message)
    }
}
```

:::info
Constructor injection is the preferred way to inject dependencies. Koin will automatically resolve and inject the `UserService` when creating `UserApplication`.
:::

## Koin Application Object

Create a `@KoinApplication` object to mark the entry point for Koin's annotation-based configuration:

```kotlin
@KoinApplication
object KoinUserApplication
```

The `@KoinApplication` annotation works with the KSP processor to generate a `startKoin()` extension function for this object.

## Start Koin

We need to start Koin with our application. Just call the generated `startKoin()` function in the application's main entry point:

```kotlin
fun main() {
    KoinUserApplication.startKoin()

    val userApplication = KoinPlatform.getKoin().get<UserApplication>()
    userApplication.sayHello("Alice")
}
```

**Key Points:**
* `KoinUserApplication.startKoin()` - Generated function that automatically discovers and loads all modules
* No need to manually call `modules()` - all annotated dependencies are discovered at compile time!
* We retrieve the `UserApplication` instance from Koin using `KoinPlatform.getKoin().get<UserApplication>()`

:::info
The `@KoinApplication` annotation with `@Configuration` on the module automatically discovers and loads all annotated dependencies at compile time via KSP.
:::

## Annotations vs Compiler Plugin DSL

Here is how our annotations-based configuration compares to the Compiler Plugin DSL:

**With Annotations:**
```kotlin
@Module
@ComponentScan("org.koin.sample")
@Configuration
class AppModule

@Singleton
class UserApplication(private val userService: UserService)

@Singleton
class UserRepositoryImpl : UserRepository

@Singleton
class UserServiceImpl(private val userRepository: UserRepository) : UserService
```

**Compiler Plugin DSL (from kotlin.md):**
```kotlin
val appModule = module {
    single<UserApplication>()
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
}
```

Both approaches achieve the same result:
- **Annotations**: Compile-time verification via KSP, automatic module discovery
- **Compiler Plugin DSL**: Auto-wiring at compile time, cleaner `single<T>()` syntax

---

### Quickstart Kotlin (`docs/quickstart/kotlin.md`)

---
title: Kotlin
---

> This tutorial lets you write a Kotlin application and use Koin dependency injection to retrieve your components.
> You need around __10 min__ to do the tutorial.

:::note
update - 2024-10-21
:::

:::tip
Looking for the **annotations version** of this tutorial? Check out [Kotlin & Annotations](./kotlin-annotations.md) which uses Koin Annotations for compile-time verification and automatic module discovery.
:::

## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/kotlin)
:::

## Setup

First, check that the `koin-core` dependency is added like below:

```groovy
dependencies {
    
    // Koin for Kotlin apps
    implementation "io.insert-koin:koin-core:$koin_version"
}
```

## Application Overview

The idea of the application is to manage a list of users, and display it in our `UserApplication` class:

> Users -> UserRepository -> UserService -> UserApplication

## The "User" Data

We will manage a collection of Users. Here is the data class:

```kotlin
data class User(val name: String, val email: String)
```

We create a "Repository" component to manage the list of users (add users or find one by name). Here below, the `UserRepository` interface and its implementation:

```kotlin
interface UserRepository {
    fun findUserOrNull(name: String): User?
    fun addUsers(users: List<User>)
}

class UserRepositoryImpl : UserRepository {

    private val _users = arrayListOf<User>()

    override fun findUserOrNull(name: String): User? {
        return _users.firstOrNull { it.name == name }
    }

    override fun addUsers(users: List<User>) {
        _users.addAll(users)
    }
}
```

## The Koin module

Use the `module` function to declare a Koin module. A Koin module is the place where we define all our components to be injected.

```kotlin
val appModule = module {

}
```

Let's declare our first component. We want a singleton of `UserRepository`, by creating an instance of `UserRepositoryImpl`

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

:::info
This tutorial uses the **Koin Compiler Plugin DSL** (`single<T>()`) which provides auto-wiring at compile time. See [Compiler Plugin Setup](/docs/setup/compiler-plugin) for configuration.
:::

## The UserService Component

Let's write the UserService component to manage user operations:

```kotlin
interface UserService {
    fun getUserOrNull(name: String): User?
    fun loadUsers()
    fun prepareHelloMessage(user: User?): String
}

class UserServiceImpl(
    private val userRepository: UserRepository
) : UserService {

    override fun getUserOrNull(name: String): User? = userRepository.findUserOrNull(name)

    override fun loadUsers() {
        userRepository.addUsers(listOf(
            User("Alice", "alice@example.com"),
            User("Bob", "bob@example.com"),
            User("Charlie", "charlie@example.com")
        ))
    }

    override fun prepareHelloMessage(user: User?): String {
        return user?.let { "Hello '${user.name}' (${user.email})! 👋" } ?: "❌ User not found"
    }
}
```

> UserRepository is referenced in UserServiceImpl's constructor

We declare `UserService` in our Koin module. We declare it as a `single` definition:

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
}
```

## Injecting Dependencies in UserApplication

The `UserApplication` class will help bootstrap instances out of Koin. It will resolve the `UserService` through constructor injection:

```kotlin
class UserApplication(
    private val userService: UserService
) {

    init {
        userService.loadUsers()
    }

    // display our data
    fun sayHello(name: String) {
        val user = userService.getUserOrNull(name)
        val message = userService.prepareHelloMessage(user)
        println(message)
    }
}
```

That's it, your app is ready.

:::info
Constructor injection is the preferred way to inject dependencies in Kotlin applications. Koin will automatically resolve and inject the `UserService` when creating `UserApplication`.
:::


## Start Koin

We need to start Koin with our application and add `UserApplication` to our module. Just call the `startKoin()` function in the application's main entry point, our `main` function:

```kotlin
val appModule = module {
    single<UserApplication>()
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
}

fun main() {
    startKoin {
        modules(appModule)
    }

    val userApplication = KoinPlatform.getKoin().get<UserApplication>()
    userApplication.sayHello("Alice")
}
```

:::info
The `modules()` function in `startKoin` loads the given list of modules. We retrieve the `UserApplication` instance from Koin using `KoinPlatform.getKoin().get<UserApplication>()`.
:::

## Koin module: DSL comparison

Here is the Koin module declaration using **Classic DSL** (manual wiring):

```kotlin
val appModule = module {
    single { UserApplication(get()) }
    single<UserRepository> { UserRepositoryImpl() }
    single<UserService> { UserServiceImpl(get()) }
}
```

With **Compiler Plugin DSL** (auto-wiring at compile time):

```kotlin
val appModule = module {
    single<UserApplication>()
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
}
```

:::tip
The Compiler Plugin DSL requires the [Koin Compiler Plugin](/docs/setup/compiler-plugin). It provides compile-time dependency resolution and cleaner syntax.
:::

---

### Quickstart Ktor Annotations (`docs/quickstart/ktor-annotations.md`)

---
title: Ktor & Annotations
---

> Ktor is a framework for building asynchronous servers and clients in connected systems using the powerful Kotlin programming language. We will use Ktor here, to build a simple web application.

Let's go 🚀

:::note
update - 2024-10-21
:::

## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/ktor-annotations)
:::

## Gradle Setup

First, add the Koin dependency like below:

```kotlin
plugins {

    id("com.google.devtools.ksp") version kspVersion
}

dependencies {
    // Koin for Kotlin apps
    implementation("io.insert-koin:koin-ktor:$koin_version")
    implementation("io.insert-koin:koin-logger-slf4j:$koin_version")

    implementation("io.insert-koin:koin-annotations:$koinAnnotationsVersion")
    ksp("io.insert-koin:koin-ksp-compiler:$koinAnnotationsVersion")
}
```

## Application Overview

The idea of the application is to manage a list of users, and display it in our `UserApplication` class:

> Users -> UserRepository -> UserService -> UserApplication

## The "User" Data

We will manage a collection of Users. Here is the data class:

```kotlin
data class User(val name: String, val email: String)
```

We create a "Repository" component to manage the list of users (add users or find one by name). Here below, the `UserRepository` interface and its implementation:

```kotlin
interface UserRepository {
    fun findUserOrNull(name: String): User?
    fun addUsers(users: List<User>)
}

@Singleton
class UserRepositoryImpl : UserRepository {

    private val _users = arrayListOf<User>()

    override fun findUserOrNull(name: String): User? {
        return _users.firstOrNull { it.name == name }
    }

    override fun addUsers(users: List<User>) {
        _users.addAll(users)
    }
}
```

## The Koin module

Use the `@Module` annotation to declare a Koin module, from a given Kotlin class. A Koin module is the place where we define all our components to be injected.

```kotlin
@Module
@ComponentScan("org.koin.sample")
@Configuration
class AppModule
```

* `@Module` - Declares this as a Koin module
* `@ComponentScan("org.koin.sample")` - Scans and registers annotated classes from the package
* `@Configuration` - Enables automatic module discovery with `@KoinApplication`

:::note
This project uses Koin's `@Singleton` annotation (from `org.koin.core.annotation`) to declare singleton components.
:::

Let's declare our first component. We want a singleton of `UserRepository`, by creating an instance of `UserRepositoryImpl`. We tag it `@Singleton`:

```kotlin
@Singleton
class UserRepositoryImpl : UserRepository
```

## The UserService Component

Let's write the UserService component to manage user operations:

```kotlin
interface UserService {
    fun getUserOrNull(name: String): User?
    fun loadUsers()
    fun prepareHelloMessage(user: User?): String
}

@Singleton
class UserServiceImpl(
    private val userRepository: UserRepository
) : UserService {

    override fun getUserOrNull(name: String): User? = userRepository.findUserOrNull(name)

    override fun loadUsers() {
        userRepository.addUsers(listOf(
            User("Alice", "alice@example.com"),
            User("Bob", "bob@example.com"),
            User("Charlie", "charlie@example.com")
        ))
    }

    override fun prepareHelloMessage(user: User?): String {
        return user?.let { "Hello '${user.name}' (${user.email})! 👋" } ?: "❌ User not found"
    }
}
```

> UserRepository is referenced in UserServiceImpl's constructor

We declare `UserService` with the `@Singleton` annotation:

## HTTP Controller and Koin Application

Finally, we need to create a `@KoinApplication` object and configure our HTTP routes:

```kotlin
@KoinApplication
object KoinUserApplication
```

The `@KoinApplication` annotation marks this as the entry point for Koin's annotation-based configuration. The KSP processor generates configuration that can be used with `withConfiguration<T>()` to initialize Koin.

## Start and Inject

Now let's configure the Ktor application with Koin:

```kotlin
fun Application.main() {
    // Install Koin with generated configuration
    install(Koin) {
        slf4jLogger()
        withConfiguration<KoinUserApplication>()
    }

    // Lazy inject UserService
    val service by inject<UserService>()
    service.loadUsers()

    // Routing section
    routing {
        get("/hello") {
            val userName = call.queryParameters["name"] ?: "Alice"
            val user = service.getUserOrNull(userName)
            val message = service.prepareHelloMessage(user)
            call.respondText(message)
        }
    }
}
```

**Key Points:**
* `withConfiguration<KoinUserApplication>()` - Uses the generated Koin configuration from the annotated application object
* No need to manually call `modules(AppModule().module)` - it's included automatically!
* The `/hello` endpoint accepts an optional `name` query parameter

Let's start Ktor:

```kotlin
fun main(args: Array<String>) {
    embeddedServer(Netty, port = 8080) {
        main()
    }.start(wait = true)
}
```

That's it! You're ready to go. Check these URLs:
- `http://localhost:8080/hello` - Greets Alice (default user)
- `http://localhost:8080/hello?name=Bob` - Greets Bob

:::info
The `@KoinApplication` annotation with `@Configuration` on the module automatically discovers and loads all annotated dependencies at compile time.
:::

---

### Quickstart Ktor (`docs/quickstart/ktor.md`)

---
title: Ktor
---

> Ktor is a framework for building asynchronous servers and clients in connected systems using the powerful Kotlin programming language. We will use Ktor here, to build a simple web application.

Let's go 🚀

:::note
update - 2024-10-21
:::

:::tip
Looking for the **annotations version** of this tutorial? Check out [Ktor & Annotations](./ktor-annotations.md) which uses Koin Annotations with Jakarta `@Singleton` for compile-time verification.
:::

## Get the code

:::info
[The source code is available at on Github](https://github.com/InsertKoinIO/koin-getting-started/tree/main/ktor)
:::

## Gradle Setup

First, add the Koin dependency like below:

```kotlin
dependencies {
    // Koin for Kotlin apps
    implementation("io.insert-koin:koin-ktor:$koin_version")
    implementation("io.insert-koin:koin-logger-slf4j:$koin_version")
}
```

## Application Overview

The idea of the application is to manage a list of users, and display it in our `UserApplication` class:

> Users -> UserRepository -> UserService -> UserApplication

## The "User" Data

We will manage a collection of Users. Here is the data class:

```kotlin
data class User(val name: String, val email: String)
```

We create a "Repository" component to manage the list of users (add users or find one by name). Here below, the `UserRepository` interface and its implementation:

```kotlin
interface UserRepository {
    fun findUserOrNull(name: String): User?
    fun addUsers(users: List<User>)
}

class UserRepositoryImpl : UserRepository {

    private val _users = arrayListOf<User>()

    override fun findUserOrNull(name: String): User? {
        return _users.firstOrNull { it.name == name }
    }

    override fun addUsers(users: List<User>) {
        _users.addAll(users)
    }
}
```

## The Koin module

Use the `module` function to declare a Koin module. A Koin module is the place where we define all our components to be injected.

```kotlin
val appModule = module {

}
```

Let's declare our first component. We want a singleton of `UserRepository`, by creating an instance of `UserRepositoryImpl`

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

:::info
This tutorial uses the **Koin Compiler Plugin DSL** (`single<T>()`) which provides auto-wiring at compile time. See [Compiler Plugin Setup](/docs/setup/compiler-plugin) for configuration.
:::

## The UserService Component

Let's write the UserService component to manage user operations:

```kotlin
interface UserService {
    fun getUserOrNull(name: String): User?
    fun loadUsers()
    fun prepareHelloMessage(user: User?): String
}

class UserServiceImpl(
    private val userRepository: UserRepository
) : UserService {

    override fun getUserOrNull(name: String): User? = userRepository.findUserOrNull(name)

    override fun loadUsers() {
        userRepository.addUsers(listOf(
            User("Alice", "alice@example.com"),
            User("Bob", "bob@example.com"),
            User("Charlie", "charlie@example.com")
        ))
    }

    override fun prepareHelloMessage(user: User?): String {
        return user?.let { "Hello '${user.name}' (${user.email})! 👋" } ?: "❌ User not found"
    }
}
```

> UserRepository is referenced in UserServiceImpl's constructor

We declare `UserService` in our Koin module. We declare it as a `single` definition:

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
}
```

## HTTP Controller

Finally, we need an HTTP Controller to create the HTTP Route. In Ktor it will be expressed through a Ktor extension function:

```kotlin
fun Application.main() {

    // Lazy inject UserService
    val service by inject<UserService>()
    service.loadUsers()

    // Routing section
    routing {
        get("/hello") {
            val userName = call.queryParameters["name"] ?: "Alice"
            val user = service.getUserOrNull(userName)
            val message = service.prepareHelloMessage(user)
            call.respondText(message)
        }
    }
}
```

The `/hello` endpoint accepts an optional `name` query parameter. If not provided, it defaults to "Alice".

Example requests:
- `http://localhost:8080/hello` - Greets Alice (default)
- `http://localhost:8080/hello?name=Bob` - Greets Bob

## Declare your dependencies

Let's assemble our components with a Koin module:

```kotlin
val appModule = module {
    single<UserRepositoryImpl>() bind UserRepository::class
    single<UserServiceImpl>() bind UserService::class
}
```

## Start and Inject

Finally, let's start Koin from Ktor:

```kotlin
fun Application.main() {
    // Install Koin
    install(Koin) {
        modules(appModule)
    }

    // Lazy inject UserService
    val service by inject<UserService>()
    service.loadUsers()

    // Routing section
    routing {
        get("/hello") {
            val userName = call.queryParameters["name"] ?: "Alice"
            val user = service.getUserOrNull(userName)
            val message = service.prepareHelloMessage(user)
            call.respondText(message)
        }
    }
}
```

Let's start Ktor:

```kotlin
fun main(args: Array<String>) {
    embeddedServer(Netty, port = 8080) {
        main()
    }.start(wait = true)
}
```

That's it! You're ready to go. Check these URLs:
- `http://localhost:8080/hello` - Greets Alice (default user)
- `http://localhost:8080/hello?name=Bob` - Greets Bob

---

### Setup Annotations Ksp (`docs/setup/annotations-ksp.md`)

---
title: KSP Processor Setup (Deprecated)
---

# KSP Processor Setup for Koin Annotations

:::warning
**`koin-ksp-compiler` is deprecated.** The KSP-based processor for Koin Annotations is replaced by the [Koin Compiler Plugin](/docs/setup/compiler-plugin). **Koin Annotations itself is not deprecated** — the `koin-annotations` library is now part of the main Koin project and remains fully supported. Only the processor changes.
:::

:::info
**Your annotations stay the same** — only the build setup changes. See [Migration Guide](#migration-to-koin-compiler-plugin) below.
:::

## Why Migrate?

| Aspect | KSP Processor (`koin-ksp-compiler`) | Koin Compiler Plugin |
|--------|-------------------------------------|----------------------|
| **Generated files** | Visible in build/ | None |
| **Build speed** | ⚠️ Slower | Faster |
| **KMP setup** | ⚠️ Complex | Simple |
| **Future support** | ⚠️ Deprecated | ✅ Active development |
| **Your code** | ⚠️ Use generated extensions | Use Kotlin Compiler plugin dedicated API |

## When to Use the KSP Processor (Temporary)

Only use `koin-ksp-compiler` if you're:
- Stuck on Kotlin 1.x (upgrade recommended)
- Mid-migration and can't switch yet
- Have specific KSP requirements

## Current KSP Processor Setup (Reference)

If you must use the KSP processor, here's the setup:

### Gradle Setup

```kotlin
// build.gradle.kts
plugins {
    id("com.google.devtools.ksp") version "$ksp_version"
}

dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-core")
    implementation("io.insert-koin:koin-annotations:$koin_ksp_version")
    ksp("io.insert-koin:koin-ksp-compiler:$koin_ksp_version")
}
```

### Version Compatibility

| Koin Annotations | KSP Version | Kotlin Version |
|------------------|-------------|----------------|
| 1.4 | 1.9 | 1.9 |
| 2.0 | 2.0 | 2.0 |
| 2.1/2.2 | 2.1/2.2 | 2.1/2.2 |
| 2.3 | 2.3 | Independant |

### Basic Usage

```kotlin
@Single
class MyComponent

@Module
class MyModule

// Import generated extensions
import org.koin.ksp.generated.*

fun main() {
    startKoin {
        modules(MyModule().module)
    }
}
```

### KSP Options

```kotlin
// build.gradle.kts
ksp {
    arg("KOIN_CONFIG_CHECK", "true")  // Enable compile-time validation
}
```

:::tip
This KSP-based compile-time check has been replaced by native compile-time safety in the **Koin Compiler Plugin**. See [Compile-Time Safety](/docs/reference/koin-compiler/compile-safety) and [Compiler Plugin Setup](/docs/setup/compiler-plugin).
:::

### KMP Setup (Complex)

```kotlin
// shared/build.gradle.kts
plugins {
    id("com.google.devtools.ksp")
}

kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation("io.insert-koin:koin-core:$koin_version")
            implementation("io.insert-koin:koin-annotations:$koin_ksp_version")
        }
    }
}

dependencies {
    // Per-platform KSP configuration required
    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")
}
```

## Migration to Koin Compiler Plugin

### Step 1: Update Kotlin

Ensure you're using Kotlin 2.3.20+:

```kotlin
// build.gradle.kts
plugins {
    kotlin("jvm") version "2.3.20-Beta1" // or earlier
}
```

### Step 2: Remove KSP

Remove KSP plugin and dependencies:

```kotlin
// Remove these:
plugins {
    // id("com.google.devtools.ksp")  // Remove
}

dependencies {
    // ksp("io.insert-koin:koin-ksp-compiler:...")  // Remove
}
```

### Step 3: Add Compiler Plugin

See the **[Compiler Plugin Setup Guide](/docs/setup/compiler-plugin)** for detailed instructions.

### Step 4: Keep Your Code

**Your annotations stay exactly the same 👍**

```kotlin
// This code doesn't change!
@Singleton
class MyService(val repository: MyRepository)

@Factory
class MyRepository

@KoinViewModel
class MyViewModel(val service: MyService)

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

### Step 5: Update Koin Startup

With the Compiler Plugin, **no generated code is used**. Replace generated extensions with typed API:

**Before (KSP):**
```kotlin
import org.koin.ksp.generated.*

startKoin {
    modules(AppModule().module)  // Uses generated extension
}
```

**After (Compiler Plugin):**
```kotlin
@KoinApplication
@ComponentScan("com.myapp")
class MyApp

// Use typed API - no generated code!
startKoin<MyApp>()

// Or with configuration
startKoin<MyApp> {
    androidContext(this@MyApplication)
}
```

Available typed APIs:
- `startKoin<T>()` - Start Koin globally with application T
- `koinApplication<T>()` - Create isolated KoinApplication with T
- `koinConfiguration<T>()` - Create KoinConfiguration from T (for Compose KoinApplication, Ktor, etc.)

Where `T` is a class annotated with `@KoinApplication`.

### Step 6: Clean Up

Delete generated files:

```bash
rm -rf build/generated/ksp
```

Rebuild your project.

### What Stays the Same

| Annotation | Status |
|------------|--------|
| `@Singleton` / `@Single` | ✅ Same |
| `@Factory` | ✅ Same |
| `@Scoped` | ✅ Same |
| `@KoinViewModel` | ✅ Same |
| `@KoinWorker` | ✅ Same |
| `@Named` | ✅ Same |
| `@InjectedParam` | ✅ Same |
| `@Property` | ✅ Same |
| `@Module` | ✅ Same |
| `@ComponentScan` | ✅ Same |
| `@Configuration` | ✅ Same |

### What Changes

| Aspect | KSP Processor | Koin Compiler Plugin |
|--------|---------------|----------------------|
| Build plugin | `com.google.devtools.ksp` | `io.insert-koin.compiler.plugin` |
| Dependencies | `ksp("io.insert-koin:koin-ksp-compiler")` | None required (plugin only) |
| `koin-annotations` version | Separate (`koin-ksp` version) | Same as main Koin version |
| Generated files | Visible in `build/` | None |
| Koin startup | `modules(AppModule().module)` | `startKoin<MyApp>()` |
| KMP setup | Per-platform KSP | Just plugin |

## Timeline

:::warning
The `koin-ksp-compiler` processor will be removed in a future Koin version. We recommend migrating to the Koin Compiler Plugin as soon as possible. The `koin-annotations` library and your `@Singleton` / `@Factory` / `@Module` annotations are not going away — they're now processed by the Koin Compiler Plugin.
:::

## Help

If you encounter issues during migration:
- Check [Troubleshooting](/docs/reference/troubleshooting)
- Ask on [Slack](https://kotlinlang.slack.com/messages/koin/)
- Open an issue on [GitHub](https://github.com/InsertKoinIO/koin)

## Next Steps

- **[Migration Guide](/docs/migration/from-ksp-to-compiler-plugin)** - Step-by-step migration to Compiler Plugin
- **[Compiler Plugin Setup](/docs/setup/compiler-plugin)** - Complete setup guide

---

### Setup Annotations (`docs/setup/annotations.md`)

---
title: Koin Annotations
---

Setup Koin Annotations for your project

## Current Versions

You can find all Koin packages on [maven central](https://search.maven.org/search?q=io.insert-koin).

Here are the currently available Koin Annotations versions:

- **Stable**: [](https://mvnrepository.com/artifact/io.insert-koin/koin-annotations) - Use for production applications
- **Latest**: [](https://mvnrepository.com/artifact/io.insert-koin/koin-annotations) - Preview of upcoming features

## KSP Plugin

We need the [Google KSP](https://github.com/google/ksp) to work. Follow the official [KSP Setup documentation](https://kotlinlang.org/docs/ksp-quickstart.html).

Just add the Gradle plugin:
```kotlin
plugins {
    id("com.google.devtools.ksp") version "$ksp_version"
}
```

**KSP Compatibility**: Koin Annotations 2.3.1 requires KSP `2.3.2`

:::info
**KSP Versioning Change**: Starting from KSP 2.x, the version numbering is now independent from Kotlin versions. Use KSP 2.3.2 for Koin Annotations 2.3.1.
:::

## Using Version Catalogs (Recommended)

In your `gradle/libs.versions.toml`:

```toml
[versions]
koin-annotations = "2.3.1"  # Stable version
ksp = "2.3.2"  # Required for Koin Annotations 2.3.1

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

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

## Android & Ktor App KSP Setup

- use KSP Gradle plugin
- add dependency for koin annotations and koin ksp compiler
- set sourceSet

```kotlin
plugins {
    alias(libs.plugins.ksp)
}

dependencies {
    // Koin
    implementation("io.insert-koin:koin-android:$koin_version")
    // Koin Annotations
    implementation("io.insert-koin:koin-annotations:$koin_annotations_version")
    // Koin Annotations KSP Compiler
    ksp("io.insert-koin:koin-ksp-compiler:$koin_annotations_version")
}
```

Or with version catalogs:

```kotlin
plugins {
    alias(libs.plugins.ksp)
}

dependencies {
    // Koin
    implementation(libs.koin.android)
    // Koin Annotations
    implementation(libs.koin.annotations)
    // Koin Annotations KSP Compiler
    ksp(libs.koin.ksp.compiler)
}
```

## Kotlin Multiplatform Setup

In a standard Kotlin/Kotlin Multiplatform project, you need to setup KSP as follow:

- use KSP Gradle plugin
- add dependency in commonMain for koin annotations
- set sourceSet for commonMain
- add KSP dependencies tasks with koin compiler
- setup compilation task dependency to `kspCommonMainKotlinMetadata`

```kotlin
plugins {
    alias(libs.plugins.ksp)
}

kotlin {

    sourceSets {

        // Add Koin Annotations
        commonMain.dependencies {
            // Koin
            implementation("io.insert-koin:koin-core:$koin_version")
            // Koin Annotations
            api("io.insert-koin:koin-annotations:$koin_annotations_version")
        }
    }

    // KSP Common sourceSet
    sourceSets.named("commonMain").configure {
        kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
    }
}

// KSP Tasks
dependencies {
    add("kspCommonMainMetadata", "io.insert-koin:koin-ksp-compiler:$koin_annotations_version")
    add("kspAndroid", "io.insert-koin:koin-ksp-compiler:$koin_annotations_version")
    add("kspIosX64", "io.insert-koin:koin-ksp-compiler:$koin_annotations_version")
    add("kspIosArm64", "io.insert-koin:koin-ksp-compiler:$koin_annotations_version")
    add("kspIosSimulatorArm64", "io.insert-koin:koin-ksp-compiler:$koin_annotations_version")
}

// Trigger Common Metadata Generation from Native tasks
tasks.matching { it.name.startsWith("ksp") && it.name != "kspCommonMainKotlinMetadata" }.configureEach {
    dependsOn("kspCommonMainKotlinMetadata")
}
```

:::info
For complete KMP setup and architecture patterns, see [Koin Annotations KMP](/docs/reference/koin-annotations/kmp).
:::

## Next Steps

Setup complete! Continue to:

- [Getting Started with Koin Annotations](/docs/reference/koin-annotations/start) - Learn how to use annotations in your code
- [Annotations Definitions](/docs/reference/koin-annotations/definitions) - Detailed annotation reference
- [Annotations Inventory](/docs/reference/koin-annotations/annotations-inventory) - Complete list of available annotations

---

### Setup Compiler Plugin (`docs/setup/compiler-plugin.md`)

---
title: Compiler Plugin Setup
---

# Koin Compiler Plugin Setup

The **Koin Compiler Plugin** is the recommended approach for all new Kotlin 2.x projects. It provides auto-wiring, compile-time safety, and a cleaner DSL syntax.

## What is the Compiler Plugin?

The Koin Compiler Plugin is a **native Kotlin Compiler Plugin (K2)** that:

- Auto-detects constructor dependencies
- Provides compile-time analysis
- Works with both DSL and Annotations
- Generates no visible files

See [Introduction to Koin Compiler Plugin](/docs/intro/koin-compiler-plugin) for details on features and benefits.

:::tip IDE Plugin
Install the **[Koin IDE Plugin](https://plugins.jetbrains.com/plugin/26131-koin-dependency-injection-official-)** for Android Studio & IntelliJ IDEA — code navigation between definitions and injection points, live safety checks, and dependency graph visualization.
:::

## Requirements

- **Kotlin 2.3.20+** (K2 compiler)
- **Gradle 8.x+**

## Setup

### Step 1: Add Koin to Version Catalog

First, check latest versions:
- Koin: [](https://mvnrepository.com/artifact/io.insert-koin/koin-core)
- Koin Compiler Plugin: [](https://mvnrepository.com/artifact/io.insert-koin/koin-compiler-plugin)

Then, in your `gradle/libs.versions.toml`:

```toml
[versions]
koin = "<KOIN_VERSION>"
koin-plugin = "<KOIN_PLUGIN_VERSION>"

[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" }
```

### Step 2: Configure Settings

In your `settings.gradle.kts`:

```kotlin
pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
}
```

### Step 3: Apply the Plugin

In your module's `build.gradle.kts`:

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

dependencies {
    implementation(libs.koin.core)
    implementation(libs.koin.annotations)  // For annotation support
}
```

:::tip
**Using `@KoinViewModel` or `@KoinWorker`?** Those annotations need their runtime DSL to be on the classpath:

- `@KoinViewModel` → `implementation("io.insert-koin:koin-core-viewmodel")`
- `@KoinWorker` → `implementation("io.insert-koin:koin-android-workmanager")`

The compiler will fail with a clear error naming the missing artifact if you add the annotation without its runtime — no more silent `NoDefinitionFoundException` at startup.
:::

## Complete Example

### gradle/libs.versions.toml

```toml
[versions]
koin = "<KOIN_VERSION>"
koin-plugin = "<KOIN_PLUGIN_VERSION>"

[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" }
```

### settings.gradle.kts

```kotlin
pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
}
```

### build.gradle.kts

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

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

## Using the Compiler Plugin

### DSL Style

Import from the compiler plugin package:

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

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

:::info
The Compiler Plugin DSL is in package **`org.koin.plugin.module.dsl`**. Classic DSL remains in `org.koin.dsl`.
:::

### Annotation Style

Use annotations on your classes:

```kotlin
@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
```

### Starting Koin with Annotations

With the Compiler Plugin, use typed APIs to start Koin - **no generated code needed**:

```kotlin
@KoinApplication
@ComponentScan("com.myapp")
class MyApp

// Start Koin with typed API
startKoin<MyApp>()

// Or with additional configuration
startKoin<MyApp> {
    androidContext(this@MyApplication)
    printLogger()
}
```

**Available typed APIs:**

| API | Description |
|-----|-------------|
| `startKoin<T>()` | Start Koin globally with application T |
| `startKoin<T> { }` | Start Koin with application T and configuration block |
| `koinApplication<T>()` | Create isolated KoinApplication with T |
| `koinConfiguration<T>()` | Create KoinConfiguration from T (for Compose KoinApplication, Ktor, etc.) |

Where `T` is a class annotated with `@KoinApplication`.

**Loading individual modules:**

You can also load `@Module` classes directly without `@KoinApplication`, using `module<T>()` or `modules()`:

```kotlin
startKoin {
    module<NetworkModule>()                              // Load a single module
    modules(DataModule::class, CacheModule::class)       // Load multiple modules
}
```

| API | Description |
|-----|-------------|
| `module<T>()` | Load a single `@Module` class into the KoinApplication |
| `modules(vararg KClass)` | Load multiple `@Module` classes into the KoinApplication |

Where `T` / each `KClass` is a class annotated with `@Module`. This is useful for tests or when mixing annotation and DSL modules:

```kotlin
// In tests
@get:Rule
val koinTestRule = KoinTestRule.create {
    module<NetworkModule>()
}
```

## Configuration Options

Configure the compiler plugin in your `build.gradle.kts`:

```kotlin
koinCompiler {
    userLogs = true
    debugLogs = false
    unsafeDslChecks = true
}
```

### Available Options

| Option | Description | Default |
|--------|-------------|---------|
| `compileSafety` | Compile-time dependency validation (A2/A3/A4) | `true` |
| `strictSafety` | Force aggregator's safety pass to re-run on every build (bypasses Kotlin IC) | auto-detected on aggregator modules |
| `skipDefaultValues` | Skip injection for parameters with Kotlin default values | `true` |
| `userLogs` | Enable logs for component detection and DSL/annotation processing | `false` |
| `debugLogs` | Enable verbose debug logs for internal plugin processing | `false` |
| `unsafeDslChecks` | Validate that `create()` calls inside lambdas are the only instruction | `true` |

:::tip
Set `userLogs = true` during development to see which components are detected and processed by the plugin.
:::

## Compile-Time Safety

The Koin Compiler Plugin provides **compile-time dependency verification** — validating that all your dependencies can be resolved at build time rather than failing at runtime. This is enabled by default.

```kotlin
koinCompiler {
    compileSafety = true       // Enabled by default
    skipDefaultValues = true   // Enabled by default
}
```

The plugin validates your graph at three levels: per-module (A2), full graph at `startKoin<T>()` (A3), and every call site (A4). See [Compile-Time Safety](/docs/reference/koin-compiler/compile-safety) for full details.

## Multi-Module Projects

For projects with multiple Gradle modules:

### Library Module

```kotlin
// feature/build.gradle.kts
plugins {
    alias(libs.plugins.koin.compiler)
}

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

```kotlin
// feature/src/main/kotlin/FeatureModule.kt
@Module
@ComponentScan("com.myapp.feature")
class FeatureModule
```

### App Module

```kotlin
// app/build.gradle.kts
plugins {
    alias(libs.plugins.koin.compiler)
}

dependencies {
    implementation(project(":feature"))
    implementation(libs.koin.core)
    implementation(libs.koin.annotations)
}
```

```kotlin
// app/src/main/kotlin/MyModule.kt
@Module
@Configuration
class MyModule

// app/src/main/kotlin/MyApp.kt
@KoinApplication
class MyApp

class MainApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin<MyApp>()
    }
}
```

Use `@KoinApplication` for the main application class with typed startup APIs.

## Kotlin Multiplatform

The Compiler Plugin works with KMP projects:

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

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

## Troubleshooting

### Plugin Not Found

Ensure the plugin is in your plugin repositories:

```kotlin
// settings.gradle.kts
pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
}
```

### Kotlin Version Mismatch

The Compiler Plugin requires Kotlin 2.3.20+. Check your Kotlin version:

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

### Import Errors

Make sure you're importing from the correct package:

```kotlin
// Compiler Plugin DSL
import org.koin.plugin.module.dsl.*

// Classic DSL
import org.koin.dsl.*
```

### Incremental Compilation & Cache Issues

Like other Kotlin compiler plugins (e.g., Compose Compiler, Metro), the Koin Compiler Plugin operates at the IR level. Kotlin's incremental compilation may sometimes produce **stale or inconsistent results** after certain changes:

**Symptoms:**
- Compile safety errors that shouldn't appear (false positives)
- Missing compile safety errors after removing a definition (false negatives)
- `NoSuchMethodError` or `ClassNotFoundException` at runtime after refactoring

**When this typically happens:**
- Changing annotations on a class (`@Single` → `@Factory`, adding/removing `@Named`)
- Moving classes between packages (affects `@ComponentScan` discovery)
- Changing module `includes` or `@Configuration` labels
- Adding/removing definitions in a library module that another module depends on

**Fix:** Run a clean build:

```bash
./gradlew clean build
```

Or in Android Studio: **Build → Clean Project**, then **Build → Rebuild Project**.

:::tip
If you encounter unexpected compile safety errors after a refactor, try a clean build first. This is a known limitation of incremental compilation with compiler plugins — not specific to Koin.

For graph-level changes (DSL definitions inside `module { }` lambdas, classes added to `@ComponentScan` packages), the plugin's `strictSafety` option auto-enables on aggregator modules to force the full-graph safety pass to re-run each build. See [`strictSafety`](/docs/reference/koin-annotations/options#strictsafety) for details.
:::

### Compile Safety False Positives in Multi-Module Projects

If the plugin reports a missing dependency that exists in a library module, ensure:

1. **The library module also applies the Koin Compiler Plugin** — it generates hint functions that downstream modules read
2. **The library builds before the consuming module** — Gradle usually handles this via `implementation(project(":lib"))`, but check your task dependencies
3. **Run a clean build** after adding the plugin to a library module for the first time

## Migration

### From Classic DSL

1. Add the Compiler Plugin
2. Update imports to `org.koin.plugin.module.dsl.*`
3. Replace `single { Class(get() ...) }` or `singleOf(::Class)` with `single<Class>()`

See the [DSL Style](#dsl-style) reference above for the compile-time safe syntax.

### From the KSP Processor (`koin-ksp-compiler`)

1. Remove KSP plugin and `koin-ksp-compiler` dependency
2. Add the Koin Compiler Plugin
3. Update `startKoin { modules(...) }` to `startKoin<MyApp>()`
4. **Your annotations stay the same!** The `koin-annotations` library remains — only the processor changes.

See **[Migrating from KSP to Compiler Plugin](/docs/migration/from-ksp-to-compiler-plugin)** for the complete guide.

## Next Steps

- **[DSL Reference](/docs/reference/dsl-reference)** - Complete DSL documentation
- **[Annotations Reference](/docs/reference/annotations-reference)** - Complete annotations documentation
- **[Starting Koin](/docs/reference/koin-core/starting-koin)** - Configure your application

---

### Setup Gradle (`docs/setup/gradle.md`)

---
title: Gradle Setup
---

# Gradle Setup

This guide covers how to add Koin dependencies to your Gradle project.

## Koin BOM (Recommended)

The **Bill of Materials (BOM)** is the recommended way to manage Koin dependencies. It ensures all Koin libraries use compatible versions.

:::info
**Best Practice**: Always use the Koin BOM to avoid version conflicts between Koin libraries.
:::

### Using Version Catalogs (Recommended)

In your `gradle/libs.versions.toml`:

```toml
[versions]
koin-bom = "4.2.0"

[libraries]
koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin-bom" }
koin-core = { module = "io.insert-koin:koin-core" }
koin-android = { module = "io.insert-koin:koin-android" }
koin-compose = { module = "io.insert-koin:koin-compose" }
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel" }
koin-ktor = { module = "io.insert-koin:koin-ktor" }
koin-test = { module = "io.insert-koin:koin-test" }
```

In your `build.gradle.kts`:

```kotlin
dependencies {
    implementation(platform(libs.koin.bom))
    implementation(libs.koin.android)  // No version needed
}
```

### Using BOM Directly

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))

    // Add dependencies without versions
    implementation("io.insert-koin:koin-android")
    implementation("io.insert-koin:koin-compose")  // Works for Android & Multiplatform
}
```

## Platform-Specific Setup

### Kotlin/JVM {#kotlin}

For pure Kotlin applications:

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-core")
}
```

Start Koin in your application:

```kotlin
fun main() {
    startKoin {
        modules(appModule)
    }
}
```

**Testing dependencies:**

```kotlin
dependencies {
    testImplementation("io.insert-koin:koin-test")
    testImplementation("io.insert-koin:koin-test-junit5")  // or junit4
}
```

### Android {#android}

For Android applications:

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-android")
}
```

Start Koin in your Application class:

```kotlin
class MainApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger()
            androidContext(this@MainApplication)
            modules(appModule)
        }
    }
}
```

**Optional Android packages:**

```kotlin
dependencies {
    // Jetpack WorkManager
    implementation("io.insert-koin:koin-androidx-workmanager")

    // Navigation Graph
    implementation("io.insert-koin:koin-androidx-navigation")

    // AndroidX Startup
    implementation("io.insert-koin:koin-androidx-startup")

    // Java Compatibility
    implementation("io.insert-koin:koin-android-compat")
}
```

### Android with Jetpack Compose {#compose-android}

For Android apps using Jetpack Compose:

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-android")
    implementation("io.insert-koin:koin-compose")
    implementation("io.insert-koin:koin-compose-viewmodel")
}
```

**With Navigation:**

```kotlin
dependencies {
    // Navigation 2 (Android only)
    implementation("io.insert-koin:koin-androidx-compose-navigation")

    // Or Navigation 3
    implementation("io.insert-koin:koin-compose-navigation3")
}
```

:::info
The `koin-androidx-compose` is now covered by `koin-compose`
:::

### Compose Multiplatform {#compose}

For Compose Multiplatform projects (Android, iOS, Desktop, Web):

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-compose")
    implementation("io.insert-koin:koin-compose-viewmodel")
    implementation("io.insert-koin:koin-compose-viewmodel-navigation")
}
```

:::info
`koin-compose` includes Android support automatically. No need for separate `koin-android` in Compose Multiplatform projects.
:::

### Kotlin Multiplatform {#kotlin-multiplatform}

In your shared module's `build.gradle.kts`:

```kotlin
kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation(platform("io.insert-koin:koin-bom:$koin_version"))
            implementation("io.insert-koin:koin-core")
        }

        commonTest.dependencies {
            implementation("io.insert-koin:koin-test")
        }

        androidMain.dependencies {
            implementation("io.insert-koin:koin-android")
        }
    }
}
```

### Ktor {#ktor}

For Ktor server applications:

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-ktor")
    implementation("io.insert-koin:koin-logger-slf4j")
}
```

Install Koin in your Ktor application:

```kotlin
fun Application.module() {
    install(Koin) {
        slf4jLogger()
        modules(appModule)
    }
}
```

## All Available Packages

Last version is currently: [](https://mvnrepository.com/artifact/io.insert-koin/koin-core)

| Package | Description |
|---------|-------------|
| `koin-core` | Core Koin library |
| `koin-core-coroutines` | Coroutines support |
| `koin-android` | Android support |
| `koin-android-compat` | Java compatibility for Android |
| `koin-androidx-navigation` | Navigation Component support |
| `koin-androidx-workmanager` | WorkManager support |
| `koin-androidx-startup` | AndroidX Startup support |
| `koin-compose` | Compose (Android & Multiplatform) |
| `koin-compose-viewmodel` | ViewModel for Compose |
| `koin-compose-viewmodel-navigation` | Navigation + ViewModel for Compose MP |
| `koin-androidx-compose` | ⚠️ Superseedeed - use `koin-compose` instead |
| `koin-androidx-compose-navigation` | Navigation 2 for Android (not KMP compatible) |
| `koin-compose-navigation3` | Navigation 3 |
| `koin-ktor` | Ktor server support |
| `koin-logger-slf4j` | SLF4J logging |
| `koin-test` | Testing utilities |
| `koin-test-junit4` | JUnit 4 support |
| `koin-test-junit5` | JUnit 5 support |
| `koin-android-test` | Android instrumented testing |

## Direct Version Specification

If you prefer not to use the BOM:

```kotlin
dependencies {
    implementation("io.insert-koin:koin-core:$koin_version")
    implementation("io.insert-koin:koin-android:$koin_version")
}
```

:::note
This approach requires manually keeping all dependencies synchronized. **Using the BOM is strongly recommended.**
:::

## Next Steps

- **[Compiler Plugin Setup](/docs/setup/compiler-plugin)** - Add compile-time safety
- **[Starting Koin](/docs/reference/koin-core/starting-koin)** - Configure your application
- **[Tutorials](/docs/quickstart/kotlin)** - Build your first app

---

### Setup Koin (`docs/setup/koin.md`)

---
title: Koin
---

All you need to setting up Koin in your project

## Current Versions

You can find all Koin packages on [Maven Central](https://central.sonatype.com/search?q=io.insert-koin+koin-core&sort=name).

Here are the currently available Koin versions:

- Koin Stable [](https://mvnrepository.com/artifact/io.insert-koin/koin-core)
- Koin Latest [](https://mvnrepository.com/artifact/io.insert-koin/koin-core)

## Koin BOM (Recommended)

:::info
**Best Practice**: Use the Koin Bill of Materials (BOM) to manage all Koin library versions consistently. This is the recommended approach for all projects.
:::

The Koin Bill of Materials (BOM) lets you manage all of your Koin library versions by specifying only the BOM's version. The BOM itself has links to the stable versions of the different Koin libraries, in such a way that they work well together. When using the BOM in your app, you don't need to add any version to the Koin library dependencies themselves. When you update the BOM version, all the libraries that you're using are automatically updated to their new versions.

### Using BOM with Version Catalogs (Recommended)

In your `gradle/libs.versions.toml`:

```toml
[versions]
koin-bom = "4.1.1"  # Stable version

[libraries]
koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin-bom" }
koin-core = { module = "io.insert-koin:koin-core" }
koin-android = { module = "io.insert-koin:koin-android" }
koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose" }
koin-compose = { module = "io.insert-koin:koin-compose" }
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel" }
koin-ktor = { module = "io.insert-koin:koin-ktor" }
koin-test = { module = "io.insert-koin:koin-test" }
```

In your `build.gradle.kts`:

```kotlin
dependencies {
    implementation(platform(libs.koin.bom))
    implementation(libs.koin.core)
    // Add other Koin dependencies without versions
}
```

### Using BOM Without Version Catalogs

```kotlin
dependencies {
    // Declare koin-bom version
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))

    // Declare the koin dependencies without versions
    implementation("io.insert-koin:koin-android")
    implementation("io.insert-koin:koin-core-coroutines")
    implementation("io.insert-koin:koin-androidx-workmanager")

    // If you need to specify a different version for a specific dependency
    implementation("io.insert-koin:koin-androidx-navigation:1.2.3-alpha03")

    // Works with test libraries too!
    testImplementation("io.insert-koin:koin-test-junit4")
    testImplementation("io.insert-koin:koin-android-test")
}
```

## Platform-Specific Setup

### Kotlin

Add the Koin BOM and `koin-core` dependency to your application:

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-core")
}
```

Or specify the exact dependency version (not recommended):

```kotlin
dependencies {
    implementation("io.insert-koin:koin-core:$koin_version")
}
```

You are now ready to start Koin:

```kotlin
fun main() {
    startKoin {
        modules(...)
    }
}
```

If you need testing capacity:

```kotlin
dependencies {
    // Koin Test features
    testImplementation("io.insert-koin:koin-test:$koin_version")
    // Koin for JUnit 4
    testImplementation("io.insert-koin:koin-test-junit4:$koin_version")
    // Koin for JUnit 5
    testImplementation("io.insert-koin:koin-test-junit5:$koin_version")
}
```

:::info
**Next Steps**: Continue with [Kotlin App Tutorial](/docs/quickstart/kotlin) or explore [Core Features](/docs/reference/koin-core/dsl).
:::

### Android

Add `koin-android` dependency to your Android application:

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-android")
}
```

You are now ready to start Koin in your `Application` class:

```kotlin
class MainApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger()
            androidContext(this@MainApplication)
            modules(appModule)
        }
    }
}
```

If you need extra features, add the following packages:

```kotlin
dependencies {
    // Java Compatibility
    implementation("io.insert-koin:koin-android-compat")
    // Jetpack WorkManager
    implementation("io.insert-koin:koin-androidx-workmanager")
    // Navigation Graph
    implementation("io.insert-koin:koin-androidx-navigation")
    // App Startup - Start Koin with AndroidX Startup
    implementation("io.insert-koin:koin-androidx-startup")
}
```

:::info
**Next Steps**: Continue with [Android App Tutorial](/docs/quickstart/android-viewmodel) or see [Starting Koin on Android](/docs/reference/koin-android/start) for detailed integration.
:::

### Jetpack Compose or Compose Multiplatform

For **Compose Multiplatform** (Android, iOS, Desktop, Web), add these dependencies:

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-compose")
    implementation("io.insert-koin:koin-compose-viewmodel")
    implementation("io.insert-koin:koin-compose-viewmodel-navigation")
}
```

For **pure Android Jetpack Compose**, you can use:

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    implementation("io.insert-koin:koin-androidx-compose")
    implementation("io.insert-koin:koin-androidx-compose-navigation")
}
```

For **Navigation 3 integration** (experimental):

```kotlin
dependencies {
    // Navigation 3 support (alpha)
    implementation("io.insert-koin:koin-compose-navigation3")
}
```

:::warning
Navigation 3 is in alpha. See [Navigation 3 Integration](/docs/reference/koin-compose/navigation3) for details.
:::

:::info
**Next Steps**: Continue with [Compose Tutorial](/docs/quickstart/android-compose) or see [Koin Compose](/docs/reference/koin-compose/compose) for detailed integration.
:::

### Kotlin Multiplatform

In your `shared/build.gradle.kts`, add `koin-core` dependency to commonMain:

```kotlin
kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation(platform("io.insert-koin:koin-bom:$koin_version"))
            implementation("io.insert-koin:koin-core")
        }

        commonTest.dependencies {
            implementation("io.insert-koin:koin-test")
        }
    }
}
```

:::info
**Next Steps**: See [Kotlin Multiplatform with Koin](/docs/reference/koin-mp/kmp) for platform-specific setup, expect/actual patterns, and architecture guidance.
:::

### Ktor

Add `koin-ktor` dependency to your Ktor application:

```kotlin
dependencies {
    implementation(platform("io.insert-koin:koin-bom:$koin_version"))
    // Koin for Ktor
    implementation("io.insert-koin:koin-ktor")
    // SLF4J Logger
    implementation("io.insert-koin:koin-logger-slf4j")
}
```

You are now ready to install Koin feature into your Ktor application:

```kotlin
fun Application.main() {
    install(Koin) {
        slf4jLogger()
        modules(appModule)
    }
}
```

:::info
**Next Steps**: Continue with [Ktor App Tutorial](/docs/quickstart/ktor) or see [Ktor Integration](/docs/reference/koin-ktor/ktor) for detailed setup.
:::

## Alternative: Direct Version Specification

If you prefer not to use the BOM, you can specify versions directly for each dependency:

```kotlin
dependencies {
    implementation("io.insert-koin:koin-core:$koin_version")
    implementation("io.insert-koin:koin-android:$koin_version")
    implementation("io.insert-koin:koin-compose:$koin_version")
}
```

:::note
This approach requires manually keeping all Koin dependencies synchronized to compatible versions. **Using the BOM is strongly recommended** to avoid version conflicts.
:::

---

### Setup Migrate (`docs/setup/migrate.md`)

---
title: Migration Guides (Deprecated)
---

Follow new [Releases Guide](../support/releases.md) to help your migration process

---

### Setup Why (`docs/setup/why.md`)

---
title: Why Koin?
---

Koin provides an easy and efficient way to incorporate dependency injection into any Kotlin application (Multiplatform, Android, Backend, etc.)

## Goals of Koin

The goals of Koin are:
- **Simplify** your Dependency Injection infrastructure with smart API
- **Kotlin DSL** easy to read, easy to use, to let you write any kind of application
- **Ecosystem Integration** - Provides different kind of integration from Android ecosystem, to more backend needs like Ktor
- **Flexibility** - Allow to be used with or without annotations

---

## Koin in a Nutshell

### Making Your Kotlin Development Easy and Productive

Koin is a smart Kotlin dependency injection library to keep you focused on your app, not on your tools.

```kotlin
class MyRepository()
class MyPresenter(val repository : MyRepository)

// just declare it
val myModule = module {
  singleOf(::MyPresenter)
  singleOf(::MyRepository)
}
```

Koin gives you simple tools and API to let you build, assemble Kotlin related technologies into your application and let you scale your business with easiness.

```kotlin
fun main() {

  // Just start Koin
  startKoin {
    modules(myModule)
  }
}
```

---

## Platform Support

### Ready for Android

Thanks to the Kotlin language, Koin extends the Android platform and provides new features as part of the original platform.

```kotlin
class MyApplication : Application() {
  override fun onCreate() {
    super.onCreate()

    startKoin {
      androidLogger()
      androidContext(this@MyApplication)
      modules(myModule)
    }
  }
}
```

Koin provides easy and powerful API to retrieve your dependencies anywhere in Android components, with just using `by inject()` or `by viewModel()`

```kotlin
class MyActivity : AppCompatActivity() {

  val myPresenter : MyPresenter by inject()

}
```

:::info
**Learn More**: [Starting Koin on Android](/docs/reference/koin-android/start)
:::

### Powering Kotlin Multiplatform

Sharing code between mobile platforms is one of the major Kotlin Multiplatform use cases. With Kotlin Multiplatform Mobile, you can build cross-platform mobile applications and share common code between Android and iOS.

Koin provides multiplatform dependency injection and helps build your components across your native mobile applications, and web/backend applications.

:::info
**Learn More**: [Kotlin Multiplatform with Koin](/docs/reference/koin-mp/kmp)
:::

### Performances and Productivity

Koin is a pure Kotlin framework, designed to be straight forward in terms of usage and execution. It's easy to use and doesn't impact your compilation time, nor require any extra plugin configuration.

---

## Koin: A Dependency Injection Framework

Koin is a popular dependency injection (DI) framework for Kotlin, offering a modern and lightweight solution for managing your application's dependencies with minimal boilerplate code.

### Dependency Injection vs. Service Locator

While Koin may appear similar to a service locator pattern, there are key differences that set it apart:

| Aspect | Service Locator | Dependency Injection (Koin) |
|--------|----------------|----------------------------|
| **Registry** | Static, global registry | Modular, scoped containers |
| **Access** | Explicit request for services | Dependencies passed automatically |
| **Testability** | Harder to mock/test | Easy to substitute dependencies |
| **Coupling** | Tighter coupling to framework | Looser coupling, explicit dependencies |
| **Best Practice** | Discouraged in modern apps | Industry-standard pattern |

:::note
**Service Locator**: A service locator is essentially a registry of available services where you can request an instance of a service as needed. It is responsible for creating and managing these instances, often using a static, global registry.

**Dependency Injection**: In contrast, Koin is a pure dependency injection framework. With Koin, you declare your dependencies in modules, and Koin handles the creation and wiring of objects. It allows for the creation of multiple, independent modules with their own scopes, making dependency management more modular and avoiding potential conflicts.
:::

### Koin's Approach: A Blend of Flexibility and Best Practices

Koin supports both DI and the Service Locator pattern, offering flexibility to developers. However, it **strongly encourages the use of DI**, particularly **constructor injection**, where dependencies are passed as constructor parameters. This approach promotes better testability and makes your code easier to reason about.

```kotlin
// ✅ Recommended: Constructor Injection
class UserViewModel(
    private val repository: UserRepository,
    private val analytics: Analytics
) : ViewModel() {
    // Dependencies are clear and testable
}

// ⚠️ Allowed but not recommended: Service Locator pattern
class UserViewModel : ViewModel(), KoinComponent {
    private val repository: UserRepository by inject()
    private val analytics: Analytics by inject()
    // Dependencies are hidden
}
```

Koin's design philosophy is centered around **simplicity and ease of setup** while allowing for complex configurations when necessary. By using Koin, developers can manage dependencies effectively, with DI being the recommended and preferred approach for most scenarios.

:::info
**Learn More**: See [Dependency Injection Basics](/docs/intro/what-is-dependency-injection) for a complete guide on DI concepts.
:::

---

## Transparency and Design Overview

Koin is designed to be a versatile Inversion of Control (IoC) container that supports both Dependency Injection (DI) and Service Locator (SL) patterns. To provide a clear understanding of how Koin operates and to guide you in using it effectively, let's explore the following aspects:

### How Koin Balances DI and SL

Koin combines elements of both DI and SL, which may influence how you use the framework:

1. **Global Context Usage:** By default, Koin provides a globally accessible component that acts like a service locator. This allows you to retrieve dependencies from a central registry using `KoinComponent` or `inject` functions.

2. **Isolated Components:** Although Koin encourages the use of Dependency Injection, particularly constructor injection, it also allows for isolated components. This flexibility means you can configure your application to use DI where it makes the most sense while still taking advantage of SL for specific cases.

3. **SL in Android Components:** In Android development, Koin often uses SL internally within components such as `Application` and `Activity` for ease of setup. From this point, Koin recommends DI, especially constructor injection, to manage dependencies in a more structured way. However, this is not enforced, and developers have the flexibility to use SL if needed.

### Why This Matters to You

Understanding the distinction between DI and SL helps in managing your application's dependencies effectively:

**Dependency Injection (Recommended):**
- ✅ Better testability
- ✅ Explicit dependencies
- ✅ Clearer code structure
- ✅ Industry best practice

**Service Locator:**
- ⚠️ Convenient for setup
- ⚠️ Can lead to tighter coupling
- ⚠️ Hidden dependencies
- ⚠️ Harder to test

:::warning
While Koin supports SL for convenience, especially in Android components, **relying solely on SL can lead to tighter coupling and reduced testability**. Koin's design provides a balanced approach, allowing you to use SL where it's practical but **promoting DI as the best practice**.
:::

---

## Making the Most of Koin

To use Koin effectively:

### 1. Follow Best Practices

Use **constructor injection** where possible to align with best practices for dependency management. This approach improves testability and maintainability.

```kotlin
// ✅ Good
class UserService(private val api: UserApi, private val db: UserDatabase)

module {
    singleOf(::UserService)
}

// ❌ Avoid
class UserService : KoinComponent {
    private val api: UserApi by inject()
    private val db: UserDatabase by inject()
}
```

### 2. Leverage Koin's Flexibility

Utilize Koin's support for SL in scenarios where it simplifies setup, but aim to rely on DI for managing core application dependencies.

### 3. Refer to Documentation and Examples

Review Koin's documentation and examples to understand how to configure and use DI and SL appropriately based on your project needs.

### 4. Use Scopes Wisely

Koin's scope feature allows you to isolate dependencies for specific parts of your application:

```kotlin
module {
    scope<MyActivity> {
        scoped { MyActivityDependency() }
    }
}
```

:::info
**Learn More**: See [Scopes](/docs/reference/koin-core/scopes) for detailed scope patterns.
:::

---

## Next Steps

Ready to get started? Choose your platform:

### Setup Guides
- [Koin Setup](/docs/setup/koin) - Gradle configuration for all platforms
- [Koin Annotations Setup](/docs/setup/annotations) - KSP setup for annotation-based DI

### Getting Started Tutorials
- [Android with ViewModel](/docs/quickstart/android-viewmodel) - Start building Android apps with Koin
- [Jetpack Compose](/docs/quickstart/android-compose) - Koin with Compose UI
- [Kotlin Multiplatform](/docs/reference/koin-mp/kmp) - Share code across platforms
- [Ktor Backend](/docs/quickstart/ktor) - Build server applications

### Core Concepts
- [Dependency Injection Basics](/docs/intro/what-is-dependency-injection) - Fundamental DI concepts
- [Core Features](/docs/reference/koin-core/dsl) - Koin DSL and module system
- [Android Integration](/docs/reference/koin-android/start) - Android-specific features

---

> By providing this guidance, we aim to help you navigate Koin's features and design choices effectively, ensuring you can leverage its full potential while adhering to best practices in dependency management.

---

### Reference Koin Android Advanced Patterns (`docs/reference/koin-android/advanced-patterns.md`)

---
title: Advanced Android Patterns
---

This guide covers Android-specific advanced dependency injection patterns with Koin.

:::info
For platform-agnostic patterns (collections, decorator, generic types, circular dependencies), see [Definitions](/docs/reference/koin-core/definitions) and [Modules](/docs/reference/koin-core/modules).
:::

## Android Context in Singletons

### Avoiding Activity Leaks

```kotlin
// ❌ Bad - Activity leaks through singleton
module {
    single { SomeService(get<Activity>()) }
}

// ✅ Good - Use Application context
module {
    single { SomeService(androidContext()) }
}

// ✅ Good - Use scoped for Activity-bound dependencies
module {
    activityScope {
        scoped { ActivityBoundService() }
    }
}
```

### Context Types

```kotlin
module {
    // Application context - safe for singletons
    single { DatabaseHelper(androidContext()) }

    // Application instance
    single { AppConfig(androidApplication()) }
}
```

## Conditional Bindings with BuildConfig

### Build Variant

```kotlin
fun createLogger(): Logger =
    if (BuildConfig.DEBUG) DebugLogger() else ReleaseLogger()

val loggingModule = module {
    single { create(::createLogger) }
}
```

Or with Annotations:

```kotlin
@Module
class LoggingModule {
    @Single
    fun provideLogger(): Logger =
        if (BuildConfig.DEBUG) DebugLogger() else ReleaseLogger()
}
```

### Analytics Toggle

```kotlin
fun createAnalyticsService(): AnalyticsService =
    if (BuildConfig.ENABLE_ANALYTICS) GoogleAnalytics() else NoOpAnalytics()

val analyticsModule = module {
    single { create(::createAnalyticsService) }
}
```

### Feature Flags

```kotlin
@Singleton
class PaymentProcessor(
    private val featureFlags: FeatureFlagService,
    private val newProcessor: NewPaymentProcessor,
    private val legacyProcessor: LegacyPaymentProcessor
) {
    fun process(amount: Double) {
        if (featureFlags.isEnabled("new_payment_flow")) {
            newProcessor.process(amount)
        } else {
            legacyProcessor.process(amount)
        }
    }
}
```

## Android Dialog Provider

Create factories for Android UI components:

```kotlin
@Factory
class DialogProvider(private val context: Context) {

    fun createConfirmDialog(title: String, onConfirm: () -> Unit): AlertDialog =
        AlertDialog.Builder(context)
            .setTitle(title)
            .setPositiveButton("OK") { _, _ -> onConfirm() }
            .create()

    fun createErrorDialog(message: String): AlertDialog =
        AlertDialog.Builder(context)
            .setTitle("Error")
            .setMessage(message)
            .setPositiveButton("OK", null)
            .create()
}

class MainActivity : AppCompatActivity() {
    private val dialogProvider: DialogProvider by inject()

    fun showConfirmation() {
        dialogProvider.createConfirmDialog("Confirm") { /* action */ }.show()
    }
}
```

## Hierarchical Scopes

Link Android scopes for shared access:

```kotlin
val appModule = module {
    single { Database() }

    scope(named("session")) {
        scoped { UserSession() }
    }

    scope(named("shopping")) {
        scoped { ShoppingCart(get()) }
    }
}

// Create and link scopes
val sessionScope = getKoin().createScope("user_session", named("session"))
val shoppingScope = getKoin().createScope("cart", named("shopping"))
shoppingScope.linkTo(sessionScope)

// Shopping cart can access UserSession from linked scope
val cart = shoppingScope.get<ShoppingCart>()
```

## Dynamic Feature Registry

Build collections based on configuration:

```kotlin
@Singleton
class FeatureRegistry(private val config: AppConfig) : KoinComponent {

    fun getEnabledFeatures(): List<Feature> {
        return config.enabledFeatures.mapNotNull { name ->
            getKoin().getOrNull<Feature>(named(name))
        }
    }
}
```

## Common Android Pitfalls

### Hidden Circular Calls

```kotlin
// ⚠️ Lazy hides the cycle but infinite loop at runtime
class ServiceA : KoinComponent {
    private val serviceB: ServiceB by inject()
    fun doA() { serviceB.doB() }
}

class ServiceB : KoinComponent {
    private val serviceA: ServiceA by inject()
    fun doB() { serviceA.doA() }  // Infinite loop!
}
```

### ViewModel Scope Confusion

```kotlin
// ❌ Bad - ViewModel in activity scope loses state on rotation
module {
    activityScope {
        scoped { UserViewModel(get()) }
    }
}

// ✅ Good - Use viewModel for proper lifecycle
module {
    viewModel { UserViewModel(get()) }
}
```

### Injecting Activity in Singleton

```kotlin
// ❌ Memory leak - Activity reference in singleton
@Singleton
class ImageLoader(private val activity: Activity)

// ✅ Use Application context
@Singleton
class ImageLoader(private val context: Context)  // Application context via androidContext()
```

## Next Steps

- **[Android Scopes](/docs/reference/koin-android/scope)** - Lifecycle-aware scoping
- **[Multi-Module Apps](/docs/reference/koin-android/multi-module)** - Organizing Android modules
- **[Best Practices](/docs/reference/koin-android/best-practices)** - Memory management and migration

---

### Reference Koin Android Best Practices (`docs/reference/koin-android/best-practices.md`)

---
title: Android Best Practices
---

This guide covers Android-specific best practices for memory management, security, and migration from Hilt.

:::info
For general module concepts, see **[Modules](/docs/reference/koin-core/modules)**. For scoping, see **[Scopes](/docs/reference/koin-core/scopes)** and **[Android Scopes](/docs/reference/koin-android/scope)**.
:::

## Memory Management

### Avoid Activity/Fragment Leaks

```kotlin
// ❌ Bad - Activity leak
module {
    single { SomeService(get<Activity>()) }  // Activity reference in singleton!
}

// ✅ Good - Use Application context
module {
    single { SomeService(androidContext()) }  // Application context, safe
}

// ✅ Good - Use activity scope
module {
    activityScope {
        scoped { SomeService(/* activity-scoped dependencies */) }
    }
}
```

### Close Scopes Properly

```kotlin
// ✅ Good - Automatic scope management
class MyActivity : ScopeActivity() {
    override val scope: Scope by activityScope()
    // Scope automatically closed in onDestroy
}

// ❌ Bad - Manual scope without cleanup
class MyActivity : AppCompatActivity() {
    private val myScope = createScope<MyActivity>()
    // Scope never closed - memory leak!
}

// ✅ Good - Manual scope with cleanup
class MyActivity : AppCompatActivity() {
    private lateinit var myScope: Scope

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        myScope = createScope<MyActivity>()
    }

    override fun onDestroy() {
        myScope.close()
        super.onDestroy()
    }
}
```

### Clear References in Long-Lived Objects

```kotlin
// ❌ Bad - Holding references to UI
class UserRepository {
    private val listeners = mutableListOf<UserUpdateListener>()  // Might hold Activity refs

    fun addListener(listener: UserUpdateListener) {
        listeners.add(listener)
    }
}

// ✅ Good - Weak references or manual cleanup
class UserRepository {
    private val listeners = mutableListOf<WeakReference<UserUpdateListener>>()

    fun addListener(listener: UserUpdateListener) {
        listeners.add(WeakReference(listener))
    }

    fun removeListener(listener: UserUpdateListener) {
        listeners.removeAll { it.get() == listener || it.get() == null }
    }
}
```

## Android Debugging

### Enable Android Logger

```kotlin
startKoin {
    androidLogger(Level.DEBUG)  // See all Koin operations
    androidContext(this@MyApplication)
    modules(appModules)
}
```

### Verify Modules in Debug Builds

```kotlin
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidContext(this@MyApplication)
            modules(allModules)
        }

        // Use the Koin Compiler Plugin for compile-time validation
        // or verify() in unit tests: appModule.verify()
    }
}
```

### Scope Callbacks for Debugging

```kotlin
class DebugActivity : ScopeActivity() {
    override val scope: Scope by activityScope()

    init {
        scope.registerCallback(object : ScopeCallback {
            override fun onScopeClose(scope: Scope) {
                Log.d("Koin", "Scope ${scope.id} closing")
            }
        })
    }
}
```

## Security Best Practices

### Don't Store Secrets in Modules

```kotlin
// ❌ Bad - Hardcoded secrets
module {
    single {
        Retrofit.Builder()
            .addInterceptor { chain ->
                chain.proceed(
                    chain.request().newBuilder()
                        .header("API-Key", "super-secret-key")  // NO!
                        .build()
                )
            }
            .build()
    }
}

// ✅ Good - Secrets from secure storage
module {
    single {
        val securePrefs = get<SecurePreferences>()
        Retrofit.Builder()
            .addInterceptor(AuthInterceptor(securePrefs))
            .build()
    }
}
```

## Migration from Dagger/Hilt

:::info
Koin supports JSR-330 annotations (`@Singleton`, `@Inject`, `@Named`) from `jakarta.inject`. You can keep using familiar annotations. See [JSR-330 Compatibility](/docs/reference/koin-android/jsr330).
:::

### Annotation Mapping

| Hilt | Koin Annotations |
|------|------------------|
| `@Singleton` | `@Singleton` (JSR-330 compatible) |
| `@Provides` | `@Factory` |
| `@Binds` | `@Singleton ... bind Interface::class` |
| `@Inject` | `@Inject` (JSR-330 compatible) |
| `@HiltViewModel` | `@KoinViewModel` |
| `@InstallIn(SingletonComponent)` | `@Module` + `@ComponentScan` |
| `@InstallIn(ActivityComponent)` | `@Scope(ActivityScope::class)` |

### Example Migration

```kotlin
// Before (Hilt)
@HiltViewModel
class HomeViewModel @Inject constructor(
    private val repository: UserRepository
) : ViewModel()

@Singleton
class UserRepositoryImpl @Inject constructor(
    private val api: ApiService
) : UserRepository

// After (Koin) - minimal changes!
@KoinViewModel
class HomeViewModel(
    private val repository: UserRepository
) : ViewModel()

@Singleton  // Keep using JSR-330
class UserRepositoryImpl(
    private val api: ApiService
) : UserRepository
```

### Module Migration

```kotlin
// Before (Hilt)
@InstallIn(SingletonComponent::class)
@Module
object NetworkModule {
    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit = Retrofit.Builder().build()
}

// After (Koin Annotations)
@Module
class NetworkModule {
    @Singleton
    fun provideRetrofit(): Retrofit = Retrofit.Builder().build()
}
```

### Gradual Migration

```kotlin
// Step 1: Add Koin alongside Hilt for new features
@KoinViewModel
class NewFeatureViewModel(
    private val repository: NewFeatureRepository
) : ViewModel()

@Singleton
class NewFeatureRepository(private val api: ApiService)

// Step 2: Migrate existing features one by one
@Singleton
class MigratedRepository(private val api: ApiService) : UserRepository

// Step 3: Remove Hilt when migration complete
```

## See Also

- **[Scopes](/docs/reference/koin-core/scopes)** - Core scoping concepts
- **[Android Scopes](/docs/reference/koin-android/scope)** - Android lifecycle scopes
- **[Testing](/docs/reference/koin-android/instrumented-testing)** - Android testing guide
- **[Multi-Module Apps](/docs/reference/koin-android/multi-module)** - Organizing Android modules

---

### Reference Koin Android Binding Patterns (`docs/reference/koin-android/binding-patterns.md`)

---
title: Android Library Integration
---

This guide covers integrating common Android libraries with Koin.

:::info
For core definition types and external library binding patterns, see [Definitions](/docs/reference/koin-core/definitions). This page focuses on Android-specific library examples.
:::

## Retrofit

```kotlin
interface ApiService {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: String): User
}

// Builder functions - Koin resolves parameters automatically
fun createOkHttpClient(): OkHttpClient =
    OkHttpClient.Builder()
        .connectTimeout(30, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .addInterceptor(HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        })
        .build()

fun createRetrofit(client: OkHttpClient): Retrofit =
    Retrofit.Builder()
        .baseUrl("https://api.example.com/")
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

fun createApiService(retrofit: Retrofit): ApiService =
    retrofit.create(ApiService::class.java)

val networkModule = module {
    single { create(::createOkHttpClient) }
    single { create(::createRetrofit) }
    single { create(::createApiService) }
}
```

Or with Annotations:

```kotlin
@Module
class NetworkModule {
    @Single
    fun provideOkHttpClient(): OkHttpClient =
        OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build()

    @Single
    fun provideRetrofit(client: OkHttpClient): Retrofit =
        Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build()

    @Single
    fun provideApiService(retrofit: Retrofit): ApiService =
        retrofit.create(ApiService::class.java)
}
```

## Room Database

```kotlin
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

fun createDatabase(context: Context): AppDatabase =
    Room.databaseBuilder(context, AppDatabase::class.java, "app-database").build()

fun createUserDao(database: AppDatabase): UserDao = database.userDao()

val databaseModule = module {
    single { create(::createDatabase) }
    single { create(::createUserDao) }
}
```

Or with Annotations:

```kotlin
@Module
class DatabaseModule {
    @Single
    fun provideDatabase(context: Context): AppDatabase =
        Room.databaseBuilder(context, AppDatabase::class.java, "app-database").build()

    @Single
    fun provideUserDao(database: AppDatabase): UserDao = database.userDao()
}
```

## Gson / Serialization

```kotlin
fun createGson(): Gson =
    GsonBuilder()
        .setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
        .setPrettyPrinting()
        .create()

val serializationModule = module {
    single { create(::createGson) }
}
```

## WorkManager

```kotlin
class SyncWorker(
    context: Context,
    params: WorkerParameters,
    private val repository: SyncRepository
) : Worker(context, params) {

    override fun doWork(): Result {
        repository.sync()
        return Result.success()
    }
}

val workModule = module {
    workerOf(::SyncWorker)
}
```

:::note
For WorkManager setup, use the `koin-androidx-workmanager` dependency. See [WorkManager Integration](/docs/reference/koin-android/workmanager) for complete setup.
:::

## Android Clean Architecture

### Repository Pattern

```kotlin
@Singleton
class UserRemoteDataSource(private val api: ApiService)

@Singleton
class UserLocalDataSource(private val database: AppDatabase)

@Singleton
class UserRepositoryImpl(
    private val remoteDataSource: UserRemoteDataSource,
    private val localDataSource: UserLocalDataSource
) : UserRepository

// DSL
val dataModule = module {
    single<UserRemoteDataSource>()
    single<UserLocalDataSource>()
    single<UserRepositoryImpl>() bind UserRepository::class
}
```

### Use Case Pattern

```kotlin
@Factory
class GetUserUseCase(private val userRepository: UserRepository) {
    suspend operator fun invoke(userId: String): Result<User> = runCatching {
        userRepository.getUser(userId)
    }
}

val domainModule = module {
    factory<GetUserUseCase>()
}
```

### ViewModel with SavedStateHandle

```kotlin
@KoinViewModel
class UserViewModel(
    private val getUserUseCase: GetUserUseCase,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    private val userId: String = savedStateHandle["userId"] ?: ""

    fun loadUser() {
        viewModelScope.launch {
            getUserUseCase(userId)
        }
    }
}

val viewModelModule = module {
    viewModel<UserViewModel>()
}
```

## Next Steps

- **[Definitions](/docs/reference/koin-core/definitions)** - Core definition types
- **[WorkManager](/docs/reference/koin-android/workmanager)** - Complete WorkManager setup
- **[Android Scopes](/docs/reference/koin-android/scope)** - Lifecycle-scoped instances
- **[ViewModel](/docs/reference/koin-android/viewmodel)** - ViewModel injection patterns

---

### Reference Koin Android Entry Points (`docs/reference/koin-android/entry-points.md`)

---
title: Android Entry Points
---

This guide covers how to inject dependencies into different Android components using Koin. Each Android component type has specific lifecycle characteristics that affect how you should use dependency injection.

:::info
This page focuses on **where** to inject (entry points). The injection APIs (`by inject()`, `get()`, `by viewModel()`) work the same regardless of how you declare definitions. For declaring definitions, see [Definitions](/docs/reference/koin-core/definitions).
:::

## Overview

Android applications consist of various component types, each with their own lifecycle and initialization patterns. Koin provides flexible ways to inject dependencies into all of them.

### Quick Reference

| Component | Injection Method | Built-in Support | Notes |
|-----------|-----------------|------------------|-------|
| **Application** | `startKoin {}` in `onCreate()` | ✅ Yes | Entry point for Koin setup |
| **Activity** | `by inject()` or `get()` | ✅ Yes | Direct injection support |
| **Fragment** | `by inject()` or `get()` | ✅ Yes | Direct injection support |
| **ViewModel** | `by viewModel()` | ✅ Yes | Lifecycle-aware injection |
| **Service** | `by inject()` or `get()` | ✅ Yes | Direct injection support |
| **BroadcastReceiver** | `KoinComponent` + `get()` | ⚠️ Manual | Implement `KoinComponent` |
| **ContentProvider** | `KoinComponent` + `get()` | ⚠️ Manual | Special timing considerations |
| **Custom View** | Constructor or `KoinComponent` | ⚠️ Manual | Consider avoiding DI |

## Application Class

The Application class is where you initialize Koin. This is the foundation for all dependency injection in your app.

```kotlin
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger()
            androidContext(this@MyApplication)
            modules(appModule, networkModule, dataModule)
        }
    }
}
```

:::info
For complete Application setup instructions, see [Starting Koin on Android](/docs/reference/koin-android/start).
:::

## Activity Injection

Activities have built-in Koin support through extension functions.

### Using by inject()

```kotlin
class UserActivity : AppCompatActivity() {
    // Lazy injection - created when first accessed
    private val presenter: UserPresenter by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)

        presenter.loadUser()  // Presenter created here
    }
}
```

### Using get()

```kotlin
class UserActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)

        // Eager injection - created immediately
        val presenter: UserPresenter = get()
        presenter.loadUser()
    }
}
```

### With Parameters

```kotlin
class UserDetailActivity : AppCompatActivity() {
    private val userId: String by lazy { intent.getStringExtra("USER_ID") ?: "" }

    // Pass runtime parameters
    private val presenter: UserDetailPresenter by inject { parametersOf(userId) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        presenter.loadUserDetails()
    }
}
```

See [Injected Parameters](/docs/reference/koin-core/definitions#injected-parameters) for declaring definitions with parameters.

:::info
For more Activity injection patterns, see [Injecting in Android](/docs/reference/koin-android/get-instances).
:::

## Fragment Injection

Fragments work identically to Activities with Koin extensions.

```kotlin
class UserListFragment : Fragment() {
    private val viewModel: UserListViewModel by viewModel()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return inflater.inflate(R.layout.fragment_user_list, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel.loadUsers()
    }
}
```

### Shared ViewModels

Share a ViewModel between Activity and Fragments:

```kotlin
class UserDetailFragment : Fragment() {
    // Get ViewModel scoped to Activity
    private val sharedViewModel: UserViewModel by activityViewModel()

    // Get ViewModel scoped to this Fragment
    private val fragmentViewModel: DetailViewModel by viewModel()
}
```

:::info
For Fragment and ViewModel injection details, see:
- [Injecting in Android](/docs/reference/koin-android/get-instances)
- [Android ViewModels](/docs/reference/koin-android/viewmodel)
:::

## Service Injection

Services have built-in Koin support through extension functions, just like Activities and Fragments.

### Using by inject()

```kotlin
class MusicPlayerService : Service() {
    // Lazy injection - created when first accessed
    private val player: MediaPlayer by inject()
    private val repository: MusicRepository by inject()

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val songId = intent?.getStringExtra("SONG_ID")
        if (songId != null) {
            val song = repository.getSong(songId)
            player.play(song)
        }
        return START_NOT_STICKY
    }

    override fun onDestroy() {
        player.release()
        super.onDestroy()
    }
}
```

### Using get()

```kotlin
class DownloadService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // Eager injection - created immediately
        val downloader: FileDownloader = get()
        val url = intent?.getStringExtra("URL") ?: ""

        downloader.start(url)
        return START_STICKY
    }
}
```

### Lifecycle Considerations

- **Services are long-lived**: Use `single` for expensive resources
- **Cleanup is critical**: Release resources in `onDestroy()`
- **Background threads**: Consider scoping background work properly

### Best Practices

```kotlin
class DownloadService : Service() {
    // Use lazy injection to delay initialization
    private val downloader: FileDownloader by inject()
    private val notificationManager: NotificationManager by inject()

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // Dependencies created only when service actually starts
        downloader.start(intent?.getStringExtra("URL") ?: "")
        return START_STICKY
    }

    override fun onDestroy() {
        // Always cleanup
        downloader.cancel()
        super.onDestroy()
    }
}
```

:::note
**Alternative:** For `WorkManager` background tasks, use Koin's built-in `WorkManager` support instead of Services. See [WorkManager Integration](/docs/reference/koin-android/workmanager).
:::

## BroadcastReceiver Injection

BroadcastReceivers also need `KoinComponent` for dependency injection.

### Dynamically Registered Receiver

```kotlin
class NetworkChangeReceiver : BroadcastReceiver(), KoinComponent {
    // Use get() for eager injection (receivers are short-lived)
    private val networkMonitor: NetworkMonitor by inject()

    override fun onReceive(context: Context?, intent: Intent?) {
        when (intent?.action) {
            ConnectivityManager.CONNECTIVITY_ACTION -> {
                networkMonitor.checkConnectivity()
            }
        }
    }
}

// Register in Activity or Service
class MainActivity : AppCompatActivity() {
    private val receiver = NetworkChangeReceiver()

    override fun onResume() {
        super.onResume()
        val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        registerReceiver(receiver, filter)
    }

    override fun onPause() {
        unregisterReceiver(receiver)
        super.onPause()
    }
}
```

### Statically Registered Receiver (Manifest)

```kotlin
class BootReceiver : BroadcastReceiver(), KoinComponent {
    override fun onReceive(context: Context?, intent: Intent?) {
        if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
            // Ensure Koin is initialized
            val app = context?.applicationContext as? MyApplication

            // Now safe to inject
            val scheduler: JobScheduler by inject()
            scheduler.scheduleWork()
        }
    }
}
```

```xml

<receiver android:name=".BootReceiver"
    android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
```

### Important Considerations

**Lifecycle:**
- Receivers are **extremely short-lived** (typically < 10 seconds)
- Use `get()` instead of `by inject()` for faster initialization
- Avoid heavy operations in `onReceive()`

**Koin Initialization:**
- For manifest-declared receivers, ensure Koin is initialized in `Application.onCreate()`
- For dynamically registered receivers, Koin is already initialized

**Best Practices:**

```kotlin
class AlarmReceiver : BroadcastReceiver(), KoinComponent {
    override fun onReceive(context: Context?, intent: Intent?) {
        // Use get() for immediate access (more efficient for receivers)
        val repository: AlarmRepository = get()

        // Offload work to a Service or WorkManager
        val workRequest = OneTimeWorkRequestBuilder<AlarmWorker>()
            .setInputData(workDataOf("alarm_id" to intent?.getIntExtra("ID", -1)))
            .build()

        WorkManager.getInstance(context!!).enqueue(workRequest)
    }
}
```

:::warning
BroadcastReceivers have strict time limits (~10 seconds). For any significant work, use `Service`, `WorkManager`, or `JobScheduler` instead.
:::

## ContentProvider Injection

ContentProviders have special timing considerations because they're created **before** `Application.onCreate()`.

### The Challenge

```kotlin
// ❌ PROBLEM: This won't work!
class MyContentProvider : ContentProvider(), KoinComponent {
    // Koin not initialized yet when ContentProvider is created!
    private val database: Database by inject()  // Will crash

    override fun onCreate(): Boolean {
        // This runs BEFORE Application.onCreate()
        return true
    }
}
```

### Solution 1: Lazy Initialization

```kotlin
class UserContentProvider : ContentProvider(), KoinComponent {
    // Use lazy to delay initialization
    private val database: Database by lazy {
        // Koin is ready by the time first query happens
        get<Database>()
    }

    override fun onCreate(): Boolean {
        // Don't access injected dependencies here!
        return true
    }

    override fun query(
        uri: Uri,
        projection: Array<out String>?,
        selection: String?,
        selectionArgs: Array<out String>?,
        sortOrder: String?
    ): Cursor? {
        // Safe to use database here (after Application.onCreate)
        return database.query(uri)
    }
}
```

### Solution 2: Manual Koin Initialization

```kotlin
class UserContentProvider : ContentProvider(), KoinComponent {
    private lateinit var database: Database

    override fun onCreate(): Boolean {
        // Initialize Koin if not already initialized
        val context = context ?: return false

        if (GlobalContext.getOrNull() == null) {
            startKoin {
                androidContext(context.applicationContext)
                modules(databaseModule)
            }
        }

        // Now safe to inject
        database = get()
        return true
    }

    override fun query(/* ... */): Cursor? {
        return database.query(uri)
    }
}
```

### Best Practices

```kotlin
class DataContentProvider : ContentProvider(), KoinComponent {
    // Lazy initialization pattern
    private val repository: DataRepository by lazy { get() }

    override fun onCreate(): Boolean {
        // Minimal initialization
        // Don't access Koin here
        return true
    }

    override fun query(
        uri: Uri,
        projection: Array<out String>?,
        selection: String?,
        selectionArgs: Array<out String>?,
        sortOrder: String?
    ): Cursor? {
        // Koin definitely ready by now
        return repository.getData(uri)
    }

    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        return repository.insert(uri, values)
    }
}
```

:::warning
**Critical:** ContentProviders are created **before** `Application.onCreate()`. Always use lazy initialization or check if Koin is initialized before injecting dependencies.
:::

## Custom View Injection

Custom Views can use dependency injection, but it should be approached carefully.

### Option 1: Constructor Injection (Recommended for Business Logic)

```kotlin
// Domain/ViewModel layer - uses constructor injection
class ChartViewModel(
    private val dataRepository: ChartDataRepository
) : ViewModel() {
    fun loadChartData(): LiveData<ChartData> {
        return dataRepository.getChartData()
    }
}

// View layer - receives data, no DI needed
class ChartView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    // No dependencies injected - just draws what it's given
    fun setData(data: ChartData) {
        // Draw chart
        invalidate()
    }
}

// Activity wires them together
class ChartActivity : AppCompatActivity() {
    private val viewModel: ChartViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val chartView = findViewById<ChartView>(R.id.chart)

        viewModel.loadChartData().observe(this) { data ->
            chartView.setData(data)
        }
    }
}
```

### Option 2: KoinComponent (When View Has Complex Logic)

```kotlin
class SmartChartView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr), KoinComponent {

    // Only inject if view has significant logic
    private val chartRenderer: ChartRenderer by inject()
    private val colorScheme: ColorScheme by inject()

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        chartRenderer.render(canvas, colorScheme)
    }
}
```

### When to Avoid DI in Views

❌ **Avoid injecting in Views when:**
- View is purely presentational (just draws data)
- View is used in layout preview (DI not available in preview)
- View is inflated from XML (can't pass constructor params)
- Dependencies can be passed as method parameters

✅ **Consider injecting in Views when:**
- View has complex rendering logic that's shared across app
- View needs configuration that changes per build variant
- View manages significant state or business logic (though consider moving to ViewModel)

### Best Practice: Keep Views Simple

```kotlin
// ❌ Too much logic in View
class UserCardView(context: Context) : FrameLayout(context), KoinComponent {
    private val userRepository: UserRepository by inject()
    private val imageLoader: ImageLoader by inject()

    fun loadUser(userId: String) {
        val user = userRepository.getUser(userId)  // Business logic in View!
        imageLoader.load(user.imageUrl, imageView)
    }
}

// ✅ Better: Move logic to ViewModel
class UserViewModel(
    private val userRepository: UserRepository,
    private val imageLoader: ImageLoader
) : ViewModel() {
    fun loadUser(userId: String): UserUiState {
        val user = userRepository.getUser(userId)
        return UserUiState(user, imageLoader.load(user.imageUrl))
    }
}

class UserCardView(context: Context) : FrameLayout(context) {
    // Just displays data - no DI needed
    fun bind(state: UserUiState) {
        textView.text = state.name
        imageView.setImageBitmap(state.image)
    }
}
```

:::info
**Recommendation:** Prefer keeping Views as "dumb" presentational components. Move business logic to ViewModels or Presenters where constructor injection is cleaner and more testable.
:::

## Summary

### Choosing the Right Injection Approach

| Component | Recommended Approach | Rationale |
|-----------|---------------------|-----------|
| **Application** | `startKoin {}` | Entry point - initialize Koin here |
| **Activity/Fragment** | `by inject()` or `by viewModel()` | Built-in support, clean syntax |
| **ViewModel** | Constructor injection via `viewModel {}` | Best testability |
| **Service** | `by inject()` or `get()` | Built-in support, long-lived |
| **BroadcastReceiver** | `KoinComponent` + `get()` | No built-in support, short-lived |
| **ContentProvider** | `KoinComponent` + `lazy { get() }` | Timing issues, use lazy init |
| **Custom View** | Avoid DI, pass data via methods | Keep Views simple, move logic to ViewModel |

### General Best Practices

1. **Prefer constructor injection** for business logic classes (Repositories, UseCases, ViewModels)
2. **Use field injection** (`by inject()`) for Android framework classes (Activities, Fragments, Services)
3. **Implement KoinComponent** only when necessary (BroadcastReceivers, ContentProviders, Custom Views)
4. **Use `get()` over `by inject()`** for short-lived components (BroadcastReceivers)
5. **Keep Views simple** - avoid injecting into Views when possible
6. **Watch lifecycle timing** - ContentProviders need special handling

## Next Steps

- **[Definitions](/docs/reference/koin-core/definitions)** - Declaring dependencies
- **[Injecting in Android](/docs/reference/koin-android/get-instances)** - Detailed Activity/Fragment injection
- **[Android ViewModels](/docs/reference/koin-android/viewmodel)** - ViewModel-specific patterns
- **[WorkManager Integration](/docs/reference/koin-android/workmanager)** - Background work with Koin
- **[Android Scopes](/docs/reference/koin-android/scope)** - Scope dependencies to component lifecycles

---

### Reference Koin Android Fragment Factory (`docs/reference/koin-android/fragment-factory.md`)

---
title: Fragment Factory
---

Koin integrates with [AndroidX FragmentFactory](https://developer.android.com/reference/kotlin/androidx/fragment/app/FragmentFactory) to enable constructor injection in Fragments.

:::info
Fragment Factory uses DSL only. Annotation and Compiler Plugin DSL support is not yet available.
:::

## Setup

### Add Dependency

```groovy
implementation "io.insert-koin:koin-android:$koin_version"
```

### Configure Fragment Factory

In your Koin configuration, enable the fragment factory:

```kotlin
startKoin {
    androidContext(this@MainApplication)
    fragmentFactory()
    modules(appModule)
}
```

## Declaring Fragments

Use the `fragment` DSL keyword with constructor injection:

```kotlin
class MyFragment(
    private val myService: MyService
) : Fragment()

val appModule = module {
    single { MyService() }
    fragment { MyFragment(get()) }
}
```

## Using Fragments

### Setup in Activity

Call `setupKoinFragmentFactory()` **before** `super.onCreate()`:

```kotlin
class MyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Must be called BEFORE super.onCreate()
        setupKoinFragmentFactory()

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}
```

### Add Fragment

Use the reified extension function:

```kotlin
supportFragmentManager.beginTransaction()
    .replace<MyFragment>(R.id.container)
    .commit()
```

With arguments and tag:

```kotlin
supportFragmentManager.beginTransaction()
    .replace<MyFragment>(
        containerViewId = R.id.container,
        args = bundleOf("key" to "value"),
        tag = "my_fragment"
    )
    .commit()
```

## Fragment Factory with Scopes

To use Activity-scoped dependencies in your Fragment:

```kotlin
val appModule = module {
    scope<MyActivity> {
        scoped { ActivityService() }
        fragment { MyFragment(get()) }
    }
}
```

Pass the scope to `setupKoinFragmentFactory()`:

```kotlin
class MyActivity : AppCompatActivity(), AndroidScopeComponent {

    override val scope: Scope by activityScope()

    override fun onCreate(savedInstanceState: Bundle?) {
        // Pass scope to fragment factory
        setupKoinFragmentFactory(scope)

        super.onCreate(savedInstanceState)
    }
}
```

## Quick Reference

| Action | Code |
|--------|------|
| Declare fragment | `fragment { MyFragment(get()) }` |
| Setup global factory | `setupKoinFragmentFactory()` |
| Setup with scope | `setupKoinFragmentFactory(scope)` |
| Add fragment | `.replace<MyFragment>(R.id.container)` |

## Next Steps

- **[AndroidX Fragment](https://developer.android.com/guide/fragments)** - Official Fragment documentation
- **[Scopes](/docs/reference/koin-android/scope)** - Android scopes
- **[ViewModel](/docs/reference/koin-android/viewmodel)** - ViewModel injection

---

### Reference Koin Android Get Instances (`docs/reference/koin-android/get-instances.md`)

---
title: Injecting in Android
---

Once you have declared some modules and started Koin, how can you retrieve your instances in your Android Activity, Fragments, or Services?

## Ready for Android Classes

`Activity`, `Fragment` & `Service` are extended with Koin extensions. Any `ComponentCallbacks` class has access to:

* `by inject()` - lazy evaluated instance from Koin container
* `get()` - eager fetch instance from Koin container
* `by viewModel()` - lazy ViewModel instance
* `getViewModel()` - eager ViewModel instance

## Defining Dependencies

### Compiler Plugin DSL

```kotlin
val appModule = module {
    factory<Presenter>()
    viewModel<UserViewModel>()
}
```

### Annotations

```kotlin
@Factory
class Presenter(private val repository: UserRepository)

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

### Classic DSL

```kotlin
val appModule = module {
    factory { Presenter(get()) }
    viewModel { UserViewModel(get()) }
}
```

## Injecting in Activity

```kotlin
class DetailActivity : AppCompatActivity() {

    // Lazy inject Presenter
    private val presenter: Presenter by inject()

    // Lazy inject ViewModel
    private val viewModel: UserViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Use presenter and viewModel
    }
}
```

## Injecting in Fragment

```kotlin
class UserFragment : Fragment() {

    // Fragment's own ViewModel
    private val viewModel: UserViewModel by viewModel()

    // Shared ViewModel with Activity
    private val sharedViewModel: SharedViewModel by activityViewModel()

    // Regular dependency
    private val presenter: Presenter by inject()
}
```

## Injecting in Service

```kotlin
class MyService : Service() {

    private val repository: UserRepository by inject()

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        repository.doSomething()
        return START_STICKY
    }
}
```

## Eager vs Lazy Injection

```kotlin
class DetailActivity : AppCompatActivity() {

    // Lazy - created on first access
    private val presenter: Presenter by inject()

    // Eager - created immediately
    private val service: MyService = get()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Or get eagerly in a function
        val anotherPresenter: Presenter = get()
    }
}
```

| Method | When Created | Use Case |
|--------|--------------|----------|
| `by inject()` | On first access | Most cases, avoids unnecessary creation |
| `get()` | Immediately | When you need the instance right away |

:::info
If your class doesn't have Koin extensions, implement the `KoinComponent` interface to access `inject()` or `get()`.
:::

## Injection with Parameters

Pass parameters at injection time:

```kotlin
@Factory
class UserPresenter(
    @InjectedParam val userId: String,
    val repository: UserRepository
)
```

```kotlin
class UserActivity : AppCompatActivity() {

    private val presenter: UserPresenter by inject { parametersOf("user_123") }
}
```

## Injection with Qualifiers

When you have multiple definitions of the same type:

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

```kotlin
class MyActivity : AppCompatActivity() {

    private val localDb: Database by inject(named("local"))
    private val remoteDb: Database by inject(named("remote"))
}
```

## Using Android Context in Definitions

Once your `Application` class configures Koin with `androidContext`, you can resolve it in your definitions.

### Annotations

With annotations, simply declare a `Context` or `Application` parameter - it will be automatically injected:

```kotlin
@Factory
class MyPresenter(private val context: Context)

@Singleton
class MyRepository(private val application: Application)
```

### DSL

Use `androidContext()` or `androidApplication()` functions in your module:

```kotlin
val appModule = module {
    factory {
        MyPresenter(androidContext())
    }
    single {
        MyRepository(androidApplication())
    }
}
```

## Android Scope & Context Resolution

When you have a scope binding the `Context` type, you may need to resolve `Context` from different levels:

```kotlin
class MyPresenter(val context: Context)

val appModule = module {
    scope<MyActivity> {
        scoped { MyPresenter(get()) }
    }
}
```

Context resolution:
- `get()` - resolves closest `Context`, here `MyActivity`
- `androidContext()` - resolves closest `Context`, here `MyActivity`
- `androidApplication()` - resolves `Application` from Koin setup

---

### Reference Koin Android Hilt Migration (`docs/reference/koin-android/hilt-migration.md`)

---
title: Migrating from Hilt to Koin
---

This guide helps you migrate your Android application from Dagger Hilt to Koin. Whether you're using Koin DSL or Koin Annotations, this guide covers the key differences and migration steps.

:::info
For a complete real-world example, check out the [Now in Android migration](https://blog.insert-koin.io/migrating-now-in-android-to-koin-annotations-2-3-67d252dbb97d) which shows how Google's production-ready news app with 30 Gradle modules was migrated from Hilt to Koin Annotations.
:::

## Why Migrate to Koin?

**Key advantages of Koin:**

- **No Code Generation** - Koin uses runtime dependency resolution without annotation processors
- **Simpler Setup** - No complex component hierarchies or `@InstallIn` declarations
- **Kotlin-First** - Idiomatic Kotlin DSL that feels natural
- **Lighter Weight** - Faster build times without kapt/KSP code generation (for DSL approach)
- **Multi-Module Friendly** - No `@EntryPoint` interfaces needed
- **JSR-330 Support** - Existing `@Inject` constructors work without modification

## Quick Reference: Hilt vs Koin

### Annotation Mappings

| Hilt | Koin DSL                                 | Koin Annotations                                                                                    |
|------|------------------------------------------|-----------------------------------------------------------------------------------------------------|
| `@HiltAndroidApp` | `startKoin {}` in Application            | `@KoinApplication`                                                                                  |
| `@AndroidEntryPoint` | `by inject()` / `by viewModel()`         | `by inject()` / `by viewModel()`                                                                    |
| `@HiltViewModel` | `viewModel { MyViewModel(...) }`         | `@KoinViewModel`                                                                                    |
| `@Inject` constructor | DSL to specify constructor parameters    | Constructor parameters detected (JSR-330)                                                           |
| `@Module` + `@InstallIn` | `module { }`                             | `@Module` + `@ComponentScan`                                                                        |
| `@Provides` | `single { }` or `factory { }`            | `@Single` / `@Factory`                                                                              |
| `@Binds` | `single<Interface> { Implementation() }` | `@Single` or `@Singleton` are detecting bindings. Also use `binds` property from those annotations. |
| `@Singleton` | `single { }`                             | `@Single`or `@Singleton`                                                                                           |
| `@Named("qualifier")` | `named("qualifier")`                     | `@Named("qualifier")`                                                                               |
| `@ApplicationContext` | Automatic context injection              | Automatic context injection                                                                         |
| `@EntryPoint` | Not needed                               | Not needed                                                                                          |

### Scope Mappings

| Hilt Scope | Koin DSL | Koin Annotations | Notes |
|------------|----------|------------------|-------|
| `@Singleton` | `single { }` | `@Single` / `@Singleton` | Application-wide singleton |
| `@ActivityScoped` | `activityScope { scoped { } }` | `@ActivityScope` | Tied to Activity lifecycle |
| `@ViewModelScoped` | `viewModelScope { scoped { } }` | `@ViewModelScope` | Tied to ViewModel lifecycle |
| `@ActivityRetainedScoped` | `activityRetainedScope { scoped { } }` | `@ActivityRetainedScope` | Survives configuration changes |

## Migration Steps

### Step 1: Update Dependencies

**Remove Hilt dependencies:**

```kotlin
// Remove these from build.gradle.kts
plugins {
    id("com.google.dagger.hilt.android") // Remove
}

dependencies {
    // Remove Hilt dependencies
    implementation("com.google.dagger:hilt-android:...")
    kapt("com.google.dagger:hilt-compiler:...")
}
```

**Add Koin dependencies:**

```kotlin
// build.gradle.kts (app module)
dependencies {
    // Koin for Android
    implementation("io.insert-koin:koin-android:$koin_version")
    implementation("io.insert-koin:koin-androidx-compose:$koin_version")

    // Optional: Koin Annotations
    implementation("io.insert-koin:koin-annotations:$koin_ksp_version")
    ksp("io.insert-koin:koin-ksp-compiler:$koin_ksp_version")
}
```

### Step 2: Application Setup

**Hilt:**

```kotlin
@HiltAndroidApp
class MyApplication : Application()
```

**Koin DSL:**

```kotlin
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger()
            androidContext(this@MyApplication)
            modules(appModule, dataModule, domainModule)
        }
    }
}
```

**Koin Annotations:**

```kotlin
@KoinApplication
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger()
            androidContext(this@MyApplication)
        }
    }
}
```

:::info
With `@KoinApplication`, modules are automatically discovered if they're tagged with `@Configuration`. You can also explicitly include modules using the `modules` property: `@KoinApplication(modules = [AppModule::class])`.
:::

### Step 3: Migrate Modules

**Hilt:**

```kotlin
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build()
    }

    @Provides
    @Singleton
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .client(okHttpClient)
            .build()
    }

    @Provides
    @Singleton
    fun provideApiService(retrofit: Retrofit): ApiService {
        return retrofit.create(ApiService::class.java)
    }
}
```

**Koin DSL:**

```kotlin
val networkModule = module {

    single {
        OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build()
    }

    single {
        Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .client(get()) // Automatic dependency resolution
            .build()
    }

    single {
        get<Retrofit>().create(ApiService::class.java)
    }
}
```

**Koin Annotations:**

```kotlin
@Module
class NetworkModule {

    @Single
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build()
    }

    @Single
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .client(okHttpClient)
            .build()
    }

    @Single
    fun provideApiService(retrofit: Retrofit): ApiService {
        return retrofit.create(ApiService::class.java)
    }
}
```

### Step 4: Migrate ViewModels

**Hilt:**

```kotlin
@HiltViewModel
class MyViewModel @Inject constructor(
    private val repository: MyRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    // ...
}

@Composable
fun MyScreen() {
    val viewModel = hiltViewModel<MyViewModel>()
    // ...
}
```

**Koin DSL:**

```kotlin
class MyViewModel(
    private val repository: MyRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    // ...
}

val appModule = module {
    viewModelOf(::MyViewModel)
}

@Composable
fun MyScreen() {
    val viewModel = koinViewModel<MyViewModel>()
    // ...
}
```

**Koin Annotations:**

```kotlin
@KoinViewModel
class MyViewModel(
    private val repository: MyRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    // ...
}

@Composable
fun MyScreen() {
    val viewModel = koinViewModel<MyViewModel>()
    // ...
}
```

:::info
The `viewModelOf` DSL function uses constructor parameter autowiring. `SavedStateHandle` is automatically provided by Koin, so you don't need to explicitly pass it. This is part of Koin's autowire DSL which simplifies ViewModel definitions.
:::

### Step 5: Migrate Activities and Fragments

**Hilt:**

```kotlin
@AndroidEntryPoint
class MainActivity : ComponentActivity() {

    @Inject
    lateinit var analytics: AnalyticsService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        analytics.logEvent("screen_view")
    }
}
```

**Koin:**

```kotlin
class MainActivity : ComponentActivity() {

    // Property delegation - no annotation needed
    private val analytics: AnalyticsService by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        analytics.logEvent("screen_view")
    }
}
```

:::info
With Koin, you don't need `@AndroidEntryPoint` - just use `by inject()` or `by viewModel()` property delegation.
:::

### Step 6: Migrate Interface Bindings

**Hilt:**

```kotlin
@Module
@InstallIn(SingletonComponent::class)
abstract class DataModule {

    @Binds
    @Singleton
    abstract fun bindRepository(
        impl: MyRepositoryImpl
    ): MyRepository
}

class MyRepositoryImpl @Inject constructor(
    private val apiService: ApiService
) : MyRepository {
    // ...
}
```

**Koin DSL:**

```kotlin
val dataModule = module {
    single<MyRepository> { MyRepositoryImpl(get()) }
}

class MyRepositoryImpl(
    private val apiService: ApiService
) : MyRepository {
    // ...
}
```

**Koin Annotations (Automatic Binding Detection):**

```kotlin
// Option 1: Automatic - Koin detects the interface binding
@Singleton
class MyRepositoryImpl(
    private val apiService: ApiService
) : MyRepository {
    // ...
}
// Koin automatically binds MyRepositoryImpl to MyRepository

// Option 2: Explicit with binds property
@Single(binds = [MyRepository::class])
class MyRepositoryImpl(
    private val apiService: ApiService
) : MyRepository {
    // ...
}
```

:::info
Koin Annotations automatically detects interface bindings when a class implements an interface. Use the `binds` property when you need to explicitly specify multiple interfaces or control binding behavior.
:::

### Step 7: Migrate Qualifiers

**Hilt:**

```kotlin
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class IoDispatcher

@Module
@InstallIn(SingletonComponent::class)
object DispatcherModule {

    @Provides
    @IoDispatcher
    fun provideIoDispatcher(): CoroutineDispatcher {
        return Dispatchers.IO
    }
}

class MyRepository @Inject constructor(
    @IoDispatcher private val dispatcher: CoroutineDispatcher
)
```

**Koin DSL (String-based):**

```kotlin
val dispatcherModule = module {
    single(named("io")) { Dispatchers.IO }
}

class MyRepository(
    private val dispatcher: CoroutineDispatcher
)

val dataModule = module {
    single { MyRepository(get(named("io"))) }
}
```

**Koin DSL (Type-safe):**

```kotlin
// Define a qualifier type
object IoDispatcher

val dispatcherModule = module {
    single(named<IoDispatcher>()) { Dispatchers.IO }
}

class MyRepository(
    private val dispatcher: CoroutineDispatcher
)

val dataModule = module {
    single { MyRepository(get(named<IoDispatcher>())) }
}
```

**Koin Annotations (String-based):**

```kotlin
@Module
class DispatcherModule {
    @Single
    @Named("io")
    fun provideIoDispatcher(): CoroutineDispatcher = Dispatchers.IO
}

@Single
class MyRepository(
    @InjectedParam @Named("io") private val dispatcher: CoroutineDispatcher
)
```

**Koin Annotations (With JSR-330 @Qualifier - Fully Compatible!):**

```kotlin
// Keep your existing JSR-330 qualifier annotation!
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class IoDispatcher

@Module
class DispatcherModule {
    @Single
    @IoDispatcher
    fun provideIoDispatcher(): CoroutineDispatcher = Dispatchers.IO
}

@Single
class MyRepository @Inject constructor(
    @IoDispatcher private val dispatcher: CoroutineDispatcher
)
```

:::info
Koin Annotations fully supports JSR-330 `@Qualifier` annotations! This is a standard Java/Kotlin DI annotation (not Hilt-specific), so you can keep your existing qualifier annotations unchanged during migration. The DSL also supports type-safe qualifiers using `named<T>()` instead of string-based `named("string")`.
:::

### Step 8: Migrate Compose Integration

**Hilt:**

```kotlin
@Composable
fun MyScreen(
    viewModel: MyViewModel = hiltViewModel()
) {
    val dependency: SomeDependency = EntryPointAccessors
        .fromActivity<MyEntryPoint>(LocalContext.current as Activity)
        .dependency()
}
```

**Koin:**

```kotlin
@Composable
fun MyScreen(
    viewModel: MyViewModel = koinViewModel()
) {
    // Direct injection - no EntryPoint needed
    val dependency: SomeDependency = koinInject()
}
```

### Step 9: Migrate Testing

**Hilt:**

```kotlin
@HiltAndroidTest
class MyTest {

    @get:Rule
    var hiltRule = HiltAndroidRule(this)

    @Inject
    lateinit var repository: MyRepository

    @Before
    fun init() {
        hiltRule.inject()
    }

    @Test
    fun myTest() {
        // ...
    }
}
```

**Koin:**

```kotlin
class MyTest : KoinTest {

    private val repository: MyRepository by inject()

    @Before
    fun before() {
        startKoin {
            modules(testModule)
        }
    }

    @After
    fun after() {
        stopKoin()
    }

    @Test
    fun myTest() {
        // ...
    }
}
```

## Multi-Module Projects

### Hilt Approach

With Hilt, you need:
- `@InstallIn` to specify component hierarchy
- `@EntryPoint` interfaces for cross-module access
- Complex component dependencies

### Koin Approach

With Koin:
- Each module declares its own Koin module
- Import all modules in your Application class
- No special interfaces needed

**Feature module with Koin:**

```kotlin
// :feature:home module
val homeModule = module {
    viewModel { HomeViewModel(get()) }
    factory { HomeRepository(get()) }
}

// :app module
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger()
            androidContext(this@MyApplication)
            modules(
                coreModule,
                dataModule,
                homeModule,  // Feature module
                profileModule // Another feature module
            )
        }
    }
}
```

See [Multi-Module Architecture](/docs/reference/koin-android/multi-module) for more details.

## Common Patterns

### Constructor Injection (JSR-330)

One of the biggest advantages: **existing `@Inject` constructors work with Koin Annotations!**

```kotlin
// This works with both Hilt and Koin Annotations
class MyRepository @Inject constructor(
    private val apiService: ApiService,
    private val database: AppDatabase
) {
    // ...
}
```

With Koin Annotations, you can keep your `@Inject` constructors unchanged and just add `@Single`, `@Singleton`, or `@Factory` to the class:

```kotlin
@Single // or @Singleton
class MyRepository @Inject constructor(
    private val apiService: ApiService,
    private val database: AppDatabase
) {
    // ...
}
```

### AssistedInject

**Hilt:**

```kotlin
class MyViewModel @AssistedInject constructor(
    private val repository: MyRepository,
    @Assisted private val userId: String
) : ViewModel() {

    @AssistedFactory
    interface Factory {
        fun create(userId: String): MyViewModel
    }
}
```

**Koin:**

```kotlin
class MyViewModel(
    private val repository: MyRepository,
    private val userId: String
) : ViewModel()

val appModule = module {
    viewModelOf(::MyViewModel)
}

// Usage
val viewModel: MyViewModel by viewModel { parametersOf("user123") }
```

### Lazy Injection

**Hilt:**

```kotlin
@Inject
lateinit var heavyService: HeavyService
```

**Koin:**

```kotlin
// Lazy by default with property delegation
private val heavyService: HeavyService by inject()

// Or explicit lazy
private val heavyService: Lazy<HeavyService> by lazy { get() }
```

## Migration Checklist

Use this checklist to track your migration progress:

- [ ] **Dependencies**
  - [ ] Remove Hilt Gradle plugin
  - [ ] Remove Hilt dependencies
  - [ ] Add Koin dependencies
  - [ ] Remove `kapt` if not needed elsewhere

- [ ] **Application Class**
  - [ ] Remove `@HiltAndroidApp`
  - [ ] Add `startKoin {}` in `onCreate()`
  - [ ] Configure `androidContext()` and modules

- [ ] **Modules**
  - [ ] Convert `@Module` + `@InstallIn` to `module { }`
  - [ ] Convert `@Provides` to `single { }` or `factory { }`
  - [ ] Convert `@Binds` to interface bindings
  - [ ] Update qualifiers to `named()`

- [ ] **ViewModels**
  - [ ] Remove `@HiltViewModel`
  - [ ] Add to module with `viewModel { }`
  - [ ] Update Composables to use `koinViewModel()`

- [ ] **Activities/Fragments**
  - [ ] Remove `@AndroidEntryPoint`
  - [ ] Convert field injection to `by inject()`

- [ ] **Testing**
  - [ ] Remove `@HiltAndroidTest`
  - [ ] Implement `KoinTest`
  - [ ] Add `startKoin` / `stopKoin` in setup/teardown

- [ ] **Verification**
  - [ ] Build project successfully
  - [ ] Run all tests
  - [ ] Test in-app dependency injection
  - [ ] Verify no runtime crashes

## Troubleshooting

### "No definition found for X"

**Issue**: Koin can't find a definition for a type.

**Solution**:
- Ensure the module is loaded in `startKoin { modules(...) }`
- Check that the definition exists (use `single { }` or `factory { }`)
- Verify the correct type is specified

### "DefinitionOverrideException"

**Issue**: Multiple definitions for the same type.

**Solution**:
- Use qualifiers: `single(named("qualifier")) { }`
- Enable override: `startKoin { allowOverride(true) }`

### Circular Dependencies

**Issue**: Two classes depend on each other.

**Solution**:
- Use `lazy` injection: `private val service by lazy { get<MyService>() }`
- Refactor to remove circular dependency
- Use scopes to break the cycle

## Additional Resources

- **Real-World Migration**: [Migrating Now in Android to Koin](https://blog.insert-koin.io/migrating-now-in-android-to-koin-annotations-2-3-67d252dbb97d)
- **Koin Documentation**: [Getting Started](/docs/setup/koin)
- **Koin Annotations**: [Android Annotations Guide](/docs/quickstart/android-annotations)

## Need Help?

- **GitHub Discussions**: Ask questions in the [Koin repository](https://github.com/InsertKoinIO/koin/discussions)
- **Slack**: Join the Koin community on Slack
- **Stack Overflow**: Tag questions with `koin`

---

### Reference Koin Android Instrumented Testing (`docs/reference/koin-android/instrumented-testing.md`)

---
title: Android Instrumented Testing
---

## Overview

Instrumented tests run on Android devices or emulators and test your app's integration with the Android framework. Unlike unit tests where you control Koin's lifecycle, instrumented tests require special handling because Koin is started by your `Application` class.

### Key Differences from Unit Tests

| Aspect | Unit Tests | Instrumented Tests |
|--------|------------|-------------------|
| **Execution** | JVM only | Android device/emulator |
| **Koin Start** | In test class (`startKoin`) | In `Application.onCreate()` |
| **Speed** | Fast | Slower |
| **Android APIs** | Mocked | Real |
| **Test Isolation** | Easy (each test starts fresh) | Requires careful setup |
| **Use Case** | Business logic, ViewModels | UI, Android components integration |

### What to Test with Instrumented Tests

✅ **Good for instrumented tests:**
- UI behavior and interactions
- Android component integration (Activity, Fragment, Service)
- Navigation flows
- Database operations with Room
- Shared preferences and file I/O
- Compose UI testing

❌ **Better as unit tests:**
- Business logic
- ViewModels (can be unit tested)
- Repositories (can be unit tested with mocks)
- Pure Kotlin functions

## Test Strategies

### Strategy 1: Custom Test Application

Create a separate Application class for tests with test-specific modules.

### Strategy 2: Test Rules

Use JUnit rules to configure Koin per test class or test method.

### Strategy 3: Module Override

Keep production Application but override specific definitions for testing.

Let's explore each strategy in detail.

## Override production modules in a custom Application class

Unlike [unit tests](/docs/reference/koin-test/testing), where you effectively call start Koin in each test class (i.e. `startKoin` or `KoinTestExtension`), in Instrumented tests Koin is started by your `Application` class. 

For overriding production Koin modules, `loadModules` and `unloadModules` are often unsafe because the changes are not applied immediately. Instead, the recommended approach is to add a `module` of your overrides to `modules` used by `startKoin` in the `Application` class.
If you want to keep the class that extends `Application` of your application untouched, you can create another one inside the `AndroidTest` package like:
```kotlin
class TestApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            modules(productionModule, instrumentedTestModule)
        }
    }
}
```
In order to use this custom `Application` in yours Instrumentation tests you may need to create a custom [AndroidJUnitRunner](https://developer.android.com/training/testing/instrumented-tests/androidx-test-libraries/runner) like:
```kotlin
class InstrumentationTestRunner : AndroidJUnitRunner() {
    override fun newApplication(
        classLoader: ClassLoader?,
        className: String?,
        context: Context?
    ): Application {
        return super.newApplication(classLoader, TestApplication::class.java.name, context)
    }
}
```
And then register it inside your gradle file with:
```groovy
testInstrumentationRunner "com.example.myapplication.InstrumentationTestRunner"
```

## Override production modules with a test rule

If you want more flexibility, you still have to create the custom `AndroidJUnitRunner` but instead of having `startKoin { ... }` inside the custom application, you can put it inside a custom test rule like:
```kotlin
class KoinTestRule(
    private val modules: List<Module>
) : TestWatcher() {
    override fun starting(description: Description) {

        if (getKoinApplicationOrNull() == null) {
            startKoin {
                androidContext(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext)
                modules(modules)
            }
        } else {
            loadKoinModules(modules)
        }
    }

    override fun finished(description: Description) {
        unloadKoinModules(modules)
    }
}
```
In this way we can potentially override the definitions directly from our test classes, like:
```kotlin
private val instrumentedTestModule = module {
    factory<Something> { FakeSomething() }
}

@get:Rule
val koinTestRule = KoinTestRule(
    modules = listOf(productionModule, instrumentedTestModule)
)
```

## Mocking and Fakes

### Using `declareMock()` (Recommended)

:::info
**Koin 4.2+:** Use `declareMock()` to quickly mock dependencies on-the-fly in tests without creating separate test modules.
:::

```kotlin
class UserViewModelTest : KoinTest {

    @get:Rule
    val koinTestRule = KoinTestRule.create {
        modules(
            module {
                viewModelOf(::UserViewModel)
                // Other production dependencies
            }
        )
    }

    @Test
    fun `test user loading`() {
        // Declare mock on the fly
        declareMock<UserRepository> {
            coEvery { getUser(any()) } returns User("1", "Test User")
        }

        val viewModel: UserViewModel by inject()
        // Test with mocked repository
    }
}
```

**Benefits of `declareMock()`:**
- ✅ No need to create separate test modules
- ✅ Mock only what you need per test
- ✅ Cleaner test code
- ✅ Works with MockK out of the box

### Using Test Doubles

Replace real implementations with mocks or fakes for testing:

```kotlin
// Production module
val productionModule = module {
    single<UserRepository> { UserRepositoryImpl(get()) }
    single { ApiService.create() }
}

// Test module with fakes
val testModule = module {
    single<UserRepository> { FakeUserRepository() }
    single<ApiService> { FakeApiService() }
}

// Fake implementation
class FakeUserRepository : UserRepository {
    private val users = mutableListOf<User>()

    override suspend fun getUser(id: String): User {
        return users.find { it.id == id } ?: throw UserNotFoundException()
    }

    override suspend fun saveUser(user: User) {
        users.add(user)
    }

    // Test-specific methods
    fun clearUsers() {
        users.clear()
    }
}
```

### Using MockK

```kotlin
// Test module with MockK
val mockModule = module {
    single<UserRepository> {
        mockk<UserRepository> {
            coEvery { getUser(any()) } returns User("1", "Test User")
            coEvery { saveUser(any()) } just Runs
        }
    }
}

// Test application
class TestApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@TestApplication)
            modules(mockModule)
        }
    }
}
```

### Partial Mocking

Replace only specific dependencies:

```kotlin
val testModule = module {
    // Keep real implementations
    single { Database.create(androidContext()) }

    // Mock network layer
    single<ApiService> { mockk<ApiService>() }

    // Use real repository with mocked API
    single<UserRepository> { UserRepositoryImpl(get()) }
}
```

## Testing Activities and Fragments

### Testing Activity with Koin

```kotlin
@RunWith(AndroidJUnit4::class)
class LoginActivityTest {

    @get:Rule
    val koinTestRule = KoinTestRule(
        modules = listOf(
            module {
                viewModel { LoginViewModel(get()) }
                single<AuthService> { FakeAuthService() }
            }
        )
    )

    @Test
    fun testSuccessfulLogin() {
        val scenario = ActivityScenario.launch(LoginActivity::class.java)

        onView(withId(R.id.email)).perform(typeText("user@example.com"))
        onView(withId(R.id.password)).perform(typeText("password123"))
        onView(withId(R.id.loginButton)).perform(click())

        onView(withId(R.id.successMessage)).check(matches(isDisplayed()))

        scenario.close()
    }
}
```

### Testing Fragment with Koin

```kotlin
@RunWith(AndroidJUnit4::class)
class ProfileFragmentTest {

    @get:Rule
    val koinTestRule = KoinTestRule(
        modules = listOf(
            module {
                viewModel { ProfileViewModel(get()) }
                single<UserRepository> {
                    mockk {
                        coEvery { getUser(any()) } returns User("1", "Test User")
                    }
                }
            }
        )
    )

    @Test
    fun testProfileDisplaysUserInfo() {
        val scenario = launchFragmentInContainer<ProfileFragment>()

        onView(withId(R.id.userName)).check(matches(withText("Test User")))

        scenario.close()
    }
}
```

## Testing ViewModels in Instrumented Tests

### Injecting ViewModel in Tests

```kotlin
@RunWith(AndroidJUnit4::class)
class HomeViewModelTest : KoinTest {

    @get:Rule
    val koinTestRule = KoinTestRule(
        modules = listOf(
            module {
                viewModelOf(::HomeViewModel)
                single<UserRepository> { FakeUserRepository() }
            }
        )
    )

    private val viewModel: HomeViewModel by inject()

    @Test
    fun testLoadUserData() = runTest {
        viewModel.loadUser("123")

        val state = viewModel.userState.value
        assertEquals("Test User", state.name)
    }
}
```

### Testing ViewModel with Activity

```kotlin
@Test
fun testViewModelStateReflectsInUI() {
    val scenario = ActivityScenario.launch(HomeActivity::class.java)

    scenario.onActivity { activity ->
        val viewModel: HomeViewModel = activity.viewModel

        // Trigger ViewModel action
        viewModel.loadUser("123")

        // Verify UI updated
        onView(withId(R.id.userName)).check(matches(withText("Test User")))
    }
}
```

## Testing with Jetpack Compose

### Compose UI Test with Koin

```kotlin
@RunWith(AndroidJUnit4::class)
class LoginScreenTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @get:Rule
    val koinTestRule = KoinTestRule(
        modules = listOf(
            module {
                viewModelOf(::LoginViewModel)
                single<AuthService> { FakeAuthService() }
            }
        )
    )

    @Test
    fun testLoginFlow() {
        composeTestRule.setContent {
            KoinContext {
                LoginScreen()
            }
        }

        composeTestRule.onNodeWithTag("email_field")
            .performTextInput("user@example.com")

        composeTestRule.onNodeWithTag("password_field")
            .performTextInput("password123")

        composeTestRule.onNodeWithTag("login_button")
            .performClick()

        composeTestRule.onNodeWithTag("success_message")
            .assertIsDisplayed()
    }
}
```

### Testing Composables with koinViewModel

```kotlin
@Composable
fun HomeScreen(viewModel: HomeViewModel = koinViewModel()) {
    val user by viewModel.user.collectAsState()

    Text(text = user?.name ?: "Loading...")
}

// Test
@Test
fun testHomeScreenDisplaysUser() {
    composeTestRule.setContent {
        KoinContext {
            HomeScreen()
        }
    }

    composeTestRule.onNodeWithText("Test User")
        .assertIsDisplayed()
}
```

## Testing Scopes

### Testing Activity Scope

```kotlin
@RunWith(AndroidJUnit4::class)
class CheckoutActivityTest {

    @get:Rule
    val koinTestRule = KoinTestRule(
        modules = listOf(
            module {
                activityScope {
                    scoped { CheckoutState() }
                }
            }
        )
    )

    @Test
    fun testActivityScopeSharedAcrossFragments() {
        val scenario = ActivityScenario.launch(CheckoutActivity::class.java)

        scenario.onActivity { activity ->
            val state1 = activity.scope.get<CheckoutState>()
            state1.selectedAddress = Address("123 Main St")

            // Navigate to next fragment
            activity.supportFragmentManager.commit {
                replace(R.id.container, PaymentFragment())
            }

            // Same scope accessible in fragment
            val fragment = activity.supportFragmentManager
                .findFragmentById(R.id.container) as PaymentFragment

            val state2 = fragment.scope.get<CheckoutState>()
            assertEquals(state1, state2)
            assertEquals("123 Main St", state2.selectedAddress?.street)
        }
    }
}
```

### Testing Custom Scopes

```kotlin
@Test
fun testCustomScopeLifecycle() {
    val testModule = module {
        scope(named("session")) {
            scoped { UserSession() }
        }
    }

    koinApplication {
        modules(testModule)

        // Create scope
        val sessionScope = koin.createScope("test_session", named("session"))
        val session = sessionScope.get<UserSession>()

        session.login("user@example.com")
        assertTrue(session.isLoggedIn)

        // Close scope
        sessionScope.close()

        // Scope is closed, can't access
        assertThrows<ClosedScopeException> {
            sessionScope.get<UserSession>()
        }
    }
}
```

## Testing Multi-Module Apps

### Testing with Feature Modules

```kotlin
@RunWith(AndroidJUnit4::class)
class MultiModuleTest {

    @get:Rule
    val koinTestRule = KoinTestRule(
        modules = listOf(
            // Core modules
            networkModule,
            databaseModule,

            // Feature modules
            loginModule,
            homeModule,

            // Test overrides
            module {
                single<ApiService>(override = true) { FakeApiService() }
            }
        )
    )

    @Test
    fun testFeatureIntegration() {
        // Test that login feature works with home feature
        val loginViewModel: LoginViewModel by inject()
        val homeViewModel: HomeViewModel by inject()

        runBlocking {
            loginViewModel.login("user@example.com", "password")
            homeViewModel.loadUserData()
        }

        assertEquals("user@example.com", homeViewModel.userState.value.email)
    }
}
```

### Module Verification in Tests

```kotlin
class ModuleVerificationTest {

    @Test
    fun verifyAllModules() {
        // Verify all definitions are satisfied
        appModule.verify()  // appModule includes other modules
    }

    @Test
    fun verifyTestModules() {
        testAppModule.verify()
    }
}
```

:::tip
The Koin Compiler Plugin now provides compile-time dependency validation, replacing the need for `verify()` and `checkModules()`. See [Compile-Time Safety](/docs/reference/koin-compiler/compile-safety).
:::

## UI Tests with Espresso

### Complete UI Flow Test

```kotlin
@RunWith(AndroidJUnit4::class)
@LargeTest
class CheckoutFlowTest {

    @get:Rule
    val koinTestRule = KoinTestRule(
        modules = listOf(
            module {
                viewModel { CheckoutViewModel(get(), get()) }
                single<CartRepository> { FakeCartRepository() }
                single<PaymentService> { FakePaymentService() }
            }
        )
    )

    @get:Rule
    val activityRule = ActivityScenarioRule(MainActivity::class.java)

    @Test
    fun testCompleteCheckoutFlow() {
        // Navigate to cart
        onView(withId(R.id.cartButton)).perform(click())

        // Add items to cart
        onView(withId(R.id.addItemButton)).perform(click())
        onView(withId(R.id.cartItemCount)).check(matches(withText("1")))

        // Proceed to checkout
        onView(withId(R.id.checkoutButton)).perform(click())

        // Fill shipping address
        onView(withId(R.id.addressField))
            .perform(typeText("123 Main St"))

        onView(withId(R.id.nextButton)).perform(click())

        // Enter payment info
        onView(withId(R.id.cardNumberField))
            .perform(typeText("4111111111111111"))

        onView(withId(R.id.completeOrderButton)).perform(click())

        // Verify order confirmation
        onView(withId(R.id.confirmationMessage))
            .check(matches(isDisplayed()))
    }
}
```

### Testing Navigation

```kotlin
@Test
fun testNavigationWithSharedState() {
    onView(withId(R.id.loginButton)).perform(click())

    // Login screen
    onView(withId(R.id.emailField)).perform(typeText("user@example.com"))
    onView(withId(R.id.passwordField)).perform(typeText("password"))
    onView(withId(R.id.submitButton)).perform(click())

    // Should navigate to home
    onView(withId(R.id.homeTitle)).check(matches(isDisplayed()))

    // User data should be available (shared through Koin)
    onView(withId(R.id.welcomeMessage))
        .check(matches(withText("Welcome, user@example.com")))
}
```

## Test Isolation

### Ensuring Clean State Between Tests

```kotlin
class KoinIsolationTestRule : TestWatcher() {

    override fun starting(description: Description) {
        // Start fresh Koin instance
        startKoin {
            androidContext(InstrumentationRegistry.getInstrumentation().targetContext)
            modules(emptyList())
        }
    }

    override fun finished(description: Description) {
        // Clean up after each test
        stopKoin()
    }
}

@RunWith(AndroidJUnit4::class)
class IsolatedTest {

    @get:Rule
    val isolationRule = KoinIsolationTestRule()

    @Test
    fun test1() {
        loadKoinModules(module { single { "Test1" } })
        assertEquals("Test1", get<String>())
    }

    @Test
    fun test2() {
        // Fresh Koin instance, no pollution from test1
        loadKoinModules(module { single { "Test2" } })
        assertEquals("Test2", get<String>())
    }
}
```

### Resetting Fakes Between Tests

```kotlin
class FakeUserRepository : UserRepository {
    private val users = mutableListOf<User>()

    override suspend fun getUser(id: String): User =
        users.find { it.id == id } ?: throw UserNotFoundException()

    fun reset() {
        users.clear()
    }
}

@RunWith(AndroidJUnit4::class)
class UserTest {

    private val fakeRepo = FakeUserRepository()

    @get:Rule
    val koinTestRule = KoinTestRule(
        modules = listOf(
            module {
                single<UserRepository> { fakeRepo }
            }
        )
    )

    @Before
    fun setup() {
        fakeRepo.reset()
    }

    @Test
    fun test1() {
        // Test with clean repository
    }

    @Test
    fun test2() {
        // Test with clean repository (reset was called)
    }
}
```

## Common Patterns

### Pattern 1: Shared Test Module

```kotlin
// TestModules.kt in androidTest package
object TestModules {

    val fakeNetworkModule = module {
        single<ApiService> { FakeApiService() }
        single { OkHttpClient() }
    }

    val fakeDatabaseModule = module {
        single { createInMemoryDatabase() }
        single<UserDao> { get<AppDatabase>().userDao() }
    }

    val fakeDataModule = module {
        single<UserRepository> { FakeUserRepository() }
    }

    fun createInMemoryDatabase(): AppDatabase {
        return Room.inMemoryDatabaseBuilder(
            InstrumentationRegistry.getInstrumentation().targetContext,
            AppDatabase::class.java
        ).build()
    }
}

// Use in tests
@get:Rule
val koinTestRule = KoinTestRule(
    modules = TestModules.fakeNetworkModule + TestModules.fakeDataModule
)
```

### Pattern 2: Test-Specific Configuration

```kotlin
class TestConfig {
    companion object {
        const val TEST_API_URL = "http://localhost:8080"
        const val TEST_TIMEOUT_MS = 1000L
    }
}

val testConfigModule = module {
    single {
        OkHttpClient.Builder()
            .connectTimeout(TestConfig.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
            .build()
    }

    single {
        Retrofit.Builder()
            .baseUrl(TestConfig.TEST_API_URL)
            .client(get())
            .build()
    }
}
```

### Pattern 3: Per-Test Override

```kotlin
@RunWith(AndroidJUnit4::class)
class FlexibleTest : KoinTest {

    @get:Rule
    val koinTestRule = KoinTestRule(modules = emptyList())

    @Test
    fun testWithFakeRepo() {
        loadKoinModules(module {
            single<UserRepository> { FakeUserRepository() }
        })

        // Test code
    }

    @Test
    fun testWithMockRepo() {
        loadKoinModules(module {
            single<UserRepository> { mockk<UserRepository>() }
        })

        // Test code
    }

    @After
    fun cleanup() {
        unloadKoinModules(/* modules loaded in test */)
    }
}
```

## Troubleshooting

### Issue: Koin Already Started

**Problem:**
```
org.koin.core.error.KoinAppAlreadyStartedException: A Koin Application has already been started
```

**Solution:**
```kotlin
class SafeKoinTestRule : TestWatcher() {
    override fun starting(description: Description) {
        // Check if Koin is already started
        if (getKoinApplicationOrNull() == null) {
            startKoin {
                modules(testModules)
            }
        } else {
            // Load modules into existing Koin instance
            loadKoinModules(testModules)
        }
    }

    override fun finished(description: Description) {
        // Don't stop Koin, just unload test modules
        unloadKoinModules(testModules)
    }
}
```

### Issue: Definition Override Doesn't Work

**Problem:**
Test definition doesn't replace production definition.

**Solution:**
```kotlin
// Use override = true
val testModule = module {
    single<UserRepository>(override = true) { FakeUserRepository() }
}

// Or use includes to replace
val testModule = module {
    includes(productionModule)
} + module {
    single<UserRepository>(override = true) { FakeUserRepository() }
}
```

### Issue: Scope Not Found

**Problem:**
```
org.koin.core.error.NoBeanDefFoundException: No definition found for class X
```

**Solution:**
```kotlin
// Ensure scope is created before accessing
val scenario = ActivityScenario.launch(MyActivity::class.java)

scenario.onActivity { activity ->
    // Scope exists here
    val dependency = activity.scope.get<MyDependency>()
}
```

### Issue: Tests Affecting Each Other

**Problem:**
Tests pass individually but fail when run together.

**Solution:**
```kotlin
// Proper cleanup between tests
@After
fun tearDown() {
    // Close scopes
    getKoin().scopeRegistry.deleteScope("test_scope")

    // Reset fakes
    fakeRepository.reset()

    // Unload test modules
    unloadKoinModules(testModules)
}
```

### Issue: ViewModel Not Updating UI

**Problem:**
ViewModel state changes but UI doesn't update in tests.

**Solution:**
```kotlin
// Use Espresso's IdlingResource for async operations
@get:Rule
val activityRule = ActivityScenarioRule(MyActivity::class.java)

@Test
fun testViewModelUpdatesUI() = runTest {
    activityRule.scenario.onActivity { activity ->
        val viewModel: MyViewModel = activity.viewModel

        // Trigger async action
        viewModel.loadData()

        // Wait for LiveData/StateFlow to emit
        advanceUntilIdle()

        // Then verify UI
        onView(withId(R.id.dataText))
            .check(matches(withText("Data Loaded")))
    }
}
```

## Best Practices

### 1. Use In-Memory Database for Tests

```kotlin
val testDatabaseModule = module {
    single {
        Room.inMemoryDatabaseBuilder(
            androidContext(),
            AppDatabase::class.java
        ).build()
    }
}
```

### 2. Keep Test Modules Focused

```kotlin
// ✅ Good - Focused test module
val loginTestModule = module {
    viewModel { LoginViewModel(get()) }
    single<AuthService> { FakeAuthService() }
}

// ❌ Bad - Too broad
val hugeTestModule = module {
    // 50+ definitions...
}
```

### 3. Share Common Fakes

```kotlin
// Create reusable test doubles
object TestDoubles {
    fun createFakeUserRepository() = FakeUserRepository().apply {
        addUser(User("1", "Test User"))
    }

    fun createMockApiService() = mockk<ApiService> {
        coEvery { getUser(any()) } returns User("1", "Test User")
    }
}
```

### 4. Test Real Integration Points

```kotlin
// Test real Room + Repository integration
@Test
fun testDatabaseIntegration() = runTest {
    val database = Room.inMemoryDatabaseBuilder(
        context,
        AppDatabase::class.java
    ).build()

    val repo = UserRepositoryImpl(database.userDao())

    repo.saveUser(User("1", "Test"))
    val user = repo.getUser("1")

    assertEquals("Test", user.name)
}
```

### 5. Use Descriptive Test Names

```kotlin
// ✅ Good
@Test
fun loginWithValidCredentials_navigatesToHomeScreen()

@Test
fun loginWithInvalidEmail_showsEmailError()

// ❌ Bad
@Test
fun test1()

@Test
fun testLogin()
```

## Summary

Key points for instrumented testing with Koin:

- **Custom Test Application** or **Test Rules** for Koin configuration
- **Override modules** using `override = true` or test-specific modules
- **Use fakes over mocks** for better performance in instrumented tests
- **Test isolation** is critical - clean up between tests
- **In-memory databases** for fast, isolated database tests
- **Compose Testing** works seamlessly with `KoinContext`
- **Scope testing** validates lifecycle-bound dependencies
- **Module verification** with the Koin Compiler Plugin (compile-time) or `verify()` (runtime) catches configuration errors early

## Next Steps

- **[Unit Testing](/docs/reference/koin-test/testing)** - Testing strategies for unit tests
- **[Module Verification](/docs/reference/koin-test/verify)** - Verify module configuration
- **[Multi-Module Apps](/docs/reference/koin-android/multi-module)** - Testing multi-module architecture
- **[Best Practices](/docs/reference/koin-android/best-practices)** - Overall Koin best practices

---

### Reference Koin Android Jsr330 (`docs/reference/koin-android/jsr330.md`)

---
title: JSR-330 Compatibility
---

Koin provides full **JSR-330** (Jakarta Inject) compatibility through Koin Annotations and the Kotlin Compiler Plugin. This makes Koin an excellent choice for teams migrating from Hilt, Dagger, or other JSR-330 compatible frameworks.

## Why JSR-330?

JSR-330 defines a standard set of annotations for dependency injection in Java/Kotlin:

| JSR-330 Annotation | Koin Equivalent | Purpose |
|-------------------|-----------------|---------|
| `@Inject` | Constructor detection | Mark injectable constructors |
| `@Singleton` | `@Single` | Single instance scope |
| `@Named` | `@Named` | String-based qualifier |
| `@Qualifier` | `@Qualifier` | Custom qualifier annotations |
| `@Scope` | `@Scope` | Custom scope annotations |

## Setup

Add the `koin-jsr330` dependency to your project:

```kotlin
dependencies {
    implementation("io.insert-koin:koin-jsr330:$koin_version")
}
```

## Basic Usage

### @Singleton and @Inject

Your existing Hilt/Dagger classes work with Koin:

```kotlin
import jakarta.inject.Inject
import jakarta.inject.Singleton

@Singleton
class UserRepository @Inject constructor(
    private val api: ApiService,
    private val database: UserDatabase
)

@Singleton
class ApiService @Inject constructor(
    private val httpClient: OkHttpClient
)
```

### @Named Qualifiers

String-based qualifiers work identically:

```kotlin
import jakarta.inject.Named
import jakarta.inject.Singleton

@Singleton
@Named("api")
class ApiOkHttpClient @Inject constructor() : OkHttpClient()

@Singleton
@Named("image")
class ImageOkHttpClient @Inject constructor() : OkHttpClient()

@Singleton
class NetworkManager @Inject constructor(
    @Named("api") private val apiClient: OkHttpClient,
    @Named("image") private val imageClient: OkHttpClient
)
```

### Custom @Qualifier Annotations

Keep your existing custom qualifiers:

```kotlin
import jakarta.inject.Qualifier

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class IoDispatcher

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class MainDispatcher

@Singleton
class CoroutineModule {
    @IoDispatcher
    fun provideIoDispatcher(): CoroutineDispatcher = Dispatchers.IO

    @MainDispatcher
    fun provideMainDispatcher(): CoroutineDispatcher = Dispatchers.Main
}

@Singleton
class DataSyncManager @Inject constructor(
    @IoDispatcher private val ioDispatcher: CoroutineDispatcher
)
```

## Android ViewModel

JSR-330 annotations work with Koin's ViewModel support:

```kotlin
import jakarta.inject.Inject

@KoinViewModel
class UserViewModel @Inject constructor(
    private val userRepository: UserRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    fun loadUser(id: String) {
        viewModelScope.launch {
            val user = userRepository.getUser(id)
            // ...
        }
    }
}
```

:::note
Use `@KoinViewModel` (Koin annotation) for ViewModels. JSR-330 doesn't define a ViewModel annotation.
:::

## Migration from Hilt

### Before (Hilt)

```kotlin
@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var analytics: Analytics
}

@Singleton
class Analytics @Inject constructor(
    @ApplicationContext private val context: Context
)

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit = Retrofit.Builder()
        .baseUrl("https://api.example.com")
        .build()
}
```

### After (Koin with JSR-330)

```kotlin
// Application - use startKoin
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyApplication)
            modules(appModule)
        }
    }
}

// Activity - use by inject()
class MainActivity : AppCompatActivity() {
    private val analytics: Analytics by inject()
}

// Keep @Singleton and @Inject unchanged!
@Singleton
class Analytics @Inject constructor(
    private val context: Context  // Koin provides Context automatically
)

// Module with @Single for provided instances
@Module
@ComponentScan
class NetworkModule {
    @Single
    fun provideRetrofit(): Retrofit = Retrofit.Builder()
        .baseUrl("https://api.example.com")
        .build()
}
```

## Mixed Usage

You can freely mix JSR-330 and Koin annotations in the same project:

```kotlin
// JSR-330 style
@Singleton
@Named("local")
class LocalDatabase @Inject constructor() : Database

// Koin style
@Single
@Named("remote")
class RemoteDatabase(private val api: ApiService) : Database

// Mixed - Koin annotation with JSR-330 injection
@Factory
class DatabaseManager @Inject constructor(
    @Named("local") private val local: Database,
    @Named("remote") private val remote: Database
)
```

## Compiler Plugin Support

The Koin Compiler Plugin also recognizes JSR-330 annotations:

```kotlin
// These are equivalent with Compiler Plugin
@Singleton
class UserRepository @Inject constructor(private val api: ApiService)

// Compiler Plugin DSL (generates same code)
val module = module {
    single<UserRepository>()
}
```

## Benefits

- **Zero code changes** - Existing `@Inject` and `@Singleton` annotations work as-is
- **Gradual migration** - Migrate module by module, mixing Hilt and Koin
- **Standard compliance** - JSR-330 is a Java/Kotlin standard, not framework-specific
- **Team familiarity** - Developers know these annotations from Hilt/Dagger
- **Tooling support** - IDE support for JSR-330 annotations

## Limitations

A few Hilt-specific features don't have direct JSR-330 equivalents:

| Hilt Feature | Koin Approach |
|-------------|---------------|
| `@HiltAndroidApp` | `@KoinApplication` + `startKoin<T>{}` in Application |
| `@AndroidEntryPoint` | `by inject()` extension functions |
| `@HiltViewModel` | `@KoinViewModel` |
| `@ApplicationContext` | `Context` parameter (auto-injected) |
| `@ActivityContext` | Scope-based context resolution |

## Next Steps

- **[Hilt Migration Guide](/docs/reference/koin-android/hilt-migration)** - Complete migration walkthrough
- **[Qualifiers](/docs/reference/koin-android/qualifiers)** - All qualifier options in Koin
- **[Definitions](/docs/reference/koin-annotations/definitions#jsr-330-compatibility-annotations)** - Full JSR-330 annotation reference

---

### Reference Koin Android Modules Android (`docs/reference/koin-android/modules-android.md`)

---
title: Android Module Loading
---

This guide covers Android-specific module loading with `androidContext()` and `androidLogger()`.

:::info
For core module concepts (declaration, includes, overrides), see [Modules](/docs/reference/koin-core/modules). For lazy module loading, see [Lazy Modules](/docs/reference/koin-core/lazy-modules).
:::

## Starting Koin on Android

### With Annotations

```kotlin
@KoinApplication
class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin<MainApplication> {
            androidLogger()
            androidContext(this@MainApplication)
        }
    }
}
```

### With DSL

```kotlin
class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin {
            // Android logger
            androidLogger()
            // or with level
            androidLogger(Level.DEBUG)

            // Android context
            androidContext(this@MainApplication)

            // Modules
            modules(appModule, networkModule, dataModule)
        }
    }
}
```

## Android-Specific Functions

| Function | Description |
|----------|-------------|
| `androidContext()` | Provides Application context in definitions |
| `androidApplication()` | Provides Application instance in definitions |
| `androidLogger()` | Android Logcat logger for Koin |

### Using Android Context

```kotlin
val androidModule = module {
    single { DatabaseHelper(androidContext()) }
    single { SharedPrefsManager(androidContext()) }
    single { NotificationHelper(androidApplication()) }
}
```

## Dynamic Module Loading

Load or unload modules at runtime based on Activity lifecycle:

```kotlin
class FeatureActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Load feature-specific dependencies
        loadKoinModules(featureModule)
        super.onCreate(savedInstanceState)
    }

    override fun onDestroy() {
        super.onDestroy()
        // Clean up when leaving feature
        unloadKoinModules(featureModule)
    }
}
```

### Use Cases

- **Premium features** - Load only when user has subscription
- **Debug tools** - Load only in debug builds
- **Optional features** - Load on-demand

```kotlin
// Premium feature module
val premiumModule = module {
    viewModel<PremiumViewModel>()
    single<PremiumRepository>()
}

class PremiumActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        if (userHasPremium()) {
            loadKoinModules(premiumModule)
        }
        super.onCreate(savedInstanceState)
    }
}
```

## Lazy Loading on Android

For background module loading, use lazy modules:

```kotlin
class MainApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger()
            androidContext(this@MainApplication)

            // Critical modules load immediately
            modules(coreModule)

            // Non-critical modules load in background
            lazyModules(analyticsModule, syncModule)
        }
    }
}
```

:::info
For complete lazy modules documentation including parallel loading, see [Lazy Modules](/docs/reference/koin-core/lazy-modules).
:::

## Next Steps

- **[Modules](/docs/reference/koin-core/modules)** - Core module concepts
- **[Lazy Modules](/docs/reference/koin-core/lazy-modules)** - Background loading
- **[Multi-Module Apps](/docs/reference/koin-android/multi-module)** - Gradle multi-module architecture

---

### Reference Koin Android Multi Module (`docs/reference/koin-android/multi-module.md`)

---
title: Multi-Module Android Apps
---

This guide covers Android-specific aspects of multi-module architecture with Koin.

:::info
For core module concepts (`includes()`, organization, overrides), see [Modules](/docs/reference/koin-core/modules).
:::

## Android Application Setup

### With Annotations

```kotlin
@KoinApplication(AppModule::class)
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin<MyApplication> {
            androidLogger()
            androidContext(this@MyApplication)
        }
    }
}

// Root module includes all features
@Module(includes = [LoginModule::class, HomeModule::class, ProfileModule::class])
class AppModule
```

### With DSL

```kotlin
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger()
            androidContext(this@MyApplication)
            modules(appModule)  // Single root module
        }
    }
}

// Root module includes all features
val appModule = module {
    includes(
        loginModule,
        homeModule,
        profileModule
    )
}
```

## Feature Module Example

```kotlin
// :feature:login module
@KoinViewModel
class LoginViewModel(
    private val loginUseCase: LoginUseCase,
    private val userRepository: UserRepository
) : ViewModel()

@Factory
class LoginUseCase(private val authService: AuthService)

@Module(includes = [DataModule::class])
@ComponentScan("com.app.feature.login")
class LoginModule
```

```kotlin
// DSL equivalent
val loginModule = module {
    includes(dataModule)
    viewModel<LoginViewModel>()
    factory<LoginUseCase>()
}
```

## Dynamic Feature Loading

Load feature modules on-demand with Activity lifecycle:

```kotlin
class FeatureActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        loadKoinModules(featureModule)
        super.onCreate(savedInstanceState)
    }

    override fun onDestroy() {
        super.onDestroy()
        unloadKoinModules(featureModule)
    }
}
```

## Koin vs Hilt Comparison

| Hilt | Koin |
|------|------|
| `@HiltAndroidApp` | `startKoin { androidContext() }` |
| `@InstallIn(SingletonComponent)` | Module loaded in `startKoin {}` |
| `@EntryPoint` for cross-module | Automatic resolution |
| Component dependencies | `includes()` |
| `@ApplicationContext` | `androidContext()` (automatic) |

:::info
**Koin Advantage:** No `@EntryPoint` interfaces needed. Dependencies resolve across modules automatically as long as all modules are loaded.
:::

## Android Testing

### Test Module in Isolation

```kotlin
class LoginViewModelTest : KoinTest {

    @get:Rule
    val koinTestRule = KoinTestRule.create {
        modules(
            loginModule,
            module {
                single<UserRepository> { mockk() }
                single<AuthService> { mockk() }
            }
        )
    }

    private val viewModel: LoginViewModel by inject()

    @Test
    fun `test login`() {
        // Test with mocked dependencies
    }
}
```

### Verify All Modules

:::tip
The Koin Compiler Plugin now validates your full dependency graph at compile time, replacing the need for runtime verification. See [Compile-Time Safety](/docs/reference/koin-compiler/compile-safety).
:::

If you're not using the compiler plugin, you can verify modules at runtime:

```kotlin
class ModuleCheckTest : KoinTest {

    @Test
    fun `verify all modules`() {
        appModule.verify()  // Verifies included modules too
    }
}
```

## See Also

- **[Modules](/docs/reference/koin-core/modules)** - Core module concepts with `includes()`
- **[Android Module Loading](/docs/reference/koin-android/modules-android)** - Dynamic module loading
- **[Lazy Modules](/docs/reference/koin-core/lazy-modules)** - Background module loading
- **[Testing](/docs/reference/koin-android/instrumented-testing)** - Android testing guide

---

### Reference Koin Android Qualifiers (`docs/reference/koin-android/qualifiers.md`)

---
title: Android Context & Qualifiers
---

This guide covers Android-specific qualifier patterns, particularly around Context handling.

:::info
For general qualifier concepts (named, type-safe, enums, JSR-330), see [Qualifiers](/docs/reference/koin-core/qualifiers).
:::

## Android Context - No Qualifiers Needed

Unlike Hilt, Koin automatically provides the Android Context without requiring qualifiers.

### Koin's Context Resolution

```kotlin
val androidModule = module {
    // Context is automatically available
    single {
        SharedPreferences(
            androidContext()  // Application context automatically provided
        )
    }

    single {
        NotificationManager(
            androidContext().getSystemService(Context.NOTIFICATION_SERVICE)
        )
    }
}
```

### No @ApplicationContext or @ActivityContext

**In Hilt, you need:**
```kotlin
// Hilt requires explicit qualifiers
class MyRepository @Inject constructor(
    @ApplicationContext private val context: Context
)
```

**In Koin, it's automatic:**
```kotlin
class MyRepository(
    private val context: Context  // Just use Context
)

val appModule = module {
    single { MyRepository(androidContext()) }
}
```

:::info
**Koin Advantage:** The `androidContext()` function always provides the Application context. No qualifiers needed to distinguish between Application and Activity contexts.
:::

## When You Need Activity Context

For cases where you need Activity context, don't inject it - use it directly:

```kotlin
class ScreenMetrics(private val activity: Activity) {
    fun getScreenSize(): Point {
        val display = activity.windowManager.defaultDisplay
        return Point().also { display.getSize(it) }
    }
}

// Don't define in modules - create directly in Activity
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val metrics = ScreenMetrics(this)  // Activity context used directly
    }
}
```

:::warning
**Best Practice:** Avoid injecting Activity context into long-lived objects. It causes memory leaks. Use Application context (`androidContext()`) for dependencies that outlive an Activity.
:::

## Qualified Android Dependencies

When you need multiple configurations of Android-specific dependencies:

```kotlin
val databaseModule = module {
    single(named("user_db")) {
        Room.databaseBuilder(
            androidContext(),
            UserDatabase::class.java,
            "user-database"
        ).build()
    }

    single(named("cache_db")) {
        Room.databaseBuilder(
            androidContext(),
            CacheDatabase::class.java,
            "cache-database"
        ).build()
    }
}
```

## Next Steps

- **[Qualifiers](/docs/reference/koin-core/qualifiers)** - Complete qualifier documentation
- **[Android Best Practices](/docs/reference/koin-android/best-practices)** - Memory management
- **[Android Scopes](/docs/reference/koin-android/scope)** - Lifecycle-aware scoping

---

### Reference Koin Android R8 Proguard (`docs/reference/koin-android/r8-proguard.md`)

---
title: R8 / ProGuard
---

This page explains how Koin behaves under code shrinking and obfuscation (R8 / ProGuard), what
Koin keeps for you, and what **you** need to keep in your own app.

## TL;DR

- **Koin's core resolution is R8-safe.** `get<T>()`, `inject<T>()`, and the `*Of` builders
  (`singleOf`, `factoryOf`, `viewModelOf`, …) resolve dependencies **at compile time** — they use
  reified types and, on Android/JVM, key the registry by `Class.getName()`. There is **no runtime
  reflection over your constructors**, so you do **not** need to keep your definitions, ViewModels,
  or their constructors on Koin's behalf.
- Koin ships `consumer-rules.pro` in its Android AARs (`koin-android`, `koin-core-viewmodel`,
  `koin-compose-viewmodel`, `koin-androidx-workmanager`, `koin-androidx-startup`), so the rules
  below are applied automatically — you usually don't add anything.
- You still need to keep classes that **something else** loads reflectively (see below).

## What Koin keeps for you (shipped consumer rules)

The AARs silence R8 warnings about Koin internals:

```proguard
-dontwarn org.koin.**
```

`koin-androidx-startup` additionally keeps its manifest-referenced initializer. None of these keep
your application classes — Koin doesn't need them kept.

## What you must keep

These come from the platform/libraries, not from Koin's resolution:

- **Fragments created by `KoinFragmentFactory`** are instantiated by class name. Keep your Fragment
  subclasses (they're usually kept already via `@Keep`, layout references, or AndroidX rules).
- **WorkManager `ListenableWorker` subclasses** are kept by `androidx.work`'s own consumer rules.
- **Saved state for process death.** `SavedStateHandle` is provided by androidx `CreationExtras`,
  not by Koin. The values you put into it must survive R8 like any other saved state — keep your
  own `@Parcelize` / `Serializable` state classes:

```proguard
# Example — keep your own saved-state payloads
-keep class com.example.** implements android.os.Parcelable { *; }
```

## ViewModels & SavedStateHandle (#2044)

A common belief is that intermittent `No definition found for SavedStateHandle` crashes are caused
by R8 stripping Koin's ViewModel reflection. **They are not** — `viewModelOf(::MyViewModel)` is
compile-time, so a `-keep` on your ViewModel won't change Koin's resolution.

`SavedStateHandle` is only available **while the ViewModel is being created** (it is built from the
`CreationExtras` passed to the factory). Resolve it **directly in the ViewModel constructor** — do
not resolve it lazily or after construction:

```kotlin
// ✅ resolved during creation
class MyViewModel(val handle: SavedStateHandle) : ViewModel()

// ❌ resolved later — the CreationExtras are gone by then
class MyViewModel(koin: Koin) : ViewModel() {
    val handle by lazy { koin.get<SavedStateHandle>() } // fails
}
```

If you declare ViewModels inside `viewModelScope { }`, enable the matching option so the scope can
be created:

```kotlin
startKoin {
    options(viewModelScopeFactory())
    modules(appModule)
}
```

## Non-Android targets (JS / WASM / Native)

On Android/JVM Koin keys the registry by `Class.getName()`, which is stable under R8. On
**Kotlin/JS, WASM, and Native**, Koin uses `qualifiedName` / `simpleName` from Kotlin reflection.
Aggressive name minification on those targets can affect type identity — prefer **named
qualifiers** (`named("...")`) over relying on class names when you minify non-Android targets.

---

### Reference Koin Android Scope (`docs/reference/koin-android/scope.md`)

---
title: Android Scopes
---

This guide covers Android-specific scope implementations. 

:::info
For core scope concepts, see [Scopes](/docs/reference/koin-core/scopes).
:::

## Overview

Scopes in Koin allow you to manage the lifecycle of your dependencies to match Android component lifecycles. This prevents memory leaks and ensures proper resource management.

### Scope Hierarchy

| Scope Type | Lifetime | Survives Rotation | DSL | Annotation |
|------------|----------|-------------------|-----|------------|
| **Application** | Entire app | ✅ Yes | `single { }` | `@Singleton` |
| **Activity** | Activity lifecycle | ❌ No | `activityScope { }` | `@ActivityScope` |
| **Activity Retained** | Until finish() | ✅ Yes | `activityRetainedScope { }` | `@ActivityRetainedScope` |
| **Fragment** | Fragment lifecycle | ❌ No | `fragmentScope { }` | `@FragmentScope` |
| **ViewModel** | ViewModel lifecycle | ✅ Yes | `viewModelScope { }` | `@ViewModelScope` |

### Scope Relationships

```
Application Scope (single { })
    └── Activity Retained Scope (survives rotation)
            └── Activity Scope
                    ├── Fragment Scope 1
                    └── Fragment Scope 2
            └── ViewModel Scope (can't access Activity/Fragment scope)
```

:::info
**Key Principle:** Child scopes can access parent scope definitions, but not vice versa.
:::

## Declaring Scoped Dependencies

### Compiler Plugin DSL

```kotlin
val appModule = module {
    // Activity scope
    activityScope {
        scoped<ActivityPresenter>()
        scoped<ActivityNavigator>()
    }

    // Fragment scope
    fragmentScope {
        scoped<FragmentPresenter>()
    }

    // ViewModel scope
    viewModelScope {
        scoped<UserCache>()
        viewModel<UserViewModel>()
    }
}
```

### Annotations

```kotlin
// Activity scope
@ActivityScope
class ActivityPresenter(private val repository: UserRepository)

@ActivityScope
class ActivityNavigator

// Activity retained scope (survives rotation)
@ActivityRetainedScope
class RetainedPresenter

// Fragment scope
@FragmentScope
class FragmentPresenter

// ViewModel scope
@ViewModelScope
class UserCache

@KoinViewModel
@ViewModelScope
class UserViewModel(private val cache: UserCache) : ViewModel()
```

### Classic DSL

```kotlin
val appModule = module {
    activityScope {
        scoped { ActivityPresenter(get()) }
        scoped { ActivityNavigator() }
    }

    fragmentScope {
        scoped { FragmentPresenter(get()) }
    }

    viewModelScope {
        scoped { UserCache() }
        viewModel { UserViewModel(get()) }
    }
}
```

## Using Scopes in Android Components

### Activity Scope

```kotlin
class MyActivity : AppCompatActivity(), AndroidScopeComponent {

    // Create scope tied to Activity lifecycle
    override val scope: Scope by activityScope()

    // Inject from scope
    private val presenter: ActivityPresenter by inject()
}
```

Or use the convenience base class:

```kotlin
class MyActivity : ScopeActivity() {

    // Scope is already set up
    private val presenter: ActivityPresenter by inject()
}
```

### Activity Retained Scope

Survives configuration changes (rotation, theme change):

```kotlin
class MyActivity : AppCompatActivity(), AndroidScopeComponent {

    // Backed by ViewModel lifecycle - survives rotation
    override val scope: Scope by activityRetainedScope()

    private val presenter: RetainedPresenter by inject()
}
```

Or use the convenience base class:

```kotlin
class MyActivity : RetainedScopeActivity() {

    private val presenter: RetainedPresenter by inject()
}
```

### Fragment Scope

Fragment scopes are automatically linked to parent Activity scope:

```kotlin
class MyFragment : Fragment(), AndroidScopeComponent {

    override val scope: Scope by fragmentScope()

    // From fragment scope
    private val presenter: FragmentPresenter by inject()

    // Can also access Activity scope dependencies
    private val activityPresenter: ActivityPresenter by inject()
}
```

Or use the convenience base class:

```kotlin
class MyFragment : ScopeFragment() {

    private val presenter: FragmentPresenter by inject()
}
```

## Type-Based vs Archetype Scopes

### Archetype Scope (Recommended)

Generic scope that works with any Activity/Fragment:

```kotlin
module {
    activityScope {
        scoped<MyPresenter>()
    }
}

// Works with any Activity
class ActivityA : ScopeActivity() {
    private val presenter: MyPresenter by inject()
}

class ActivityB : ScopeActivity() {
    private val presenter: MyPresenter by inject()
}
```

### Type-Based Scope

Scope tied to a specific class:

```kotlin
module {
    scope<MyActivity> {
        scoped<MyPresenter>()
    }
}

// Only works with MyActivity
class MyActivity : AppCompatActivity(), AndroidScopeComponent {
    override val scope: Scope by activityScope()
    private val presenter: MyPresenter by inject()
}
```

## ViewModel Scope

ViewModels cannot access Activity or Fragment scopes (to prevent memory leaks). Use ViewModel Scope for scoped dependencies:

```kotlin
module {
    viewModelScope {
        scoped<UserCache>()
        scoped<UserRepository>()
        viewModel<UserViewModel>()
    }
}
```

```kotlin
@ViewModelScope
class UserCache

@ViewModelScope
class UserRepository(private val cache: UserCache)

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

For detailed ViewModel scope usage, see [Scopes - ViewModel Scope](/docs/reference/koin-core/scopes#viewmodel-scope).

## Scope Lifecycle

### Handling Scope Close

Override `onCloseScope()` to run cleanup before scope is destroyed:

```kotlin
class MyActivity : AppCompatActivity(), AndroidScopeComponent {

    override val scope: Scope by activityScope()

    override fun onCloseScope() {
        // Called BEFORE scope.close()
        // Scope is still accessible here
    }
}
```

:::warning
Don't access scope in `onDestroy()` - it's already closed at that point.
:::

## Scope Links

Share instances between components with custom scopes:

```kotlin
module {
    scope(named("session")) {
        scoped<UserSession>()
    }
}
```

```kotlin
class MyActivity : ScopeActivity() {

    fun startSession() {
        val sessionScope = getKoin().createScope("session", named("session"))

        // Link to current scope
        scope.linkTo(sessionScope)

        // Now UserSession is accessible
        val session: UserSession = get()
    }
}
```

## Quick Reference

| Component | Delegate | Base Class |
|-----------|----------|------------|
| Activity | `by activityScope()` | `ScopeActivity` |
| Activity (retained) | `by activityRetainedScope()` | `RetainedScopeActivity` |
| Fragment | `by fragmentScope()` | `ScopeFragment` |

| Scope | Survives Rotation | Use Case |
|-------|-------------------|----------|
| `activityScope` | ❌ No | UI state, presenters |
| `activityRetainedScope` | ✅ Yes | Form state, pending requests |
| `fragmentScope` | ❌ No | Fragment-specific presenters |
| `viewModelScope` | ✅ Yes | ViewModel dependencies |

## Best Practices

1. **Use archetypes** - Prefer `activityScope { }` over `scope<MyActivity> { }` for reusability
2. **Retained for rotation** - Use `activityRetainedScope` for state that should survive rotation
3. **Don't leak** - Never inject Activity/Fragment into singletons
4. **Close custom scopes** - Always close manually created scopes
5. **Use onCloseScope** - For cleanup before scope destruction

## Next Steps

- **[Core Scopes](/docs/reference/koin-core/scopes)** - Scope fundamentals and ViewModel scope
- **[ViewModel](/docs/reference/koin-android/viewmodel)** - ViewModel injection
- **[Testing](/docs/reference/koin-test/testing)** - Testing scoped dependencies

---

### Reference Koin Android Start (`docs/reference/koin-android/start.md`)

---
title: Start Koin on Android
---

The `koin-android` project is dedicated to provide Koin powers to Android world. See the [Android setup](/docs/setup/koin#android) section for more details.

## From your Application class

From your `Application` class you can use the `startKoin` function and inject the Android context with `androidContext` as follows:

```kotlin
class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin {
            // Log Koin into Android logger
            androidLogger()
            // Reference Android context
            androidContext(this@MainApplication)
            // Load modules
            modules(myAppModules)
        }
    }
}
```

:::info
You can also start Koin from anywhere if you don't want to start it from your Application class.
:::

If you need to start Koin from another Android class, you can use the `startKoin` function and provide your Android `Context`
instance with just like:

```kotlin
startKoin {
    //inject Android context
    androidContext(/* your android context */)
    // ...
}
```

## Start Koin with Annotations

When using Koin Annotations, you can use `startKoin<T>()` to start Koin with your annotated module class:

```kotlin
@KoinApplication
class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin<MainApplication> {
            androidLogger()
            androidContext(this@MainApplication)
        }
    }
}
```

The `startKoin<T>()` function automatically loads the generated module from your `@Module` annotated class.

For multiple modules:

```kotlin
@Module
@Configuration
@ComponentScan("com.myapp.data")
class DataModule

@Module
@Configuration
@ComponentScan("com.myapp.domain")
class DomainModule

@KoinApplication
class MainApplication

// Start with multiple modules
startKoin<MainApplication> {
    androidLogger()
    androidContext(this@MainApplication)
}
```

## Extra Configurations

From your Koin configuration (in `startKoin { }` block code), you can also configure several parts of Koin.

### Koin Logging for Android

Within your `KoinApplication` instance, we have an extension `androidLogger` which uses the `AndroidLogger()` class.
This logger is an Android implementation of the Koin logger.

Up to you to change this logger if it doesn't suit to your needs.

```kotlin
startKoin {
    // use Android logger - Level.INFO by default
    androidLogger()
    // ...
}
```

### Loading Properties

You can use Koin properties in the `assets/koin.properties` file, to store keys/values:

```kotlin
startKoin {
    // ...
    // use properties from assets/koin.properties
    androidFileProperties()   
}
```

## Start Koin with AndroidX Startup (4.0.1)

[AndroidX Startup](https://developer.android.com/topic/libraries/app-startup) is a library that provides a straightforward way to initialize components at app startup. It uses a single ContentProvider to initialize all dependencies, avoiding the overhead of separate ContentProviders for each component that needs early initialization.

By using Gradle package `koin-androidx-startup`, we can use `KoinStartup` interface to declare your Koin configuration in your Application class:

```kotlin
class MainApplication : Application(), KoinStartup {

     override fun onKoinStartup() = koinConfiguration {
        androidContext(this@MainApplication)
        modules(appModule)
    }

    override fun onCreate() {
        super.onCreate()
    }
}
```

This replaces the `startKoin` function that is usally used in `onCreate`. The `koinConfiguration` function is returning a `KoinConfiguration` instance.

:::info
`KoinStartup` integrates with AndroidX App Startup to initialize Koin via ContentProvider before `Application.onCreate()`. This is useful when you need to manage initialization order with other Initializers that depend on Koin being available.
:::

:::warning
`KoinStartup` runs on the main thread during app startup. If you're not using AndroidX App Startup library to manage other Initializers, there is **no benefit** to using `KoinStartup` - use the standard `startKoin` approach instead. For dispatching module loading to background threads, see [Lazy Modules](/docs/reference/koin-core/lazy-modules).
:::

If you have other Initializers that need Koin, make them depend on `KoinInitializer`:

```kotlin
class CrashTrackerInitializer : Initializer<Unit>, KoinComponent {
    private val crashTrackerService: CrashTrackerService by inject()

    override fun create(context: Context) {
        crashTrackerService.configure(context)
    }

    override fun dependencies(): List<Class<out Initializer<*>>> {
        return listOf(KoinInitializer::class.java)
    }
}
```

## Next Steps

- **[JSR-330 Compatibility](/docs/reference/koin-android/jsr330)** - Use standard `@Inject`, `@Singleton` annotations
- **[Injecting in Android](/docs/reference/koin-android/get-instances)** - Get instances in Activity, Fragment, Service
- **[Android ViewModel](/docs/reference/koin-android/viewmodel)** - ViewModel injection and scoping
- **[Hilt Migration](/docs/reference/koin-android/hilt-migration)** - Migrate from Hilt to Koin

---

### Reference Koin Android Viewmodel (`docs/reference/koin-android/viewmodel.md`)

---
title: Android ViewModel
---

This page covers Android-specific ViewModel features. For core ViewModel DSL and multiplatform support, see [ViewModel](/docs/reference/koin-core/viewmodel).

## Overview

[ViewModels](https://developer.android.com/topic/libraries/architecture/viewmodel) are architecture components designed to survive configuration changes and manage UI-related data. Koin provides special support for ViewModels with lifecycle-aware injection.

### Key Concepts

- **Survives Configuration Changes** - ViewModels persist through rotations and theme changes
- **Scoped to Lifecycle** - Tied to Activity, Fragment, or Navigation Graph lifecycle
- **Lazy Creation** - Created only when first accessed
- **Shared Instances** - Can be shared between Fragments and their host Activity

:::info
**Multiplatform ViewModel** - Koin ViewModel DSL is fully multiplatform via `koin-core-viewmodel`. For Compose Multiplatform, see [Compose ViewModel](/docs/reference/koin-compose/compose#viewmodel-for-composable).
:::

### ViewModel Scope Limitations

:::warning
**Important:** ViewModels are created against the root Koin scope and **cannot access** Activity or Fragment scoped dependencies. This prevents memory leaks as ViewModels outlive Activities and Fragments.

**Need scoped dependencies in ViewModel?** Use [ViewModel Scope](/docs/reference/koin-core/scopes#viewmodel-scope) to create a dedicated scope tied to your ViewModel's lifecycle.
:::

## Declaring ViewModels

### Compiler Plugin DSL

```kotlin
val appModule = module {
    viewModel<DetailViewModel>()
    viewModel<UserViewModel>()
}
```

### Annotations

```kotlin
@KoinViewModel
class DetailViewModel(
    private val repository: DetailRepository
) : ViewModel()

@KoinViewModel
class UserViewModel(
    private val userRepository: UserRepository
) : ViewModel()
```

### Classic DSL

```kotlin
val appModule = module {
    // With constructor reference
    viewModelOf(::DetailViewModel)

    // With lambda
    viewModel { DetailViewModel(get()) }
}
```

## Injecting ViewModels

In `Activity`, `Fragment` or `Service`, use:

* `by viewModel()` - lazy delegate property
* `getViewModel()` - eager fetch

```kotlin
class DetailActivity : AppCompatActivity() {

    // Lazy inject ViewModel
    private val viewModel: DetailViewModel by viewModel()

    // Or eager
    // private val viewModel: DetailViewModel = getViewModel()
}
```

## Shared ViewModel (Activity)

Share a ViewModel between Fragments and their host Activity:

* `by activityViewModel()` - lazy delegate for shared ViewModel
* `getActivityViewModel()` - eager fetch

```kotlin
class WeatherActivity : AppCompatActivity() {
    private val weatherViewModel: WeatherViewModel by viewModel()
}

class WeatherHeaderFragment : Fragment() {
    // Shared with Activity
    private val weatherViewModel: WeatherViewModel by activityViewModel()
}

class WeatherListFragment : Fragment() {
    // Same instance as WeatherHeaderFragment
    private val weatherViewModel: WeatherViewModel by activityViewModel()
}
```

## Passing Parameters

### Compiler Plugin DSL

```kotlin
class DetailViewModel(
    @InjectedParam val itemId: String,
    private val repository: DetailRepository
) : ViewModel()

val appModule = module {
    viewModel<DetailViewModel>()
}
```

### Annotations

```kotlin
@KoinViewModel
class DetailViewModel(
    @InjectedParam val itemId: String,
    private val repository: DetailRepository
) : ViewModel()
```

### Classic DSL

```kotlin
val appModule = module {
    viewModel { params ->
        DetailViewModel(
            itemId = params.get(),
            repository = get()
        )
    }
}
```

### Injection Site

```kotlin
class DetailActivity : AppCompatActivity() {

    private val itemId: String by lazy { intent.getStringExtra("ITEM_ID")!! }

    // Pass parameter at injection
    private val viewModel: DetailViewModel by viewModel { parametersOf(itemId) }
}
```

## SavedStateHandle

Add `SavedStateHandle` to your ViewModel constructor - Koin injects it automatically:

### Annotations

```kotlin
@KoinViewModel
class MyStateViewModel(
    private val handle: SavedStateHandle,
    private val repository: MyRepository
) : ViewModel()
```

### DSL

```kotlin
class MyStateViewModel(
    private val handle: SavedStateHandle,
    private val repository: MyRepository
) : ViewModel()

val appModule = module {
    viewModel<MyStateViewModel>()  // Compiler Plugin DSL
    // or
    viewModelOf(::MyStateViewModel)  // Classic DSL
}
```

### Usage

```kotlin
class DetailActivity : AppCompatActivity() {
    // SavedStateHandle automatically injected
    private val viewModel: MyStateViewModel by viewModel()
}
```

:::info
All `stateViewModel` functions are deprecated. Use the regular `viewModel` function - `SavedStateHandle` is injected automatically.
:::

## Navigation Graph ViewModel

Scope a ViewModel to a Navigation graph:

```kotlin
class NavFragment : Fragment() {

    // Scoped to navigation graph
    private val navViewModel: NavViewModel by koinNavGraphViewModel(R.id.my_graph)
}
```

The ViewModel is:
- Created when first fragment in graph accesses it
- Shared across all fragments in the graph
- Destroyed when navigation graph is popped

## ViewModel with Scoped Dependencies

If your ViewModel needs its own scoped dependencies, use [ViewModel Scope](/docs/reference/koin-core/scopes#viewmodel-scope):

```kotlin
val appModule = module {
    viewModelScope {
        scoped<UserCache>()
        scoped<UserRepository>()
        viewModel<UserViewModel>()
    }
}
```

```kotlin
@ViewModelScope
class UserCache

@ViewModelScope
class UserRepository(private val cache: UserCache)

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

## ViewModel Generic API

For advanced use cases, Koin provides lower-level APIs:

```kotlin
// From ComponentActivity or Fragment
val viewModel = viewModelForClass(
    clazz = MyViewModel::class,
    qualifier = null,
    owner = this,
    key = null,
    parameters = { parametersOf("param") }
)
```

## Java Compatibility

Add the compat dependency:

```groovy
implementation "io.insert-koin:koin-android-compat:$koin_version"
```

Use `ViewModelCompat` static methods:

```java
MyViewModel viewModel = ViewModelCompat.getViewModel(this, MyViewModel.class);
```

## Quick Reference

| Action | Code |
|--------|------|
| Declare ViewModel | `viewModel<MyVM>()` / `@KoinViewModel` |
| Inject in Activity/Fragment | `by viewModel()` |
| Share with Activity | `by activityViewModel()` |
| Pass parameters | `by viewModel { parametersOf(id) }` |
| Navigation graph scope | `by koinNavGraphViewModel(R.id.graph)` |
| With SavedStateHandle | Just add to constructor |

## Next Steps

- **[Core ViewModel](/docs/reference/koin-core/viewmodel)** - Multiplatform ViewModel DSL
- **[Scopes](/docs/reference/koin-core/scopes#viewmodel-scope)** - ViewModel Scope for scoped dependencies
- **[Testing](/docs/reference/koin-test/testing)** - Testing ViewModels
- **[Compose](/docs/reference/koin-compose/compose)** - ViewModels in Compose

---

### Reference Koin Android Workmanager (`docs/reference/koin-android/workmanager.md`)

---
title: WorkManager
---

Koin integrates with [Android WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) to enable constructor injection in Workers.

## Setup

### Add Dependencies

```groovy
implementation "io.insert-koin:koin-android:$koin_version"
implementation "io.insert-koin:koin-androidx-workmanager:$koin_version"
```

### Configure WorkManager

Set up the Koin WorkManager factory in your Application:

```kotlin
class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidContext(this@MainApplication)
            workManagerFactory()
            modules(appModule)
        }
    }
}
```

### Disable Default Initializer

Add to your `AndroidManifest.xml` to disable the default WorkManager initializer:

```xml
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="androidx.work.WorkManagerInitializer"
        android:value="androidx.startup"
        tools:node="remove" />
</provider>
```

## Declaring Workers

### Compiler Plugin DSL

```kotlin
class MyWorker(
    context: Context,
    workerParams: WorkerParameters,
    private val myService: MyService
) : CoroutineWorker(context, workerParams) {

    override suspend fun doWork(): Result {
        myService.performTask()
        return Result.success()
    }
}

val appModule = module {
    single<MyService>()
    worker<MyWorker>()
}
```

### Annotations

```kotlin
@KoinWorker
class MyWorker(
    context: Context,
    workerParams: WorkerParameters,
    private val myService: MyService
) : CoroutineWorker(context, workerParams) {

    override suspend fun doWork(): Result {
        myService.performTask()
        return Result.success()
    }
}

@Singleton
class MyService
```

### Classic DSL

```kotlin
val appModule = module {
    single { MyService() }
    worker { params ->
        MyWorker(
            context = params.get(),
            workerParams = params.get(),
            myService = get()
        )
    }
}
```

## Enqueuing Work

Use WorkManager normally to enqueue your worker:

```kotlin
val workRequest = OneTimeWorkRequestBuilder<MyWorker>().build()
WorkManager.getInstance(context).enqueue(workRequest)
```

## Worker with Parameters

Pass parameters via WorkManager's input data:

```kotlin
@KoinWorker
class SyncWorker(
    context: Context,
    workerParams: WorkerParameters,
    private val repository: DataRepository
) : CoroutineWorker(context, workerParams) {

    override suspend fun doWork(): Result {
        val userId = inputData.getString("USER_ID") ?: return Result.failure()
        repository.syncUser(userId)
        return Result.success()
    }
}
```

Enqueue with data:

```kotlin
val workRequest = OneTimeWorkRequestBuilder<SyncWorker>()
    .setInputData(workDataOf("USER_ID" to "123"))
    .build()

WorkManager.getInstance(context).enqueue(workRequest)
```

## Quick Reference

| Approach | Declaration |
|----------|-------------|
| Compiler Plugin DSL | `worker<MyWorker>()` |
| Annotations | `@KoinWorker` |
| Classic DSL | `worker { params -> MyWorker(params.get(), params.get(), get()) }` |

| Setup | Code |
|-------|------|
| Enable factory | `workManagerFactory()` in startKoin |
| Disable default | Remove `WorkManagerInitializer` in manifest |

## Next Steps

- **[Android WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)** - Official WorkManager documentation
- **[Scopes](/docs/reference/koin-android/scope)** - Android scopes
- **[ViewModel](/docs/reference/koin-android/viewmodel)** - ViewModel injection

---

### Reference Koin Annotations Annotations Inventory (`docs/reference/koin-annotations/annotations-inventory.md`)

# Koin Annotations Inventory

This document provides a comprehensive inventory of all Koin annotations, their parameters, behaviors, and usage examples.

## Table of Contents

- [Definition Annotations](#definition-annotations)
  - [@Single](#single)
  - [@Factory](#factory)
  - [@Scoped](#scoped)
- [Scope Annotations](#scope-annotations)
  - [@Scope](#scope)
  - [@ViewModelScope](#viewmodelscope)
  - [@ActivityScope](#activityscope)
  - [@ActivityRetainedScope](#activityretainedscope)
  - [@FragmentScope](#fragmentscope)
  - [@ScopeId](#scopeid)
- [ViewModel & Android-Specific Annotations](#viewmodel--android-specific-annotations)
  - [@KoinViewModel](#koinviewmodel)
  - [@KoinWorker](#koinworker)
- [Qualifier Annotations](#qualifier-annotations)
  - [@Named](#named)
  - [@Qualifier](#qualifier)
- [Parameter Annotations](#parameter-annotations)
  - [@InjectedParam](#injectedparam)
  - [@Property](#property)
  - [@PropertyValue](#propertyvalue)
- [Safety Annotations](#safety-annotations)
  - [@Provided](#provided)
- [Module & Application Annotations](#module--application-annotations)
  - [@Module](#module)
  - [@ComponentScan](#componentscan)
  - [@Configuration](#configuration)
  - [@KoinApplication](#koinapplication)
- [Monitoring Annotations](#monitoring-annotations)
  - [@Monitor](#monitor)
- [Meta Annotations (Internal)](#meta-annotations-internal)
  - [@ExternalDefinition](#externaldefinition)
  - [@MetaDefinition](#metadefinition)
  - [@MetaModule](#metamodule)
  - [@MetaApplication](#metaapplication)

---

## Definition Annotations

### @Single / @Singleton

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`, `FUNCTION`

**Description:** Declares a type or function as a `single` (singleton) definition in Koin. A single instance is created and shared across the application. `@Singleton` is the preferred name (standard naming convention); `@Single` is an alias.

**Parameters:**
- `binds: Array<KClass<*>> = [Unit::class]` - Explicit types to bind to this definition. Supertypes are automatically detected.
- `createdAtStart: Boolean = false` - If `true`, the instance is created when Koin starts.

**Behavior:**
All dependencies are filled by constructor injection.

**Example:**
```kotlin
@Single
class MyClass(val d : MyDependency)
```

**Generated Koin DSL:**
```kotlin
single { MyClass(get()) }
```

**With explicit binding:**
```kotlin
@Single(binds = [MyInterface::class])
class MyClass(val d : MyDependency) : MyInterface
```

**With creation at start:**
```kotlin
@Single(createdAtStart = true)
class MyClass(val d : MyDependency)
```

---

### @Factory

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`, `FUNCTION`

**Description:** Declares a type or function as a `factory` definition in Koin. A new instance is created each time it is requested.

**Parameters:**
- `binds: Array<KClass<*>> = [Unit::class]` - Explicit types to bind to this definition. Supertypes are automatically detected.

**Behavior:**
All dependencies are filled by constructor injection. Each request creates a new instance.

**Example:**
```kotlin
@Factory
class MyClass(val d : MyDependency)
```

**Generated Koin DSL:**
```kotlin
factory { MyClass(get()) }
```

---

### @Scoped

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`, `FUNCTION`

**Description:** Declares a type or function as a `scoped` definition in Koin. Must be associated with `@Scope` annotation. Instance is shared within a specific scope.

**Parameters:**
- `binds: Array<KClass<*>> = [Unit::class]` - Explicit types to bind to this definition. Supertypes are automatically detected.

**Behavior:**
Creates a scoped instance that lives within the defined scope's lifetime.

**Example:**
```kotlin
@Scope(MyScope::class)
@Scoped
class MyClass(val d : MyDependency)
```

**See Also:** [@Scope](#scope)

---

## Scope Annotations

### @Scope

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`, `FUNCTION`

**Description:** Declares a class in a Koin scope. Scope name is described by either value (class) or name (string). By default, declares a `scoped` definition. Can be overridden with `@Scoped`, `@Factory`, `@KoinViewModel` annotations for explicit bindings.

**Parameters:**
- `value: KClass<*> = Unit::class` - Scope class value
- `name: String = ""` - Scope string value

**Behavior:**
Creates a scope definition associated with the specified scope type or name.

**Example with class:**
```kotlin
@Scope(MyScope::class)
class MyClass(val d : MyDependency)
```

**Generated Koin DSL:**
```kotlin
scope<MyScope> {
    scoped { MyClass(get()) }
}
```

**Example with string name:**
```kotlin
@Scope(name = "my_custom_scope")
class MyClass(val d : MyDependency)
```

---

### @ViewModelScope

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`, `FUNCTION`

**Description:** Declares a class in a ViewModelScope Koin scope. This is a scope archetype for components that should live within a ViewModel's lifecycle.

**Parameters:** None

**Behavior:**
Creates a scoped definition within the `viewModelScope`.

**Example:**
```kotlin
@ViewModelScope
class MyClass(val d : MyDependency)
```

**Generated Koin DSL:**
```kotlin
viewModelScope {
    scoped { MyClass(get()) }
}
```

**Usage:**
The tagged class is meant to be used with ViewModel and `viewModelScope` function to activate the scope.

---

### @ActivityScope

**Package:** `org.koin.android.annotation`

**Target:** `CLASS`, `FUNCTION`

**Description:** Declares a class in an Activity Koin Scope.

**Parameters:** None

**Behavior:**
Creates a scoped definition within the `activityScope`.

**Example:**
```kotlin
@ActivityScope
class MyClass(val d : MyDependency)
```

**Generated Koin DSL:**
```kotlin
activityScope {
    scoped { MyClass(get()) }
}
```

**Usage:**
The tagged class is meant to be used with Activity and `activityScope` function to activate the scope.

---

### @ActivityRetainedScope

**Package:** `org.koin.android.annotation`

**Target:** `CLASS`, `FUNCTION`

**Description:** Declares a class in an Activity Koin scope, but retained across configuration changes.

**Parameters:** None

**Behavior:**
Creates a scoped definition within the `activityRetainedScope`.

**Example:**
```kotlin
@ActivityRetainedScope
class MyClass(val d : MyDependency)
```

**Generated Koin DSL:**
```kotlin
activityRetainedScope {
    scoped { MyClass(get()) }
}
```

**Usage:**
The tagged class is meant to be used with Activity and `activityRetainedScope` function to activate the scope.

---

### @FragmentScope

**Package:** `org.koin.android.annotation`

**Target:** `CLASS`, `FUNCTION`

**Description:** Declares a class in a Fragment Koin scope.

**Parameters:** None

**Behavior:**
Creates a scoped definition within the `fragmentScope`.

**Example:**
```kotlin
@FragmentScope
class MyClass(val d : MyDependency)
```

**Generated Koin DSL:**
```kotlin
fragmentScope {
    scoped { MyClass(get()) }
}
```

**Usage:**
The tagged class is meant to be used with Fragment and `fragmentScope` function to activate the scope.

---

### @ScopeId

**Package:** `org.koin.core.annotation`

**Target:** `VALUE_PARAMETER`

**Description:** Annotates a parameter from class constructor or function to request resolution for a given scope with Scope ID.

**Parameters:**
- `value: KClass<*> = Unit::class` - Scope type
- `name: String = ""` - Scope string identifier

**Behavior:**
Resolves the dependency from a specific scope identified by type or name.

**Example with string name:**
```kotlin
@Factory
class MyClass(@ScopeId(name = "my_scope_id") val d : MyDependency)
```

**Generated Koin DSL:**
```kotlin
factory { MyClass(getScope("my_scope_id").get()) }
```

**Example with type:**
```kotlin
@Factory
class MyClass(@ScopeId(MyScope::class) val d : MyDependency)
```

---

## ViewModel & Android-Specific Annotations

### @KoinViewModel

**Package:** `org.koin.android.annotation`

**Target:** `CLASS`, `FUNCTION`

**Description:** ViewModel annotation for Koin definition. Declares a type or function as a `viewModel` definition in Koin.

**Platform Support:**
- ✅ Android
- ✅ Kotlin Multiplatform (KMP)
- ✅ Compose Multiplatform (CMP)

**Parameters:**
- `binds: Array<KClass<*>> = []` - Explicit types to bind to this definition. Supertypes are automatically detected.

**Behavior:**
All dependencies are filled by constructor injection. Creates a ViewModel instance managed by Koin. Works across all platforms including Android, iOS, Desktop, and Web when using Compose Multiplatform.

**Example (Android/CMP):**
```kotlin
@KoinViewModel
class MyViewModel(val d : MyDependency) : ViewModel()
```

**Example (KMP/CMP shared):**
```kotlin
@KoinViewModel
class SharedViewModel(
    val repository: Repository,
    val analytics: Analytics
) : ViewModel()
```

**Generated Koin DSL:**
```kotlin
viewModel { MyViewModel(get()) }
```

---

### @KoinWorker

**Package:** `org.koin.android.annotation`

**Target:** `CLASS`, `FUNCTION`

**Description:** Worker annotation for Koin Definition. Declares a type as a `worker` definition for WorkManager workers.

**Parameters:**
- `binds: Array<KClass<*>> = []` - Explicit types to bind to this definition.

**Behavior:**
Creates a worker definition for Android WorkManager integration.

**Example:**
```kotlin
@KoinWorker
class MyWorker() : Worker()
```

---

## Qualifier Annotations

### @Named

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`, `FUNCTION`, `VALUE_PARAMETER`

**Description:** Defines a qualifier for a given definition. Generates `StringQualifier("...")` or type-based qualifier.

**Parameters:**
- `value: String = ""` - String qualifier
- `type: KClass<*> = Unit::class` - Class qualifier

**Behavior:**
Used to distinguish between multiple definitions of the same type.

**Example with string:**
```kotlin
@Single
@Named("special")
class MyClass(val d : MyDependency)
```

**Usage in parameter:**
```kotlin
@Single
class Consumer(@Named("special") val myClass: MyClass)
```

**Example with type:**
```kotlin
@Single
@Named(type = MyType::class)
class MyClass(val d : MyDependency)
```

---

### @Qualifier

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`, `FUNCTION`, `VALUE_PARAMETER`

**Description:** Defines a qualifier for a given definition. Similar to `@Named` but with reversed parameter priority.

**Parameters:**
- `value: KClass<*> = Unit::class` - Class qualifier
- `name: String = ""` - String qualifier

**Behavior:**
Used to distinguish between multiple definitions of the same type.

**Example:**
```kotlin
@Single
@Qualifier(name = "special")
class MyClass(val d : MyDependency)
```

---

## Parameter Annotations

### @InjectedParam

**Package:** `org.koin.core.annotation`

**Target:** `VALUE_PARAMETER`

**Description:** Marks a constructor or function parameter to be resolved from `ParametersHolder` (passed via `parametersOf()` at call site) instead of the DI container.

**Parameters:** None

**Behavior:**
Resolves the parameter value from `ParametersHolder.get()` at runtime, not from Koin definitions. Compile safety validation skips `@InjectedParam` parameters.

**Example:**
```kotlin
@Factory
class MyClass(@InjectedParam val id: Int, val service: Service)
```

**Generated Koin DSL:**
```kotlin
factory { params -> MyClass(params.get(), get()) }
```

**Usage:**
```kotlin
val instance = koin.get<MyClass> { parametersOf(42) }
```

---

### @Property

**Package:** `org.koin.core.annotation`

**Target:** `VALUE_PARAMETER`

**Description:** Annotates a constructor parameter or function parameter to resolve as a Koin property.

**Parameters:**
- `value: String` - Property name

**Behavior:**
Resolves the parameter value from Koin properties instead of dependency injection.

**Example:**
```kotlin
@Factory
class MyClass(@Property("name") val name : String)
```

**Generated Koin DSL:**
```kotlin
factory { MyClass(getProperty("name")) }
```

**With default value:**
```kotlin
@PropertyValue("name")
val defaultName = "MyName"

@Factory
class MyClass(@Property("name") val name : String)
```

**Generated Koin DSL:**
```kotlin
factory { MyClass(getProperty("name", defaultName)) }
```

---

### @PropertyValue

**Package:** `org.koin.core.annotation`

**Target:** `FIELD`

**Description:** Annotates a field value that will be a Property default value.

**Parameters:**
- `value: String` - Property name

**Behavior:**
Defines a default value for a property that can be used when the property is not found.

**Example:**
```kotlin
@PropertyValue("name")
val defaultName = "MyName"

@Factory
class MyClass(@Property("name") val name : String)
```

**Generated Koin DSL:**
```kotlin
factory { MyClass(getProperty("name", defaultName)) }
```

---

## Safety Annotations

### @Provided

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`, `VALUE_PARAMETER`

**Description:** Marks a type or parameter as externally provided at runtime (e.g., Android framework types, third-party SDKs). Compile-time safety validation skips these types.

**Parameters:** None

**Behavior:**
- On a **class**: all usages of that type skip compile safety validation
- On a **parameter**: only that specific parameter skips validation
- The type is still resolved via `scope.get<T>()` at runtime — `@Provided` only affects compile-time checks

**Example on class:**
```kotlin
@Provided
class FirebaseAnalytics  // all usages skip validation

@Singleton
class AnalyticsService(val analytics: FirebaseAnalytics)
// No compile error — FirebaseAnalytics is @Provided
```

**Example on parameter:**
```kotlin
@Factory
class PaymentProcessor(@Provided val gateway: PaymentGateway)
// No compile error — only this parameter skips validation
```

**Note:** Common Android framework types (`Context`, `Application`, `Activity`, `Fragment`, `SavedStateHandle`, `WorkerParameters`) are automatically whitelisted and don't need `@Provided`.

**See Also:** [Compile-Time Safety](/docs/reference/koin-compiler/compile-safety#external-types-provided)

---

## Module & Application Annotations

### @Module

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`

**Description:** Class annotation to help gather definitions inside a Koin module. Each function can be annotated with a Koin definition annotation.

**Parameters:**
- `includes: Array<KClass<*>> = []` - Module classes to include
- `createdAtStart: Boolean = false` - If `true`, module instances are created at start

**Behavior:**
Gathers all annotated functions and classes within the module.

**Example:**
```kotlin
@Module
class MyModule {
    @Single
    fun myClass(d : MyDependency) = MyClass(d)
}
```

**Generated Koin DSL:**
```kotlin
val MyModule.module = module {
    val moduleInstance = MyModule()
    single { moduleInstance.myClass(get()) }
}
```

**With includes:**
```kotlin
@Module(includes = [OtherModule::class])
class MyModule {
    // definitions
}
```

---

### @ComponentScan

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`, `FIELD`

**Description:** Gathers definitions declared with Koin definition annotations. Scans current package or explicit package names.

**Parameters:**
- `value: vararg String = []` - Packages to scan (supports glob patterns)

**Behavior:**
Scans specified packages for annotated classes. Supports both exact package names and glob patterns.

**Glob Pattern Support:**

1. **Exact package names (no wildcards):**
   - `com.example.service` - Scans package and all subpackages (equivalent to `com.example**`)

2. **Multi-level scan including root:**
   - `com.example**` - Scans `com.example` and all subpackages

3. **Multi-level scan excluding root:**
   - `com.example.**` - Scans only subpackages of `com.example`, excludes root

4. **Single-level wildcard:**
   - `com.example.*.service` - Matches exactly one level (e.g., `com.example.user.service`)

5. **Combined wildcards:**
   - `com.**.service.*data` - Complex pattern matching
   - `com.*.service.**` - Scans subpackages under pattern

**Example - scan current package:**
```kotlin
@ComponentScan
class MyApp
```

**Example - scan specific packages:**
```kotlin
@ComponentScan("com.example.services", "com.example.repositories")
class MyApp
```

**Example - with glob patterns:**
```kotlin
@ComponentScan("com.example.**", "org.app.*.services")
class MyApp
```

---

### @Configuration

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`, `FIELD`

**Description:** Applied to `@Module` class to associate it with one or more configurations (tags/flavors).

**Parameters:**
- `value: vararg String = []` - Configuration names

**Behavior:**
Modules can be grouped into configurations for conditional loading.

**Default Configuration:**
```kotlin
@Module
@Configuration
class MyModule
```
This module is part of the "default" configuration.

**Multiple Configurations:**
```kotlin
@Module
@Configuration("prod", "test")
class MyModule
```
This module is available in both "prod" and "test" configurations.

**With Default:**
```kotlin
@Module
@Configuration("default", "test")
class MyModule
```
Available in default and test configurations.

**Note:** `@Configuration("default")` is equivalent to `@Configuration`

---

### @KoinApplication

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`

**Description:** Tags a class as a Koin application entry point. Generates Koin application bootstrap with `startKoin()` or `koinApplication()` functions.

**Parameters:**
- `configurations: Array<String> = []` - List of configuration names to scan
- `modules: Array<KClass<*>> = [Unit::class]` - List of modules to load besides configurations

**Behavior:**
Generates bootstrap functions that scan for configurations and included modules.

**Example - default configuration:**
```kotlin
@KoinApplication
class MyApp
```

**Generated functions:**
```kotlin
MyApp.startKoin()
MyApp.koinApplication()
```

**Example - specific configurations:**
```kotlin
@KoinApplication(configurations = ["default", "prod"])
class MyApp
```

**Example - with modules:**
```kotlin
@KoinApplication(
    configurations = ["default"],
    modules = [CoreModule::class, ApiModule::class]
)
class MyApp
```

**Usage with custom configuration:**
```kotlin
MyApp.startKoin {
    printLogger()
    // additional configuration
}
```

---

## Monitoring Annotations

### @Monitor

**Package:** `org.koin.core.annotation`

**Target:** `CLASS`, `FUNCTION`

**Description:** Marks a class or function for automatic monitoring and performance tracing through Kotzilla Platform, the official tooling platform for Koin.

**Parameters:** None

**Behavior:**
- When applied to a class: Generates a Koin proxy that monitors all public method calls
- When applied to a function: Monitors that specific method within a Koin-managed component
- Automatically captures performance metrics, error rates, and usage patterns
- Sends data to Kotzilla workspace for real-time analysis

**Requirements:**
- `implementation 'io.kotzilla:kotzilla-core:latest.version'`
- Valid Kotzilla Platform account and API key

**Example:**
```kotlin
@Monitor
class UserService(private val userRepository: UserRepository) {
    fun findUser(id: String): User? = userRepository.findById(id)
}
```

**Resources:**
- [Kotzilla Platform](https://kotzilla.io)
- [Complete Documentation](https://doc.kotzilla.io)
- [Latest Version](https://doc.kotzilla.io/docs/releaseNotes/changelogSDK)

**Since:** Kotzilla 1.2.1

---

## Meta Annotations (Internal)

These annotations are for internal use only by the Koin compiler and code generation.

### @ExternalDefinition

**Package:** `org.koin.meta.annotations`

**Target:** `CLASS`, `FIELD`, `FUNCTION`

**Description:** Internal usage for components discovery in generated package.

**Parameters:**
- `value: String = ""` - Package of declared definition

---

### @MetaDefinition

**Package:** `org.koin.meta.annotations`

**Target:** `CLASS`, `FUNCTION`, `PROPERTY`

**Description:** Meta Definition annotation to help represent definition metadata.

**Parameters:**
- `value: String = ""` - Definition full path
- `moduleTagId: String = ""` - Module Tag + ID (format: "module_id:module_tag")
- `dependencies: Array<String> = []` - Parameters tags to check
- `binds: Array<String> = []` - Bound types
- `qualifier: String = ""` - Qualifier
- `scope: String = ""` - Scope where it's declared

---

### @MetaModule

**Package:** `org.koin.meta.annotations`

**Target:** `CLASS`

**Description:** Meta Module annotation to help represent module metadata.

**Parameters:**
- `value: String = ""` - Module full path
- `id: String = ""` - Module ID
- `includes: Array<String> = []` - Includes Module Tags to check
- `configurations: Array<String> = []` - Module Configurations to check
- `isObject: Boolean = false` - Whether the module is an object

---

### @MetaApplication

**Package:** `org.koin.meta.annotations`

**Target:** `CLASS`

**Description:** Meta Application annotation to help represent application metadata.

**Parameters:**
- `value: String = ""` - Application full path
- `includes: Array<String> = []` - Used Module Tags to check
- `configurations: Array<String> = []` - Used Configurations modules to check

---

## Summary Table

| Annotation | Package | Purpose | Common Use Case |
|------------|---------|---------|-----------------|
| `@Singleton` / `@Single` | `org.koin.core.annotation` | Singleton definition | Shared application services |
| `@Factory` | `org.koin.core.annotation` | Factory definition | Per-request instances |
| `@Scoped` | `org.koin.core.annotation` | Scoped definition | Scope-specific instances |
| `@Scope` | `org.koin.core.annotation` | Scope declaration | Custom scopes |
| `@ViewModelScope` | `org.koin.core.annotation` | ViewModel scope | ViewModel-scoped dependencies |
| `@ActivityScope` | `org.koin.android.annotation` | Activity scope | Activity-scoped dependencies |
| `@ActivityRetainedScope` | `org.koin.android.annotation` | Retained activity scope | Config-change surviving deps |
| `@FragmentScope` | `org.koin.android.annotation` | Fragment scope | Fragment-scoped dependencies |
| `@ScopeId` | `org.koin.core.annotation` | Scope resolution | Resolve from specific scope |
| `@KoinViewModel` | `org.koin.android.annotation` | ViewModel definition | Android/KMP/CMP ViewModels |
| `@KoinWorker` | `org.koin.android.annotation` | Worker definition | WorkManager workers |
| `@Named` | `org.koin.core.annotation` | String/type qualifier | Distinguish same-type beans |
| `@Qualifier` | `org.koin.core.annotation` | Type/string qualifier | Distinguish same-type beans |
| `@InjectedParam` | `org.koin.core.annotation` | Runtime parameter | `parametersOf()` values |
| `@Property` | `org.koin.core.annotation` | Property injection | Configuration values |
| `@PropertyValue` | `org.koin.core.annotation` | Property default | Default config values |
| `@Provided` | `org.koin.core.annotation` | Skip safety validation | External/framework types |
| `@Module` | `org.koin.core.annotation` | Module declaration | Group definitions |
| `@ComponentScan` | `org.koin.core.annotation` | Package scanning | Auto-discover definitions |
| `@Configuration` | `org.koin.core.annotation` | Module configuration | Build variants/flavors |
| `@KoinApplication` | `org.koin.core.annotation` | App entry point | Bootstrap Koin |
| `@Monitor` | `org.koin.core.annotation` | Performance monitoring | Production monitoring |

---

**Document Version:** 1.0
**Last Updated:** 20-10-2025
**Koin Annotations Version:** 2.2.x+

---

### Reference Koin Annotations Definitions (`docs/reference/koin-annotations/definitions.md`)

---
title: Definitions with Annotations
---


Koin Annotations allow declaring the same kind of definitions as the regular Koin DSL, but with annotations. Just tag your class with the needed annotation, and it will generate everything for you!

For example, the equivalent of `single { MyComponent(get()) }` DSL declaration is just done by tagging with `@Single` like this:

```kotlin
@Single
class MyComponent(val myDependency : MyDependency)
```

Koin Annotations keep the same semantics as the Koin DSL. You can declare your components with the following definitions:

- `@Single` - singleton instance (declared with `single { }` in DSL)
- `@Factory` - factory instance. For instance recreated each time you need an instance. (declared with `factory { }` in DSL)
- `@KoinViewModel` - Android ViewModel instance (declared with `viewModel { }` in DSL)
- `@KoinWorker` - Android Worker Workmanager instance (declared with `worker { }` in DSL)

For Scopes, check the [Declaring Scopes](/docs/reference/koin-core/scopes) section.

## Annotated Top-Level Functions

Annotations work on **top-level functions**, not just classes. This is useful for providing instances from external libraries or builder patterns. Top-level functions are discovered by `@ComponentScan` like classes:

```kotlin
import org.koin.core.annotation.Singleton
import org.koin.core.annotation.Factory
import org.koin.core.annotation.Named

// Provide a Room database instance
@Singleton
fun provideDatabase(context: Context): AppDatabase =
    Room.databaseBuilder(context, AppDatabase::class.java, "my-db").build()

// Provide a JSON serializer
@Singleton
fun provideJson(): Json = Json { ignoreUnknownKeys = true }

// Provide an HTTP client
@Singleton
@Named("api")
fun provideHttpClient(json: Json): HttpClient = HttpClient { install(ContentNegotiation) { json(json) } }
```

Parameters are auto-resolved from the DI container. Qualifiers (`@Named`, custom `@Qualifier`) work on both the function and its parameters.

## Module Functions (Provider Functions)

Inside a `@Module` class, functions annotated with `@Singleton`, `@Factory`, etc. act as provider functions — similar to `@Provides` in Dagger/Hilt:

```kotlin
import org.koin.core.annotation.Module
import org.koin.core.annotation.Singleton

@Module
internal object DatabaseModule {

    @Singleton
    fun providesDatabase(context: Context): AppDatabase =
        Room.databaseBuilder(context, AppDatabase::class.java, "my-db").build()
}

@Module(includes = [DatabaseModule::class])
class DaosModule {

    @Singleton
    fun providesTopicDao(database: AppDatabase): TopicDao = database.topicDao()

    @Singleton
    fun providesNewsDao(database: AppDatabase): NewsResourceDao = database.newsResourceDao()
}
```

This is the pattern for wrapping external libraries (Room, Retrofit, OkHttp, etc.) that you don't own and can't annotate directly.

## Custom Qualifier Annotations

Beyond `@Named`, you can create custom qualifier annotations with parameters using `@Qualifier`:

```kotlin
import org.koin.core.annotation.Qualifier

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class Dispatcher(val niaDispatcher: NiaDispatchers)

enum class NiaDispatchers { Default, IO }
```

Use it on provider functions and injection points:

```kotlin
import org.koin.core.annotation.Module
import org.koin.core.annotation.Configuration
import org.koin.core.annotation.Singleton

@Module
@Configuration
class DispatchersModule {

    @Singleton
    @Dispatcher(NiaDispatchers.IO)
    fun providesIODispatcher(): CoroutineDispatcher = Dispatchers.IO

    @Singleton
    @Dispatcher(NiaDispatchers.Default)
    fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default

    @Singleton
    fun providesApplicationScope(
        @Dispatcher(NiaDispatchers.Default) dispatcher: CoroutineDispatcher,
    ): CoroutineScope = CoroutineScope(SupervisorJob() + dispatcher)
}
```

Custom qualifiers are validated at compile time — a mismatch between the qualifier on the provider and the injection point produces a build error.

### ViewModel for Kotlin Multiplatform

The `@KoinViewModel` annotation generates ViewModels using the unified `koin-core-viewmodel` API, providing Kotlin Multiplatform compatibility.

```kotlin
@KoinViewModel
class UserViewModel(val repository: UserRepository) : ViewModel()
```

This generates `viewModel` definitions compatible with both Android and Compose Multiplatform.

## Automatic or Specific Binding

When declaring a component, all detected "bindings" (associated supertypes) will already be prepared for you. For example, the following definition:

```kotlin
@Single
class MyComponent(val myDependency : MyDependency) : MyInterface
```

Koin will declare that your `MyComponent` component is also tied to `MyInterface`. The DSL equivalent is `single { MyComponent(get()) } bind MyInterface::class`.


Instead of letting Koin detect things for you, you can also specify what type you really want to bind with the `binds` annotation parameter:

 ```kotlin
@Single(binds = [MyBoundType::class])
```

## Nullable Dependencies

If your component is using nullable dependency, don't worry it will be handled automatically for you. Keep using your definition annotation, and Koin will guess what to do:

```kotlin
@Single
class MyComponent(val myDependency : MyDependency?)
```

The generated DSL equivalent will be `single { MyComponent(getOrNull()) }`


> Note that this also works for injected Parameters and properties

## Qualifier with @Named

You can add a "name" to a definition (also called qualifier), to make a distinction between several definitions for the same type, with the `@Named` annotation:

```kotlin
@Single
@Named("InMemoryLogger")
class LoggerInMemoryDataSource : LoggerDataSource

@Single
@Named("DatabaseLogger")
class LoggerLocalDataSource(private val logDao: LogDao) : LoggerDataSource
```

When resolving a dependency, just use the qualifier with `named` function:

```kotlin
val logger: LoggerDataSource by inject(named("InMemoryLogger"))
```

It is also possible to create custom qualifier annotations. Using the previous example:

```kotlin
@Named
annotation class InMemoryLogger

@Named
annotation class DatabaseLogger

@Single
@InMemoryLogger
class LoggerInMemoryDataSource : LoggerDataSource

@Single
@DatabaseLogger
class LoggerLocalDataSource(private val logDao: LogDao) : LoggerDataSource
```

```kotlin
val logger: LoggerDataSource by inject(named<InMemoryLogger>())
```

## Injected Parameters with @InjectedParam

You can tag a constructor member as "injected parameter", which means that the dependency will be passed in the graph when calling for resolution.

For example:

```kotlin
@Single
class MyComponent(@InjectedParam val myDependency : MyDependency)
```

Then you can call your `MyComponent` and pass an instance of `MyDependency`:

```kotlin
val m = MyDependency()
// Resolve MyComponent while passing MyDependency
koin.get<MyComponent> { parametersOf(m) }
```

The generated DSL equivalent will be `single { params -> MyComponent(params.get()) }`


## Injecting a lazy dependency - `Lazy<T>`

Koin can automatically detect and resolve a lazy dependency. Here, for example, we want to resolve lazily the `LoggerDataSource` definition. You just need to use the `Lazy` Kotlin type as follows:

```kotlin
@Single
class LoggerInMemoryDataSource : LoggerDataSource

@Single
class LoggerAggregator(val lazyLogger : Lazy<LoggerDataSource>)
```

Behind it will generate the DSL like with `inject()` instead of `get()`:

```kotlin
single { LoggerAggregator(inject()) }
```

## Injecting a list of dependencies - `List<T>`

Koin can automatically detect and resolve a list of dependencies. Here, for example, we want to resolve all `LoggerDataSource` definitions. You just need to use the `List` Kotlin type as follows:

```kotlin
@Single
@Named("InMemoryLogger")
class LoggerInMemoryDataSource : LoggerDataSource

@Single
@Named("DatabaseLogger")
class LoggerLocalDataSource(private val logDao: LogDao) : LoggerDataSource

@Single
class LoggerAggregator(val datasource : List<LoggerDataSource>)
```

Behind it will generate the DSL, like with `getAll()` function:

```kotlin
single { LoggerAggregator(getAll()) }
```

## Properties with @Property

To resolve a Koin property in your definition, just tag a constructor member with `@Property`. This will resolve the Koin property thanks to the value passed to the annotation:

```kotlin
@Factory
public class ComponentWithProps(
    @Property("id") public val id : String
)
```

The generated DSL equivalent will be `factory { ComponentWithProps(getProperty("id")) }`

### @PropertyValue - Property with default value (since 1.4)

Koin Annotations offers you the possibility to define a default value for a property, directly from your code with `@PropertyValue` annotation.
Let's follow our sample:

```kotlin
@Factory
public class ComponentWithProps(
    @Property("id") public val id : String
){
    public companion object {
        @PropertyValue("id")
        public const val DEFAULT_ID : String = "_empty_id"
    }
}
```

The generated DSL equivalent will be `factory { ComponentWithProps(getProperty("id", ComponentWithProps.DEFAULT_ID)) }`

## JSR-330 Compatibility Annotations

Koin Annotations provides JSR-330 (Jakarta Inject) compatible annotations through the `koin-jsr330` module. These annotations are particularly useful for developers migrating from other JSR-330 compatible frameworks like Hilt, Dagger, or Guice.

### Setup

Add the `koin-jsr330` dependency to your project:

```kotlin
dependencies {
    implementation "io.insert-koin:koin-jsr330:$koin_version"
}
```

### Available JSR-330 Annotations

#### @Singleton (jakarta.inject.Singleton)

JSR-330 standard singleton annotation, equivalent to Koin's `@Single`:

```kotlin
import jakarta.inject.Singleton

@Singleton
class DatabaseService
```

This generates the same result as `@Single` - a singleton instance in Koin.

#### @Named (jakarta.inject.Named)

JSR-330 standard qualifier annotation for string-based qualifiers:

```kotlin
import jakarta.inject.Named
import jakarta.inject.Singleton

@Singleton
@Named("inMemory")
class InMemoryCache : Cache

@Singleton  
@Named("redis")
class RedisCache : Cache
```

#### @Inject (jakarta.inject.Inject)

JSR-330 standard injection annotation. While Koin Annotations doesn't require explicit constructor marking, `@Inject` can be used for JSR-330 compatibility:

```kotlin
import jakarta.inject.Inject
import jakarta.inject.Singleton

@Singleton
class UserService @Inject constructor(
    private val repository: UserRepository
)
```

#### @Qualifier (jakarta.inject.Qualifier)

Meta-annotation for creating custom qualifier annotations:

```kotlin
import jakarta.inject.Qualifier

@Qualifier
annotation class Database

@Qualifier  
annotation class Cache

@Singleton
@Database
class DatabaseConfig

@Singleton
@Cache  
class CacheConfig
```

#### @Scope (jakarta.inject.Scope)

Meta-annotation for creating custom scope annotations:

```kotlin
import jakarta.inject.Scope

@Scope
annotation class RequestScoped

// Use with Koin's scope system
@Scope(name = "request") 
@RequestScoped
class RequestProcessor
```

### Mixed Usage

You can freely mix JSR-330 annotations with Koin annotations in the same project:

```kotlin
// JSR-330 style
@Singleton
@Named("primary")
class PrimaryDatabase : Database

// Koin style  
@Single
@Named("secondary")
class SecondaryDatabase : Database

// Mixed in same class
@Factory
class DatabaseManager @Inject constructor(
    @Named("primary") private val primary: Database,
    @Named("secondary") private val secondary: Database  
)
```

### Framework Migration Benefits

Using JSR-330 annotations provides several advantages for framework migration:

- **Familiar API**: Developers coming from Hilt, Dagger, or Guice can use known annotations
- **Gradual Migration**: Existing JSR-330 annotated code works with minimal changes
- **Standard Compliance**: Following JSR-330 ensures compatibility with dependency injection standards
- **Team Onboarding**: Easier for teams familiar with other DI frameworks

:::info
JSR-330 annotations in Koin generate the same underlying DSL as their Koin equivalents. The choice between JSR-330 and Koin annotations is purely stylistic and based on team preferences or migration requirements.
:::

---

### Reference Koin Annotations Kmp (`docs/reference/koin-annotations/kmp.md`)

---
title: Kotlin Multiplatform
---

## Setup

The Koin Compiler Plugin simplifies KMP setup - just apply the plugin.

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

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

That's it! No per-platform KSP configuration needed.

## Defining Definitions and Modules in Common Code

In your `commonMain` sourceSet, declare your Module, scan for definitions, or define functions as regular Kotlin Koin declarations. See [Definitions](./definitions) and [Modules](./modules).

## Sharing Patterns

In this section, we will see together several ways to share components with definitions and modules.

In a Kotlin Multiplatform application, some components must be implemented specifically per platform. You can share those components at the definition level, with expected/actual on the given class (definition or module).
You can share a definition with expect/actual implementation, or a module with expect/actual.

:::info
Please look at [Multiplatform Expect & Actual Rules](https://www.jetbrains.com/help/kotlin-multiplatform-dev/multiplatform-expect-actual.html) documentation for general Kotlin guidance.
:::

:::warning
Expect/Actual classes can't have different constructors per platform. You need to respect the current constructor contract designed in common space
:::

### Sharing Definitions for native implementations

:::info
We target sharing with a Common Module + Expect/Actual Class Definition
:::

For this first classic pattern, you can use both definitions scanning with `@ComponentScan` or declare a definition as a module class function.

Be aware that to use `expect/actual` definitions, you will use the same constructor (either the default or a custom one). This constructor has to be the same on all platforms.  

#### Scanning for Expect/Actual definitions

In commonMain:
```kotlin
// commonMain

@Module
@ComponentScan("com.jetbrains.kmpapp.native")
class NativeModuleA()

// package com.jetbrains.kmpapp.native
@Factory
expect class PlatformComponentA() {
    fun sayHello() : String
}
```

In native sources, implement our actual classes:

```kotlin
// androidMain

// package com.jetbrains.kmpapp.native
actual class PlatformComponentA {
    actual fun sayHello() : String = "I'm Android - A"
}

// iOSMain

// package com.jetbrains.kmpapp.native
actual class PlatformComponentA {
    actual fun sayHello() : String = "I'm iOS - A"
}
```

#### Declaring Expect/Actual function definitions

In commonMain:
```kotlin
// commonMain

@Module
class NativeModuleB() {

    @Factory
    fun providesPlatformComponentB() : PlatformComponentB = PlatformComponentB()
}

expect class PlatformComponentB() {
    fun sayHello() : String
}
```

In native sources, implement our actual classes:

```kotlin
// androidMain

// package com.jetbrains.kmpapp.native
actual class PlatformComponentB {
    actual fun sayHello() : String = "I'm Android - B"
}

// iOSMain

// package com.jetbrains.kmpapp.native
actual class PlatformComponentB {
    actual fun sayHello() : String = "I'm iOS - B"
}
```

### Sharing Definitions with different native contracts

:::info
We target Expect/Actual common Module + common Interface + native implementations
:::

In some cases, you need different constructor arguments on each native implementation. Then Expect/Actual class is not your solution. 
You need to go with an `interface` to implement on each platform, and a Expect/Actual class module to allow a module to define your right platform implementation:

In commonMain:
```kotlin
// commonMain

expect class NativeModuleD() {
    @Factory
    fun providesPlatformComponentD(scope : org.koin.core.scope.Scope) : PlatformComponentD
}

interface PlatformComponentD {
    fun sayHello() : String
}
```

In native sources, implement our actual classes:

```kotlin
// androidMain

@Module
actual class NativeModuleD {
    @Factory
    actual fun providesPlatformComponentD(scope : org.koin.core.scope.Scope) : PlatformComponentD = PlatformComponentDAndroid(scope)
}

class PlatformComponentDAndroid(scope : org.koin.core.scope.Scope) : PlatformComponentD{
    val context : Context = scope.get()
    override fun sayHello() : String = "I'm Android - D - with ${context}"
}

// iOSMain
@Module
actual class NativeModuleD {
    @Factory
    actual fun providesPlatformComponentD(scope : org.koin.core.scope.Scope) : PlatformComponentD = PlatformComponentDiOS()
}

class PlatformComponentDiOS : PlatformComponentD{
    override fun sayHello() : String = "I'm iOS - D"
}
```

:::note
Each time you use manual access to Koin scope, you are doing dynamic wiring. Compile safety doesn't cover such wiring.
:::

### Safely Sharing across platforms with Platform Wrapper

:::info
Wrap a specific platform component, as a "platform wrapper"
:::

You can wrap a specific platform component, as a "platform wrapper", to help you minimize dynamic injection.

For example, we can do a `ContextWrapper` that lets us inject Android `Context` when needed, but doesn't impact the iOS side.

In commonMain:
```kotlin
// commonMain

expect class ContextWrapper

@Module
expect class ContextModule() {

    @Single
    fun providesContextWrapper(scope : Scope) : ContextWrapper
}
```

In native sources, implement our actual classes:

```kotlin
// androidMain
actual class ContextWrapper(val context: Context)

@Module
actual class ContextModule {
    
    // needs androidContext() to be setup at start
    @Single
    actual fun providesContextWrapper(scope : Scope) : ContextWrapper = ContextWrapper(scope.get())
}

// iOSMain
actual class ContextWrapper

@Module
actual class ContextModule {

    @Single
    actual fun providesContextWrapper(scope : Scope) : ContextWrapper = ContextWrapper()
}
```

:::info
This way, you minimize the dynamic platform wiring to one definition, and inject safely in your entire system.
:::

You can now use your `ContextWrapper` from common code, and easily pass it in your Expect/Actual classes:

In commonMain:
```kotlin
// commonMain

@Module
@ComponentScan("com.jetbrains.kmpapp.native")
class NativeModuleA()

// package com.jetbrains.kmpapp.native
@Factory
expect class PlatformComponentA(ctx : ContextWrapper) {
    fun sayHello() : String
}
```

In native sources, implement our actual classes:

```kotlin
// androidMain

// package com.jetbrains.kmpapp.native
actual class PlatformComponentA actual constructor(val ctx : ContextWrapper) {
    actual fun sayHello() : String = "I'm Android - A - with context: ${ctx.context}"
}

// iOSMain

// package com.jetbrains.kmpapp.native
actual class PlatformComponentA actual constructor(val ctx : ContextWrapper) {
    actual fun sayHello() : String = "I'm iOS - A"
}
```

### Sharing Expect/Actual Module - rely on Native Module Scanning

:::info
Rely on a native module from a common module 
:::

In some cases, you don't want to have constraints, and scan for components on each native side. Define an empty module class in the common source set, and define your implementation on each platform.

:::info
If you define an empty module in the common side, each native module implementation will be generated from each native target, allowing to scan native only components for example.
:::

In commonMain:
```kotlin
// commonMain

@Module
expect class NativeModuleC()
```

In native source sets:

```kotlin
// androidMain
@Module
@ComponentScan("com.jetbrains.kmpapp.other.android")
actual class NativeModuleC

//com.jetbrains.kmpapp.other.android
@Factory
class PlatformComponentC(val context: Context) {
    fun sayHello() : String = "I'm Android - C - $context"
}

// iOSMain
// do nothing on iOS
@Module
actual class NativeModuleC
```

---

### Reference Koin Annotations Modules (`docs/reference/koin-annotations/modules.md`)

---
title: Application, Configuration and Modules 
---

## Application Bootstrap with @KoinApplication

Use `@KoinApplication` to define your application entry point:

```kotlin
@KoinApplication(modules = [MyModule::class])
class MyApp
```

Start Koin using the typed API:

```kotlin
fun main() {
    startKoin<MyApp>()

    // Or with configuration
    startKoin<MyApp> {
        printLogger()
    }
}
```

### Available Typed APIs

| API | Description |
|-----|-------------|
| `startKoin<T>()` | Start Koin globally |
| `startKoin<T> { }` | Start with configuration block |
| `koinApplication<T>()` | Create isolated KoinApplication |
| `koinConfiguration<T>()` | Create configuration (for Compose, Ktor) |
| `module<T>()` | Load a single `@Module` class |
| `modules(A::class, B::class)` | Load multiple `@Module` classes |

### Loading Individual Modules

Use `module<T>()` or `modules(vararg KClass)` to load `@Module` classes directly, without needing `@KoinApplication`:

```kotlin
startKoin {
    module<NetworkModule>()
    modules(DataModule::class, CacheModule::class)
}
```

This is useful for tests or when mixing annotation modules with DSL configuration:

```kotlin
// In tests — load only the modules you need
@get:Rule
val koinTestRule = KoinTestRule.create {
    module<NetworkModule>()
}
```

:::info
`module<T>()` and `modules(vararg KClass)` are stub functions that the compiler plugin intercepts and transforms at compile time. They require the Koin Compiler Plugin to be applied.
:::

### @KoinApplication Parameters

- `modules`: Array of module classes to include
- `configurations`: Array of configuration labels to load

```kotlin
@KoinApplication(
    modules = [CoreModule::class],
    configurations = ["production"]
)
class ProdApp
```

:::info
When no configurations are specified, modules marked with `@Configuration` (default label) are loaded automatically.
:::

### Module Load Order and Overrides

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 from `@KoinApplication` in this order:

1. **Auto-discovered `@Configuration` modules** (local + dependency JARs) — load first
2. **Explicit `@KoinApplication(modules = [A, B, C])`** — load last, **in declaration order**

So app-level overrides always win over dependency defaults:

```kotlin
// In a dependency library module
@Module @Configuration
class CoreModule {
    @Singleton fun feature(): Feature = DefaultFeature()
}

// In your app module
@Module
class AppModule {
    @Singleton fun feature(): Feature = AppFeature()  // custom override
}

@KoinApplication(modules = [AppModule::class])
class MyApp
// Load order: CoreModule (DefaultFeature) → AppModule (AppFeature wins)
// Runtime get<Feature>() returns AppFeature.
```

Within the explicit list, the declared order is preserved — so `@KoinApplication(modules = [A, B, C])` loads A, then B, then C, and C wins among those three. Each entry's `@Module(includes = [...])` chain stays grouped with that entry.

If a module appears in both the explicit list and is also `@Configuration`-discovered, it loads once — at its **explicit position** — so the declaration order in `modules = [...]` always controls override precedence.

:::tip
If you need a specific load order between several `@Configuration` modules (instead of the classpath-scan order), list them explicitly in `@KoinApplication(modules = [Core::class, Feature::class, App::class])` — the explicit list honours declaration order.
:::

## Configuration Management with @Configuration

The `@Configuration` annotation allows you to organize modules into different configurations (environments, flavors, etc.). This is useful for organizing modules by deployment environment or feature sets.

### Basic Configuration Usage

```kotlin
// Put module in default Configuration
@Module
@Configuration
class CoreModule
```

:::info
The default configuration is named "default", can be used with `@Configuration` or `@Configuration("default")`
:::

You need to use the `@KoinApplication` to be able to scan modules from configuration:

```kotlin
// module A
@Module
@Configuration
class ModuleA

// module B
@Module
@Configuration
class ModuleB

// module App, scan all @Configuration modules
@KoinApplication
object MyApp
```


### Multiple Configuration Support

A module can be associated with multiple configurations:

```kotlin
// This module is available in both "prod" and "test" configurations
@Module
@Configuration("prod", "test")
class DatabaseModule {
    @Single
    fun database() = PostgreSQLDatabase()
}

// This module is available in default, test, and development
@Module
@Configuration("default", "test", "development") 
class LoggingModule {
    @Single
    fun logger() = Logger()
}
```

### Environment-Specific Configurations

```kotlin
// Development-only configuration
@Module
@Configuration("development")
class DevDatabaseModule {
    @Single
    fun database() = InMemoryDatabase()
}

// Production-only configuration  
@Module
@Configuration("production")
class ProdDatabaseModule {
    @Single
    fun database() = PostgreSQLDatabase()
}

// Available in multiple environments
@Module
@Configuration("default", "production", "development")
class CoreModule {
    @Single
    fun logger() = Logger()
}
```

### Using Configurations with @KoinApplication

By default, the `@KoinApplication` is loading all default configurations (modules tagged with `@Configuration`)

You can also reference these configurations in your application bootstrap:

```kotlin
@KoinApplication(configurations = ["default", "production"])
class ProductionApp

@KoinApplication(configurations = ["default", "development"])  
class DevelopmentApp

// Load only default configuration (same as @KoinApplication with no parameters)
@KoinApplication
class SimpleApp
```

:::info
- Empty `@Configuration` is equivalent to `@Configuration("default")`
- The "default" configuration is loaded automatically when no specific configurations are specified
- Modules can belong to multiple configurations by listing them in the annotation
:::


## Organizing with Modules

Always organize your definitions in explicit modules using `@Module`:

## Class Module with @Module

To declare a module, tag a class with `@Module` annotation:

```kotlin
@Module
class MyModule
```

Reference modules in your `@KoinApplication`:

```kotlin
@KoinApplication(modules = [MyModule::class])
class MyApp

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

## Components Scan with @ComponentScan

Use `@ComponentScan` to automatically discover annotated components:

```kotlin
@Module
@ComponentScan
class MyModule
```

This scans the current package and subpackages for annotated components. Specify a package explicitly:

```kotlin
@Module
@ComponentScan("com.myapp.features")
class FeatureModule
```

:::info
`@ComponentScan` traverses across all Gradle modules for the same package.
:::

## Definitions in Class Modules

To define a definition directly in your code, you can annotate a function with definition annotations:

```kotlin
// given 
// class MyComponent(val myDependency : MyDependency)

@Module
class MyModule {

  @Single
  fun myComponent(myDependency : MyDependency) = MyComponent(myDependency)
}
```

> **Note**: `@InjectedParam` (for injected parameters from startKoin) and `@Property` (for property injection) are also usable on function members. See the definitions documentation for more details on these annotations.


## Including Modules

Use the `includes` attribute to compose modules:

```kotlin
@Module
class ModuleA

@Module(includes = [ModuleA::class])
class ModuleB
```

Reference the root module in your application:

```kotlin
@KoinApplication(modules = [ModuleB::class])  // Includes ModuleA automatically
class MyApp

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

---

### Reference Koin Annotations Monitor (`docs/reference/koin-annotations/monitor.md`)

---
title: Koin Built-in Performance Monitoring with @Monitor
---

The `@Monitor` annotation (available since Koin Annotations 2.2.0) enables automatic performance monitoring and tracing for your Koin components through the [Kotzilla Platform](https://kotzilla.io), the official tooling platform for Koin.

## Setup

Add the Kotzilla SDK dependency:

```kotlin
dependencies {
    implementation "io.kotzilla:kotzilla-core:latest.version"
}
```

Check the [latest version](https://doc.kotzilla.io/docs/releaseNotes/changelogSDK) on the Kotzilla documentation.

Configure the `allOpen` plugin to make monitored classes extensible:

```kotlin
plugins {
    id "org.jetbrains.kotlin.plugin.allopen"
}

allOpen {
    annotation("org.koin.core.annotation.Monitor")
}
```

Initialize Kotzilla analytics in your Koin configuration:

```kotlin
import io.kotzilla.sdk.analytics.koin.analytics

fun initKoin() {
    startKoin {
        // Enable Kotzilla monitoring
        analytics()
        modules(appModule)
    }
}
```

## Basic Usage

Simply annotate your Koin components with `@Monitor`:

```kotlin
@Monitor
@Single
class UserService(private val userRepository: UserRepository) {
    fun findUser(id: String): User? = userRepository.findById(id)
    
    suspend fun createUser(userData: UserData): User {
        return userRepository.save(userData)
    }
}
```

## Generated Code

The compiler automatically generates a proxy class that wraps your component:

```kotlin
/**
 * Generated by @Monitor - Koin proxy for 'UserService'
 */
class UserServiceProxy(userRepository: UserRepository) : UserService(userRepository) {
    override fun findUser(id: String): User? { 
        return KotzillaCore.getDefaultInstance().trace("UserService.findUser") { 
            super.findUser(id) 
        } 
    }
    
    override suspend fun createUser(userData: UserData): User { 
        return KotzillaCore.getDefaultInstance().suspendTrace("UserService.createUser") { 
            super.createUser(userData) 
        } 
    }
}
```

Koin automatically uses the proxy instead of the original class, transparently capturing:
- Method execution times
- Call frequency and patterns
- Error rates and types
- Performance bottlenecks

## ViewModels Monitoring

Monitor your ViewModels to track UI performance:

```kotlin
@Monitor
@KoinViewModel
class DetailViewModel(private val repository: Repository) : ViewModel() {
    fun loadData(id: String): StateFlow<Data> = repository.getData(id)
}
```

## Kotzilla Platform Integration

The monitoring data is automatically sent to your [Kotzilla Platform](https://kotzilla.io) workspace, providing:

- **Real-time Performance Dashboard**: View method execution times and trends
- **Error Tracking**: Monitor exception rates and stack traces
- **Usage Analytics**: Understand which components are most heavily used
- **Performance Alerts**: Get notified of performance regressions

Create your free Kotzilla account and configure the API key in your `kotzilla.json` file:

```json
{
  "sdkVersion": "latest.version",
  "keys": [
    {
      "appId": "your-app-id",
      "applicationPackageName": "com.example.app",
      "keyId": "your-key-id", 
      "apiKey": "your-api-key"
    }
  ]
}
```

## Requirements

- Classes annotated with `@Monitor` must be open (automatically handled by `allOpen` plugin)
- Kotzilla SDK dependency must be available at runtime
- Valid Kotzilla Platform account and API key for data collection

:::info
The `@Monitor` annotation only tracks method calls on the monitored class itself. Dependencies injected into the monitored class are not automatically monitored unless they are also annotated with `@Monitor`.
:::

:::note
For complete setup instructions and advanced configuration options, visit the [Kotzilla Documentation](https://doc.kotzilla.io).
:::

---

### Reference Koin Annotations Options (`docs/reference/koin-annotations/options.md`)

---
title: Compiler Plugin Options
---

The Koin Compiler Plugin supports configuration options to customize its behavior.

## Configuration

Configure the compiler plugin in your `build.gradle.kts`:

```kotlin
koinCompiler {
    userLogs = true
    debugLogs = false
    compileSafety = true
    strictSafety = true       // auto-detected by default
    skipDefaultValues = true
    unsafeDslChecks = true
}
```

## Available Options

### userLogs

- **Type**: Boolean
- **Default**: `false`
- **Description**: Enables logs for component detection and DSL/annotation processing. Shows which components are discovered and processed by the plugin.
- **Usage**: Enable during development to debug component discovery issues.

```kotlin
koinCompiler {
    userLogs = true
}
```

### debugLogs

- **Type**: Boolean
- **Default**: `false`
- **Description**: Enables verbose debug logs for internal plugin processing (FIR/IR phases, module discovery).
- **Usage**: Enable when troubleshooting plugin issues or reporting bugs.

```kotlin
koinCompiler {
    debugLogs = true
}
```

### compileSafety

- **Type**: Boolean
- **Default**: `true`
- **Description**: Enables compile-time dependency validation. When enabled, the plugin validates that all dependencies can be resolved at build time — catching missing definitions, qualifier mismatches, and broken call sites before runtime.
- **Usage**: Enabled by default. Disable temporarily if you need to bypass validation during migration.

```kotlin
koinCompiler {
    compileSafety = true
}
```

See [Compile-Time Safety](/docs/reference/koin-compiler/compile-safety) for full details on what gets validated.

### strictSafety

- **Type**: Boolean
- **Default**: auto-detected (enabled on aggregator modules — those containing `startKoin`, `koinApplication`, or `@KoinApplication`)
- **Description**: Forces the full-graph safety pass (A3) to re-run on every build, bypassing Kotlin's incremental compilation cache on the aggregator module. Library and feature modules stay fully incremental.
- **Usage**: Leave at default. Set explicitly to `true` if auto-detection misses your aggregator, or to `false` to opt out (e.g. when a test fixture only references `startKoin` in a comment and trips the detector).

```kotlin
koinCompiler {
    strictSafety = true   // force-enable
    // or
    strictSafety = false  // opt out of auto-detection
}
```

**Why it exists**: K2's incremental compilation today (via the Build Tools API used by AGP) doesn't track two things the DI graph relies on — DSL definitions inside `module { … }` lambda bodies (not part of any declaration's ABI), and `@ComponentScan` package-scope discovery (no source-level edge from the scanner to newly-added classes). The aggregator's `compileKotlin` task can be marked UP-TO-DATE even when the graph changed. `strictSafety` is the smallest correct workaround on top of what K2 IC currently surfaces; it has bounded cost since only the aggregator re-runs each build.

Has no effect when `compileSafety = false`. See [koin-compiler-plugin issue #32](https://github.com/InsertKoinIO/koin-compiler-plugin/issues/32) for background.

### skipDefaultValues

- **Type**: Boolean
- **Default**: `true`
- **Description**: When enabled, parameters with Kotlin default values will use the default instead of being resolved from the DI container. Nullable parameters and annotated parameters (`@Named`, `@InjectedParam`, etc.) are still resolved normally.
- **Usage**: Enabled by default. Disable to always inject all parameters from the DI container.

```kotlin
koinCompiler {
    skipDefaultValues = true
}
```

### unsafeDslChecks

- **Type**: Boolean
- **Default**: `true`
- **Description**: Validates that DSL function calls (like `create()`) inside lambdas are the only instruction. Helps prevent common mistakes.
- **Usage**: Disable temporarily during migration from classic DSL if needed.

```kotlin
koinCompiler {
    unsafeDslChecks = false  // Disable during migration
}
```

## Complete Example

```kotlin
// build.gradle.kts
plugins {
    alias(libs.plugins.koin.compiler)
}

koinCompiler {
    userLogs = true           // Log component detection
    debugLogs = false         // Verbose logs (off by default)
    compileSafety = true      // Compile-time dependency validation
    strictSafety = true       // Force aggregator to re-run safety pass (auto-detected by default)
    skipDefaultValues = true  // Use Kotlin defaults instead of DI resolution
    unsafeDslChecks = true    // Validate DSL usage
}
```

## Best Practices

- **Keep `compileSafety` enabled** (default) for compile-time dependency validation
- **Leave `strictSafety` at auto-detect** — only override if the detector misses your aggregator or trips on a non-aggregator file
- **Keep `skipDefaultValues` enabled** (default) to respect Kotlin default values
- **Enable `userLogs`** during development to see which components are detected
- **Keep `unsafeDslChecks` enabled** (default) for safer DSL usage
- **Use `debugLogs`** only when troubleshooting plugin issues

## See Also

- **[Compile-Time Safety](/docs/reference/koin-compiler/compile-safety)** - What gets validated and how
- **[Compiler Plugin Setup](/docs/setup/compiler-plugin)** - Complete setup guide
- **[Starting with Annotations](/docs/reference/koin-annotations/start)** - Getting started

---

### Reference Koin Annotations Scope (`docs/reference/koin-annotations/scope.md`)

---
title: Scopes in Koin Annotations
---

While using definitions and modules, you may need to define scopes for a particular space and time resolution.

## Defining a Scope with @Scope

Koin allows the use of scopes. Please refer to [Koin Scopes](/docs/reference/koin-core/scopes) section for more details on basics. 

To declare a scope with annotations, just use `@Scope` annotation on a class, like this

```kotlin
@Scope
class MyScopeClass
```

> this will be equivalent of the following scope section:
> ```kotlin
> scope<MyScopeClass> {
> 
>}
> ```

Else, if you need a scope name more than a type, you need to tag a class with `@Scope(name = )` annotation, using `name` parameter:

```kotlin
@Scope(name = "my_scope_name")
class MyScopeClass
```

> this will be the equivalent of 
>
>```kotlin
>scope<named("my_scope_name")> {
>
>}
>```

## Adding a definition in a Scope with @Scoped

To declare a definition inside a scope (defined or not with annotations), just tag a class with `@Scope` and `@Scoped` annotations:

```kotlin
@Scope(name = "my_scope_name")
@Scoped
class MyScopedComponent
```

This will generate the right definition inside the scope section:

```kotlin
scope<named("my_scope_name")> {
  scoped { MyScopedComponent() }
}
```

:::info
  You need both annotations to indicate the needed scope space (with `@Scope`) and the kind of component to define (with `@Scoped`)
:::

## Dependency resolution from a scope

From a scoped definition, you can resolve any definition from your inner Scope and from the parent scopes.

For example, the following case will work:

```kotlin
@Single
class MySingle

@Scope(name = "my_scope_name")
@Scoped
class MyScopedComponent(
  val mySingle : MySingle,
  val myOtherScopedComponent :MyOtherScopedComponent
)

@Scope(name = "my_scope_name")
@Scoped
class MyOtherScopedComponent(
  val mySingle : MySingle
)
```

The component `MySingle` is defined as `single` definition, in the root. `MyScopedComponent` and `MyOtherScopedComponent` are defined in scope "my_scope_name".
The dependencies resolution from `MyScopedComponent` is accessing the Koin root with `MySingle` instance, and `MyOtherScopedComponent` scoped instance from the current "my_scope_name" scope.


## Resolving outside a Scope with @ScopeId (since 1.3.0)

You may need to resolve a component from another scope that is not directly accessible to your scope. For this, you need to tag your dependency with `@ScopeId` annotation to tell Koin to find this dependency in the scope of the given scope Id.

With a string name:

```kotlin
@Factory
class MyFactory(
  @ScopeId(name = "my_scope_id") val myScopedComponent : MyScopedComponent
)
```

Or with a type reference:

```kotlin
@Factory
class MyFactory(
  @ScopeId(MyScope::class) val myScopedComponent : MyScopedComponent
)
```

The above code is equivalent is generated:

```kotlin
factory { Myfactory(getScope("my_scope_id").get()) }
```

This example shows that `MyFactory` component will resolve `MyScopedComponent` component from a scope instance with id "my_scope_id". This scope, created with id "my_scope_id" needs to be created with the right scope definition.

:::info
  The `MyScopedComponent` component needs to be defined in a Scope section, and a scope instance needs to be created with id "my_scope_id". 
:::

## Scope Archetype Annotations

Koin Annotations provides predefined scope archetype annotations for common scope patterns, eliminating the need to manually declare scope types. These annotations combine scope declaration and component definition in a single annotation.

### Android Scope Archetypes

For Android development, you can use these predefined scope annotations:

#### @ActivityScope

Declare a component in an Activity scope:

```kotlin
@ActivityScope
class ActivityScopedComponent(val dependency: MyDependency)
```

This generates:
```kotlin
activityScope {
    scoped { ActivityScopedComponent(get()) }
}
```

**Usage:** The tagged class is meant to be used with Activity and the `activityScope` function to activate the scope.

#### @ActivityRetainedScope

Declare a component in an Activity Retained scope (survives configuration changes):

```kotlin
@ActivityRetainedScope
class RetainedComponent(val repository: MyRepository)
```

This generates:
```kotlin
activityRetainedScope {
    scoped { RetainedComponent(get()) }
}
```

**Usage:** The tagged class is meant to be used with Activity and the `activityRetainedScope` function to activate the scope.

#### @FragmentScope

Declare a component in a Fragment scope:

```kotlin
@FragmentScope
class FragmentScopedComponent(val service: MyService)
```

This generates:
```kotlin
fragmentScope {
    scoped { FragmentScopedComponent(get()) }
}
```

**Usage:** The tagged class is meant to be used with Fragment and the `fragmentScope` function to activate the scope.

### Core Scope Archetypes

#### @ViewModelScope

Declare a component in a ViewModel scope. This annotation is **Kotlin Multiplatform (KMP) compatible** and works with both Android ViewModels and Compose Multiplatform ViewModels:

```kotlin
@ViewModelScope
class ViewModelScopedRepository(val apiService: ApiService)

@ViewModelScope  
class ViewModelScopedUseCase(
    val repository: ViewModelScopedRepository,
    val analytics: AnalyticsService
)
```

This generates:
```kotlin
viewModelScope {
    scoped { ViewModelScopedRepository(get()) }
    scoped { ViewModelScopedUseCase(get(), get()) }
}
```

**Usage:** The tagged class is meant to be used with ViewModel and the `viewModelScope` function to activate the scope.

**KMP Support:** Works seamlessly across all Kotlin Multiplatform targets including Android, iOS, Desktop, and Web platforms where ViewModels are used.

### Using Scope Archetypes

Scope archetype annotations work seamlessly with regular Koin scoping:

```kotlin
// Regular components
@Single
class GlobalService

// Scoped components using archetypes
@ActivityScope
class ActivityService(val global: GlobalService)

@FragmentScope  
class FragmentService(
    val global: GlobalService,
    val activity: ActivityService
)
```

### Combining with Function Definitions

Scope archetypes can also be used on functions within modules:

```kotlin
@Module
class MyModule {
    
    @ActivityScope
    fun activityComponent(dep: MyDependency) = MyActivityComponent(dep)
    
    @FragmentScope
    fun fragmentComponent(dep: MyDependency) = MyFragmentComponent(dep)
}
```

:::info
Scope archetype annotations automatically create the appropriate scope definition and scoped component declaration, reducing boilerplate code for common scope patterns.
:::

---

### Reference Koin Annotations Start (`docs/reference/koin-annotations/start.md`)

---
title: Starting with Koin Annotations
---

:::info Koin Annotations status
**Koin Annotations is now part of the Koin project.** The `koin-annotations` library ships under the main Koin version and is fully supported.

The legacy KSP processor (`koin-ksp-compiler`) is **deprecated** in favor of the **Koin Compiler Plugin** — your annotations stay the same; only the build setup changes. See [Migrating from KSP to Compiler Plugin](/docs/migration/from-ksp-to-compiler-plugin).
:::

Koin Annotations let you declare definitions using annotations on your classes. The Koin Compiler Plugin processes these annotations and generates all underlying Koin DSL for you at compile time.

## Getting Started

Not familiar with Koin? First, take a look at [Koin Getting Started](https://insert-koin.io/docs/quickstart/kotlin/)

### Setup

Add the Koin Compiler Plugin to your project. See [Compiler Plugin Setup](/docs/setup/compiler-plugin) for complete instructions.

```kotlin
// build.gradle.kts
plugins {
    alias(libs.plugins.koin.compiler)
}

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

### Annotating Components

Tag your components with definition annotations:

```kotlin
@Singleton
class MyRepository

@Singleton
class MyService(val repository: MyRepository)

@Factory
class MyUseCase(val service: MyService)
```

### Declaring Modules

Create a module to organize your definitions:

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

### Starting Koin

Use `@KoinApplication` with the typed startup API:

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

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

    // Use your Koin API as usual
    KoinPlatform.getKoin().get<MyService>()
}
```

## Configuration Labels

Use `@Configuration` to create modules that load based on labels:

```kotlin
@Module
@Configuration  // Default configuration
class CoreModule

@Module
@Configuration("prod")
class ProdModule

@Module
@Configuration("test")
class TestModule
```

Load specific configurations:

```kotlin
@KoinApplication(
    modules = [CoreModule::class],
    configurations = ["prod"]  // Only loads @Configuration("prod") modules
)
class ProdApp

fun main() {
    startKoin<ProdApp>()
}
```

## Typed Startup APIs

The Compiler Plugin provides typed APIs for starting Koin:

| API | Description |
|-----|-------------|
| `startKoin<T>()` | Start Koin globally |
| `startKoin<T> { }` | Start with configuration block |
| `koinApplication<T>()` | Create isolated KoinApplication |
| `koinConfiguration<T>()` | Create configuration (for Compose, Ktor) |
| `module<T>()` | Load a single `@Module` class |
| `modules(A::class, B::class)` | Load multiple `@Module` classes |

Where `T` is a class annotated with `@KoinApplication` (for startup APIs) or `@Module` (for module loading APIs).

### Loading Individual Modules

You can load `@Module` classes directly without `@KoinApplication`:

```kotlin
startKoin {
    module<NetworkModule>()
    modules(DataModule::class, CacheModule::class)
}
```

This is especially useful for **tests**:

```kotlin
@get:Rule
val koinTestRule = KoinTestRule.create {
    module<NetworkModule>()
}
```

## Compile-Time Safety

The Compiler Plugin verifies your Koin configuration at compile time, checking that all dependencies are declared and accessible.

### Bypass with @Provided

Use `@Provided` to indicate a dependency is provided externally:

```kotlin
class ExternalComponent  // Declared elsewhere

@Factory
class MyPresenter(@Provided val external: ExternalComponent)
```

## Compiler Plugin Options

See **[Compiler Plugin Options](/docs/reference/koin-annotations/options)** for all configuration options.

## ProGuard Rules

For SDK development with ProGuard/R8:

```
# Keep annotation definitions
-keep class org.koin.core.annotation.** { *; }

# Keep classes annotated with Koin annotations
-keep @org.koin.core.annotation.* class * { *; }
```

## See Also

- **[Compiler Plugin Setup](/docs/setup/compiler-plugin)** - Complete setup guide
- **[Definitions](/docs/reference/koin-annotations/definitions)** - All definition annotations
- **[Modules](/docs/reference/koin-annotations/modules)** - Module organization
- **[KMP Support](/docs/reference/koin-annotations/kmp)** - Kotlin Multiplatform

---

### Reference Koin Compiler Compile Safety (`docs/reference/koin-compiler/compile-safety.md`)

---
title: Compile-Time Safety
---

The Koin Compiler Plugin validates your dependency graph at compile time — catching missing dependencies, qualifier mismatches, and broken call sites before your app runs.

This replaces runtime verification tools like `verify()` and `checkModules()`. If it compiles, it works.

## How It Works

The plugin validates your graph at three levels during compilation:

### A2 — Per-Module (Early Feedback)

Each module's definitions are checked against visible definitions: its own definitions, explicitly included modules, and `@Configuration` sibling modules.

```kotlin
@Module(includes = [DataModule::class])
@ComponentScan("app")
class AppModule
// Validates: definitions from AppModule + DataModule
```

Modules sharing a `@Configuration` label are mutually visible:

```kotlin
@Module @ComponentScan("core") @Configuration("prod")
class CoreModule  // provides Repository

@Module @ComponentScan("service") @Configuration("prod")
class ServiceModule  // Service(repo: Repository) → OK, visible from CoreModule
```

Different labels are isolated:

```kotlin
@Configuration("core")
class CoreModule

@Configuration("service")  // different label — CoreModule NOT visible
class ServiceModule         // Service(repo: Repository) → ERROR
```

**What A2 catches:**

- Missing dependencies
- Qualifier mismatches (`@Named("prod")` requested but only `@Named("test")` provided)
- Cross-scope violations
- `Lazy<T>` without `T` provided
- External deps not marked `@Provided`

### A3 — Full Graph (Complete Guarantee)

At `startKoin<T>()`, all modules from all sources are assembled and the complete graph is validated. Everything A2 couldn't see — cross-module dependencies, definitions from JARs — is checked here.

```kotlin
@KoinApplication(modules = [CoreModule::class, ServiceModule::class])
object MyApp

startKoin<MyApp> { }
// Validates: ALL definitions from CoreModule + ServiceModule combined
```

A3 also validates DSL definitions (`single<T>()`, `factory<T>()`, etc.) when they are part of the graph.

### A4 — Call-Site Validation

Every `koinViewModel<T>()`, `get<T>()`, `inject<T>()` call in your codebase is intercepted. The plugin captures the target type, file, line, and column — then checks that `T` exists in the assembled graph.

```kotlin
@Composable
fun UserScreen() {
    val viewModel: UserViewModel = koinViewModel()  // ← A4 validates this
}

class MyFragment : Fragment() {
    val service: PaymentService by inject()  // ← A4 validates this
}
```

If `UserViewModel` isn't in the graph → build error with exact file, line, and column.

**Cross-module call sites:** If a feature module calls `koinViewModel<T>()` but doesn't have visibility into the full graph, the plugin generates a call-site hint. When the app module compiles, it discovers these hints from dependency JARs and validates them against the complete graph.

## What Gets Validated

| Scenario | Result |
|----------|--------|
| Non-nullable param, no definition | **ERROR** |
| Nullable param (`T?`), no definition | OK — uses `getOrNull()` |
| Param with default value, no definition | OK — uses Kotlin default (when `skipDefaultValues=true`) |
| `@InjectedParam`, no definition | OK — provided at runtime via `parametersOf()` |
| `@Property("key")` param | OK — property injection (warns if no `@PropertyValue` default) |
| `List<T>` param | OK — `getAll()` returns empty list if none |
| `Lazy<T>`, no definition for `T` | **ERROR** — unwraps to validate inner type |
| `@Named("x")` param, no matching qualifier | **ERROR** — with hint if unqualified binding exists |
| Scoped dependency from wrong scope | **ERROR** |
| Default value param with `@Named` qualifier | **ERROR** — qualifier forces injection |
| `@Provided` type or parameter, no definition | OK — externally provided at runtime |
| `@ScopeId(name = "x")` param | OK — resolved from named scope at runtime |
| `Scope` type param | OK — scope receiver passed directly |
| Android framework type (e.g. `Context`) | OK — hardcoded whitelist |
| Circular dependency (A → B → A) | **ERROR** — detected during A2/A3 graph traversal |

## Safety with Annotations

Annotate your classes, organize them in modules, and the compiler validates everything:

```kotlin
@Singleton
class Database

@Singleton
class UserRepository(private val db: Database)

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

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

The plugin discovers annotated classes via `@ComponentScan`, validates each module's definitions at A2, and validates the full graph at A3 when you declare your application entry point:

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

startKoin<MyApp> { }  // ← triggers A3 full graph validation
```

**Top-level functions** are also supported. Annotated top-level functions are discovered by `@ComponentScan` and validated like class definitions:

```kotlin
@Singleton
fun provideDatabase(): DatabaseService = PostgresDatabase()

@Factory
fun provideCache(db: DatabaseService): CacheService = RedisCache(db)
// ← validated: DatabaseService exists
```

Use `@Configuration` labels to organize modules into groups that are validated together:

```kotlin
@Module @ComponentScan("core") @Configuration("prod")
class CoreModule

@Module @ComponentScan("feature") @Configuration("prod")
class FeatureModule  // can see CoreModule's definitions
```

## Safety with DSL

The compiler plugin also validates DSL definitions. When you write `single<T>()`, `factory<T>()`, or `viewModel<T>()`, the plugin intercepts the call, auto-wires the constructor, and validates all parameters:

```kotlin
val appModule = module {
    single<Database>()
    single<UserRepository>()       // ← validated: Database exists
    viewModel<UserViewModel>()     // ← validated: UserRepository exists
}
```

No manual `get()` calls needed — the plugin generates them and validates them at the same time.

The `create(::T)` function is also validated. It calls a function reference (typically a builder function, but can also be a constructor) and validates all its parameters:

```kotlin
fun buildUserRepository(db: Database): UserRepository = UserRepository(db)

val appModule = module {
    scope<UserSession> {
        scoped { create(::buildUserRepository) }  // ← validated: Database exists
    }
}
```

DSL definitions participate in A3 validation (full graph) and A4 validation (call sites). If you use `startKoin { modules(appModule) }`, the plugin validates all DSL definitions against the assembled graph.

## Both Styles Together

You can mix annotations and DSL in the same project. Both are collected into the same validation graph:

```kotlin
// Annotations
@Singleton class Database

// DSL
val featureModule = module {
    single<UserRepository>()  // ← validated: Database from annotations is visible
}
```

## Error Messages

Errors report the missing type, which definition needs it, and in which module:

```
[Koin] Missing dependency: Repository
  required by: Service (parameter 'repo')
  in module: ServiceModule
```

When a binding exists with a different qualifier, a hint is shown:

```
[Koin] Missing dependency: NetworkClient (qualifier: @Named("http"))
  required by: ApiService (parameter 'client')
  in module: AppModule
  Hint: Found NetworkClient without qualifier — did you mean to add @Named("http")?
```

Call-site errors include exact location:

```
[Koin] Missing definition: com.app.UserRepository
  resolved by: koinViewModel<UserViewModel>()
  No matching definition found in any declared module.
  → file: UserScreen.kt, line: 12, column: 5
```

## Forbidden Definitions

Some return types can never be resolved meaningfully through Koin and are rejected at compile time:

### KOIN-D007: `@Factory` returning a suspend `fun interface`

A `@Factory` that returns a type extending a suspend `fun interface` cannot be invoked through Koin's synchronous `get<T>()` API. The plugin blocks this at compile time.

```kotlin
fun interface AsyncTask { suspend operator fun invoke(): Result }

@Factory
fun provideTask(): AsyncTask = AsyncTask { ... }
// KOIN-D007 — ERROR: @Factory return types cannot extend a suspend fun interface
```

Refactor to a regular interface, or expose the suspend operation through a class with a suspend method.

## Generic DSL Types

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 (same raw class, no qualifier).

```kotlin
class Box<T>(val value: T)

val appModule = module {
    single { Box(42) }   // registered as Box (raw)
}

koin.get<Box<Int>>()    // → returns the single Box registration
koin.get<Box<String>>() // → returns the same registration (erasure)
```

Validating on the raw class also avoids a Kotlin/Native klib signature mangling failure that used to crash iOS builds when a DSL definition carried an unsubstituted type parameter.

### Discriminating generic instances: type qualifier on the generic parameter

The idiomatic pattern when multiple instances of the same generic class must coexist is to register a **concrete wrapper type** and use a **type qualifier derived from the generic parameter** — `named<T>()`. This is what `koin-compose-navigation3` does internally to key each navigation route to its route type:

```kotlin
inline fun <reified T : Any> Module.navigation(
    noinline definition: @Composable Scope.(T) -> Unit,
): KoinDefinition<EntryProviderInstaller> {
    // Register a CONCRETE type (EntryProviderInstaller),
    // discriminated by a type qualifier derived from the generic param T.
    return _singleInstanceFactory<EntryProviderInstaller>(named<T>(), { ... })
}
```

Used on both sides:

```kotlin
// Declaration — T is a concrete type (HomeRoute, SettingsRoute, ...)
module {
    navigation<HomeRoute> { route -> HomeScreen() }
    navigation<SettingsRoute> { route -> SettingsScreen() }
}

// Resolution — same type qualifier keys the lookup
koin.get<EntryProviderInstaller>(named<HomeRoute>())
```

`named<T>()` produces a type qualifier from the reified `T`, so each generic instantiation gets a stable, distinct qualifier. Runtime Koin matches on (raw class + qualifier), which reintroduces the discrimination that type erasure removes.

Prefer this pattern over `single<Box<X>>()` directly whenever you need to distinguish generic instantiations.

## Scope Parameter Injection

Parameters of type `org.koin.core.scope.Scope` are automatically injected with the scope receiver — no annotation needed. Validation is skipped since injecting the scope enables dynamic lookups.

```kotlin
@Scoped
class ScopedService(val scope: Scope) {
    fun dynamicLookup() = scope.get<SomeDep>()
}
// Generates: ScopedService(scope)  — passes the scope receiver directly
```

## Named Scope Resolution: `@ScopeId`

Use `@ScopeId` to resolve a dependency from a named Koin scope instead of the current scope. Validation is skipped since the scope is resolved at runtime.

```kotlin
@Factory
class ProfileService(@ScopeId(name = "user_session") val session: UserSession)
// Generates: ProfileService(scope.getScope("user_session").get<UserSession>())
```

`@ScopeId` supports two forms:

| Form | Example | Scope ID |
|------|---------|----------|
| String name | `@ScopeId(name = "user_session")` | `"user_session"` |
| Type reference | `@ScopeId(UserSessionScope::class)` | FQ class name |

## Property Validation

`@Property("key")` parameters are resolved from Koin properties (set via `properties()` at startup). The plugin warns at compile time when no `@PropertyValue("key")` default exists:

```kotlin
@PropertyValue("api.timeout")
val defaultTimeout = 30

@Factory
class ApiClient(@Property("api.timeout") val timeout: Int)
// OK — @PropertyValue("api.timeout") provides compile-time default

@Factory
class Other(@Property("missing.key") val value: String)
// WARNING — no @PropertyValue("missing.key") found
// (still compiles — property may be provided at runtime)
```

## External Types: `@Provided`

Some types are provided by the platform or by external frameworks at runtime and are never declared as Koin definitions. Mark them with `@Provided` to skip validation.

`@Provided` works on both **classes** (all usages skip validation) and **parameters** (only that parameter is skipped):

```kotlin
// On a class — all usages of this type skip validation
@Provided
class SavedStateHandle

// On a parameter — only this parameter skips validation
@Singleton
class MyViewModel(@Provided val handle: SavedStateHandle)
```

**When to use `@Provided`:**

- **Android framework types** not in the whitelist — e.g., custom Android services
- **Third-party SDK types** injected externally — e.g., Firebase, analytics SDKs
- **Cross-module types from non-Koin modules** — when a dependency comes from a library that doesn't use Koin
- **Test doubles** — when replacing real implementations in test configurations
- **Types provided by hand** — `androidContext()`, manual `single { }` registrations

```kotlin
// External SDK — not managed by Koin
@Singleton
class AnalyticsService(@Provided val firebaseAnalytics: FirebaseAnalytics)

// Cross-module: provided by another team's module at runtime
@Factory
class PaymentProcessor(@Provided val paymentGateway: PaymentGateway)
```

**Common Android framework types are automatically whitelisted** and don't need `@Provided`:

- `android.content.Context`
- `android.app.Application`
- `android.app.Activity`
- `androidx.fragment.app.Fragment`
- `androidx.lifecycle.SavedStateHandle`
- `androidx.work.WorkerParameters`

## Default Values and skipDefaultValues

When `skipDefaultValues` is enabled (default), parameters with Kotlin default values use the default instead of being resolved from the DI container:

```kotlin
// With skipDefaultValues = true (default):
@Singleton
class ServiceWithDefault(val timeout: Int = 5000)
// → uses Kotlin default (5000), not DI resolution

// Nullable parameters are still injected:
@Singleton
class Service(val dep: Dependency? = null)
// → uses getOrNull() from DI

// Annotated parameters always use DI regardless of defaults:
@Singleton
class Service(@Named("custom") val name: String = "fallback")
// → resolves from DI with @Named("custom") qualifier

// Mixed: some from DI, some from defaults
@Singleton
class ApiClient(
    val repo: UserRepository,                        // → resolved from DI
    val timeout: Int = 30_000,                       // → uses Kotlin default
    @Property("api_url") val url: String = "https://api.example.com"  // → resolved from DI (annotated)
)
```

Set `skipDefaultValues = false` to always inject all parameters from the DI container, ignoring Kotlin default values.

## Configuration

Compile-time safety is enabled by default. To disable it:

```kotlin
koinCompiler {
    compileSafety = false  // Disable compile-time safety checks
}
```

Other related options:

```kotlin
koinCompiler {
    compileSafety = true       // Compile-time dependency validation (default: true)
    strictSafety = true        // Force aggregator's safety pass to re-run on every build
                               // (default: auto-detected on modules with startKoin / @KoinApplication)
    skipDefaultValues = true   // Skip injection for params with default values (default: true)
    unsafeDslChecks = true     // Validate create() is only instruction in lambda (default: true)
}
```

:::info Incremental compilation & `strictSafety`
The full-graph pass (A3) only runs in the aggregator's `compileKotlin`. Kotlin's incremental compilation under K2 doesn't track DSL changes inside `module { }` lambda bodies, nor classes newly added to `@ComponentScan` packages — so the aggregator can be marked UP-TO-DATE even when the graph changed. The plugin auto-enables [`strictSafety`](/docs/reference/koin-annotations/options#strictsafety) on detected aggregator modules to force A3 to re-run; library and feature modules stay fully incremental.
:::

## Migrating from verify() / checkModules()

The compiler plugin replaces runtime verification. You can remove your verification tests:

| Before | After |
|--------|-------|
| `module.verify()` in test | Compiler plugin (automatic) |
| `checkModules()` in test | Compiler plugin (automatic) |
| Runtime verification | Compile-time verification |
| Manual test setup | No test code needed |

The compiler validates on every build — no test code required.

## See Also

- **[Compiler Plugin Options](/docs/reference/koin-annotations/options)** - All configuration options
- **[Compiler Plugin Setup](/docs/setup/compiler-plugin)** - Installation guide
- **[Starting with Annotations](/docs/reference/koin-annotations/start)** - Getting started
- **[Playground Apps](https://github.com/InsertKoinIO/koin-compiler-plugin/tree/main/playground-apps)** - Complete reference apps with both annotations (`app-annotations/`) and DSL (`app-dsl/`) approaches

---

### Reference Koin Compose Compose Lifecycle (`docs/reference/koin-compose/compose-lifecycle.md`)

---
title: Lifecycle & State
---

# Lifecycle & State in Compose

This guide covers how Koin integrates with Compose's lifecycle and state management. Understanding these concepts helps you write efficient, bug-free Compose applications.

:::info
This guide aligns with [Android's official Compose lifecycle documentation](https://developer.android.com/develop/ui/compose/lifecycle).
:::

## Compose Lifecycle Overview

A Composable has three lifecycle events:

1. **Enter Composition** - Composable is first called
2. **Recomposition** - Composable re-executes when state changes (0 or more times)
3. **Leave Composition** - Composable is removed from the tree

Koin's Compose APIs are designed to work efficiently with this lifecycle.

## Injection and Recomposition

### How koinInject() Works

`koinInject()` retrieves instances from Koin and **remembers** them across recompositions:

```kotlin
@Composable
fun MyScreen() {
    // Resolved once, remembered across recompositions
    val repository = koinInject<UserRepository>()

    // Safe - uses the same instance
    val users by repository.users.collectAsState()
}
```

### Injection Timing

Inject dependencies at the **Composable function level**, not inside callbacks:

```kotlin
@Composable
fun MyScreen() {
    // Correct - resolved at composition time
    val repository = koinInject<UserRepository>()
    val viewModel = koinViewModel<MyViewModel>()

    Button(onClick = {
        // Wrong - don't inject in callbacks
        val service = koinInject<Service>() // Avoid!

        // Correct - use already-injected instance
        repository.save()
    }) {
        Text("Save")
    }
}
```

### Performance with Parameters

When using parameters with `koinInject`, prefer the explicit parameter form:

```kotlin
@Composable
fun MyScreen(userId: String) {
    // More efficient - parameters evaluated once
    val presenter = koinInject<UserPresenter>(
        parameters = parametersOf(userId)
    )

    // Less efficient - lambda re-evaluated on recomposition
    val presenter = koinInject<UserPresenter> {
        parametersOf(userId)
    }
}
```

## State Management with Koin

### StateFlow and collectAsState

The standard pattern for reactive UI with Koin:

```kotlin
@KoinViewModel
class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {
    private val _state = MutableStateFlow<UiState>(UiState.Loading)
    val state: StateFlow<UiState> = _state.asStateFlow()

    init {
        loadUsers()
    }

    private fun loadUsers() {
        viewModelScope.launch {
            _state.value = UiState.Success(repository.getUsers())
        }
    }
}

@Composable
fun UserScreen(
    viewModel: UserViewModel = koinViewModel()
) {
    val state by viewModel.state.collectAsState()

    when (val s = state) {
        is UiState.Loading -> LoadingIndicator()
        is UiState.Success -> UserList(s.users)
        is UiState.Error -> ErrorMessage(s.message)
    }
}
```

### Direct Repository Injection

For simpler cases, inject repositories directly:

```kotlin
@Singleton
class UserRepository {
    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users.asStateFlow()
}

@Composable
fun UserListScreen() {
    val repository = koinInject<UserRepository>()
    val users by repository.users.collectAsState()

    LazyColumn {
        items(users) { user ->
            UserCard(user)
        }
    }
}
```

### remember() vs koinInject()

Use the right tool for each job:

```kotlin
@Composable
fun MyScreen() {
    // Koin-managed dependencies
    val viewModel = koinViewModel<MyViewModel>()
    val repository = koinInject<Repository>()

    // Compose-managed state
    val scrollState = rememberScrollState()
    val coroutineScope = rememberCoroutineScope()
    var text by remember { mutableStateOf("") }

    // Don't wrap koinInject in remember (unnecessary)
    val service = remember { koinInject<Service>() } // Redundant!
}
```

## Side Effects with Koin

### LaunchedEffect

Execute suspending code when composition enters or keys change:

```kotlin
@Composable
fun UserDetailScreen(userId: String) {
    val repository = koinInject<UserRepository>()
    var user by remember { mutableStateOf<User?>(null) }

    // Runs when userId changes
    LaunchedEffect(userId) {
        user = repository.getUser(userId)
    }

    user?.let { UserContent(it) }
}
```

### DisposableEffect

Clean up resources when leaving composition:

```kotlin
@Composable
fun EventScreen() {
    val eventBus = koinInject<EventBus>()

    DisposableEffect(Unit) {
        val listener = eventBus.subscribe { event ->
            // Handle event
        }

        onDispose {
            eventBus.unsubscribe(listener)
        }
    }
}
```

### SideEffect

Execute non-suspending side effects after every successful recomposition:

```kotlin
@Composable
fun AnalyticsScreen(screenName: String) {
    val analytics = koinInject<Analytics>()

    SideEffect {
        analytics.logScreenView(screenName)
    }
}
```

## Stability and Skipping

### Understanding Stable Types

Compose can skip recomposition when inputs haven't changed. For this to work, parameter types must be **stable**:

```kotlin
// Stable - Compose can skip
@Composable
fun UserCard(
    name: String,                    // Primitive - stable
    onClick: () -> Unit,             // Lambda - stable
    viewModel: UserViewModel = koinViewModel()  // Treated as stable
)

// Potentially unstable - may not skip
@Composable
fun UserCard(
    user: User  // Data class - stable if all properties stable
)
```

### Koin Injections and Stability

Koin injections are treated as stable because they return the same instance (for singletons) or are remembered:

```kotlin
@Composable
fun MyScreen() {
    // Stable - singleton returns same instance
    val repository = koinInject<UserRepository>()

    // Stable - ViewModel is remembered
    val viewModel = koinViewModel<MyViewModel>()
}
```

## Passing Parameters vs Injection

### Decision Guide

| Pass as Parameter | Inject with Koin |
|-------------------|------------------|
| Changes frequently (userId, query) | Stable dependencies (repositories, services) |
| UI state (selected item) | Infrastructure (database, network) |
| Navigation arguments | Business logic (use cases) |
| Parent-provided data | ViewModels |

### Example Pattern

```kotlin
// userId changes - pass as parameter
// repository is stable - inject
@Composable
fun UserProfile(
    userId: String,
    repository: UserRepository = koinInject()
) {
    var user by remember { mutableStateOf<User?>(null) }

    LaunchedEffect(userId) {
        user = repository.getUser(userId)
    }

    user?.let { ProfileContent(it) }
}

// Pure composable - no injection needed
@Composable
fun ProfileContent(user: User) {
    Column {
        Text(user.name)
        Text(user.email)
    }
}
```

## Best Practices

### 1. Inject at the Top Level

```kotlin
@Composable
fun FeatureScreen() {
    // Inject here
    val viewModel = koinViewModel<FeatureViewModel>()
    val repository = koinInject<FeatureRepository>()

    // Pass down to children
    FeatureContent(
        state = viewModel.state,
        onAction = viewModel::handleAction
    )
}
```

### 2. Keep Child Composables Pure

```kotlin
// Pure - receives all data as parameters
@Composable
fun UserCard(
    user: User,
    onEdit: () -> Unit,
    onDelete: () -> Unit
) {
    // No injection here
}
```

### 3. Use ViewModel for Complex State

```kotlin
// Complex state management in ViewModel
@KoinViewModel
class SearchViewModel(
    private val searchRepository: SearchRepository
) : ViewModel() {
    var query by mutableStateOf("")
        private set

    private val _results = MutableStateFlow<List<Result>>(emptyList())
    val results = _results.asStateFlow()

    fun updateQuery(newQuery: String) {
        query = newQuery
        viewModelScope.launch {
            _results.value = searchRepository.search(newQuery)
        }
    }
}
```

### 4. Avoid Injection in Loops

```kotlin
@Composable
fun UserList(userIds: List<String>) {
    // Inject once outside the loop
    val repository = koinInject<UserRepository>()

    LazyColumn {
        items(userIds) { userId ->
            // Don't inject inside items!
            UserCard(userId, repository)
        }
    }
}
```

## Next Steps

- **[ViewModel in Compose](/docs/reference/koin-compose/compose-viewmodel)** - ViewModel APIs
- **[Dynamic Modules](/docs/reference/koin-compose/compose-modules)** - Module loading/unloading
- **[Testing](/docs/reference/koin-compose/compose-testing)** - Testing Composables

---

### Reference Koin Compose Compose Modules (`docs/reference/koin-compose/compose-modules.md`)

---
title: Dynamic Modules
---

# Dynamic Modules in Compose

Koin provides APIs to dynamically load and unload modules tied to Composable lifecycle. This is useful for feature modules, lazy loading, and on-demand dependencies.

## rememberKoinModules

Load Koin modules when a Composable enters composition:

```kotlin
val featureModule = module {
    factory<FeatureRepository>()
    viewModel<FeatureViewModel>()
}

@Composable
fun FeatureScreen() {
    // Load module when this Composable enters composition
    rememberKoinModules(featureModule)

    val viewModel = koinViewModel<FeatureViewModel>()
}
```

### Multiple Modules

```kotlin
@Composable
fun FeatureScreen() {
    rememberKoinModules(
        featureDataModule,
        featureDomainModule,
        featureUiModule
    )
}
```

### Unloading Modules

Control when modules are unloaded:

```kotlin
@Composable
fun FeatureScreen() {
    rememberKoinModules(
        featureModule,
        unloadOnForgotten = true,  // Unload when Composable leaves
        unloadOnAbandoned = true   // Unload if composition fails
    )
}
```

| Option | When Triggered |
|--------|----------------|
| `unloadOnForgotten` | Composable removed from composition |
| `unloadOnAbandoned` | Composition fails or is abandoned |

## Use Cases

### Feature Modules

Load feature-specific dependencies on demand:

```kotlin
// Feature module in separate Gradle module
val checkoutModule = module {
    factory<PaymentProcessor>()
    factory<CheckoutRepository>()
    viewModel<CheckoutViewModel>()
}

@Composable
fun CheckoutScreen() {
    rememberKoinModules(checkoutModule, unloadOnForgotten = true)

    val viewModel = koinViewModel<CheckoutViewModel>()
    CheckoutContent(viewModel)
}
```

### Lazy Feature Loading

Combine with navigation for lazy feature loading:

```kotlin
NavHost(navController, startDestination = "home") {
    composable("home") {
        HomeScreen()  // No extra modules needed
    }
    composable("checkout") {
        // Checkout module loaded only when navigating here
        rememberKoinModules(checkoutModule, unloadOnForgotten = true)
        CheckoutScreen()
    }
    composable("profile") {
        // Profile module loaded only when navigating here
        rememberKoinModules(profileModule, unloadOnForgotten = true)
        ProfileScreen()
    }
}
```

### Debug/Preview Modules

Swap implementations for previews:

```kotlin
val debugModule = module {
    single<ApiClient> { MockApiClient() }
}

@Preview
@Composable
fun FeatureScreenPreview() {
    rememberKoinModules(debugModule)
    FeatureScreen()
}
```

### Conditional Modules

Load modules based on conditions:

```kotlin
@Composable
fun App(isDebug: Boolean) {
    if (isDebug) {
        rememberKoinModules(debugModule)
    }

    MainScreen()
}
```

## With Lazy Modules

Combine with Koin's lazy module loading for better performance:

```kotlin
val featureModule = lazyModule {
    // Definitions parsed lazily when module is loaded
    factory<HeavyService>()
    viewModel<FeatureViewModel>()
}

@Composable
fun FeatureScreen() {
    rememberKoinModules(featureModule, unloadOnForgotten = true)

    val viewModel = koinViewModel<FeatureViewModel>()
}
```

## Best Practices

1. **Use `unloadOnForgotten = true`** - prevents memory leaks
   ```kotlin
   rememberKoinModules(featureModule, unloadOnForgotten = true)
   ```

2. **One module per feature** - keep modules focused and independent

3. **Combine with lazy modules** - for large apps with many features
   ```kotlin
   val featureModule = lazyModule { /* ... */ }
   ```

4. **Load at navigation level** - load modules in NavHost composables

5. **Avoid circular dependencies** - feature modules should not depend on each other

## Next Steps

- **[Scopes in Compose](/docs/reference/koin-compose/compose-scopes)** - Scope APIs
- **[Compose Overview](/docs/reference/koin-compose/compose)** - Setup and basic injection
- **[Isolated Context](/docs/reference/koin-compose/isolated-context)** - SDK isolation

---

### Reference Koin Compose Compose Scopes (`docs/reference/koin-compose/compose-scopes.md`)

---
title: Scopes in Compose
---

# Scopes in Compose

Koin provides several APIs to manage scopes within Compose applications, from simple composable-bound scopes to navigation-integrated scopes.

## KoinScope

Create a Koin scope tied to a Composable's lifecycle:

```kotlin
val featureModule = module {
    scope<FeatureScope> {
        scoped<FeatureCache>()
        scoped<FeatureRepository>()
    }
}

@Composable
fun FeatureScreen() {
    KoinScope(scopeOf<FeatureScope>()) {
        // All children can access scoped dependencies
        FeatureContent()
    }
}

@Composable
fun FeatureContent() {
    // Resolves from parent KoinScope
    val cache = koinInject<FeatureCache>()
}
```

The scope is automatically closed when the Composable leaves composition (on `onForgotten` or `onAbandoned`).

## KoinNavigationScope

Create a scope tied to a navigation back stack entry:

```kotlin
val appModule = module {
    // Define navigation-scoped dependencies
    navigationScope {
        scoped<ScreenRepository>()
        scoped<ScreenCache>()
        viewModel<ScreenViewModel>()
    }
}

// In your NavHost
NavHost(navController, startDestination = "home") {
    composable("detail/{id}") { backStackEntry ->
        KoinNavigationScope(backStackEntry) {
            DetailScreen()
        }
    }
}

@Composable
fun DetailScreen() {
    // Dependencies scoped to this navigation destination
    val repository = koinInject<ScreenRepository>()
    val viewModel = koinViewModel<ScreenViewModel>()
}
```

**Key characteristics:**
- Scope ID derived from `NavBackStackEntry.id`
- Scope closed only when navigation is abandoned (not on recomposition)
- Perfect for per-screen dependencies

:::info
Requires `koin-compose-viewmodel-navigation` package.
:::

### navigationScope DSL

Define navigation-scoped dependencies in your modules:

```kotlin
val appModule = module {
    // Dependencies scoped to navigation destinations
    navigationScope {
        scoped<ScreenRepository>()
        scoped<ScreenStateHolder>()
        viewModel<ScreenViewModel>()
    }
}
```

This creates a scope qualified by `NavBackStackEntry` for use with `KoinNavigationScope()`.

## UnboundKoinScope

Provide an externally-managed scope without lifecycle binding:

```kotlin
@Composable
fun MyFeature(externalScope: Scope) {
    UnboundKoinScope(scope = externalScope) {
        // Children can access the scope
        val service = koinInject<MyService>()
        FeatureContent()
    }
}
```

:::warning
**Delicate API** - The scope is NOT automatically closed. You must manage the scope lifecycle manually to prevent memory leaks.
:::

**Use cases:**
- Scopes managed by external systems
- Scopes shared across multiple composable trees
- When scope lifecycle doesn't match Composable lifecycle

```kotlin
@Composable
fun MyFeature(externalScope: Scope, onClose: () -> Unit) {
    UnboundKoinScope(scope = externalScope) {
        FeatureContent()

        // Manual cleanup when needed
        DisposableEffect(Unit) {
            onDispose { onClose() }
        }
    }
}
```

## currentKoinScope

Get the current Koin scope from composition:

```kotlin
@Composable
fun MyScreen() {
    val scope = currentKoinScope()

    // Use scope directly
    val service = scope.get<MyService>()
}
```

This retrieves the scope from `LocalKoinScopeContext`. It's the default scope used by `koinInject()`.

## rememberKoinScope

Remember a Koin scope across recompositions with automatic lifecycle management:

```kotlin
@Composable
fun FeatureScreen() {
    val scope = rememberKoinScope(scopeOf<FeatureScope>())

    // Use scope for injection
    val repository = scope.get<FeatureRepository>()

    // When FeatureScreen leaves composition, scope is closed
}
```

## Android-Specific Scopes

### KoinActivityScope

Provide Activity scope to composable hierarchy:

```kotlin
class MainActivity : ComponentActivity(), AndroidScopeComponent {
    override val scope: Scope by activityScope()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            KoinActivityScope {
                // All children access Activity's scope
                MainScreen()
            }
        }
    }
}

@Composable
fun MainScreen() {
    // Resolves from Activity's scope
    val presenter = koinInject<ActivityPresenter>()
}
```

### KoinFragmentScope

Provide Fragment scope to composable hierarchy:

```kotlin
class MyFragment : Fragment(), AndroidScopeComponent {
    override val scope: Scope by fragmentScope()

    override fun onCreateView(...): View {
        return ComposeView(requireContext()).apply {
            setContent {
                KoinFragmentScope {
                    FragmentScreen()
                }
            }
        }
    }
}
```

### koinActivityInject

Inject from Activity scope within any Composable:

```kotlin
val appModule = module {
    scope<MainActivity> {
        scoped<SessionManager>()
    }
}

@Composable
fun DeepNestedScreen() {
    // Resolves from Activity's scope anywhere in the tree
    val sessionManager: SessionManager = koinActivityInject()
}
```

## Scope Comparison

| API | Lifecycle | Use Case |
|-----|-----------|----------|
| `KoinScope` | Composable | Custom scoped composables |
| `KoinNavigationScope` | NavBackStackEntry | Per-destination scopes |
| `UnboundKoinScope` | Manual | External scope providers |
| `KoinActivityScope` | Activity | Activity-wide dependencies |
| `KoinFragmentScope` | Fragment | Fragment-wide dependencies |

## Use Cases

### Per-Screen Navigation Scopes

Each screen gets its own scope:

```kotlin
val appModule = module {
    navigationScope {
        scoped<ScreenStateHolder>()
        viewModel<ScreenViewModel>()
    }
}

NavHost(navController, startDestination = "list") {
    composable("list") { entry ->
        KoinNavigationScope(entry) {
            ListScreen() // Has its own ScreenStateHolder
        }
    }
    composable("detail/{id}") { entry ->
        KoinNavigationScope(entry) {
            DetailScreen() // Has its own ScreenStateHolder
        }
    }
}
```

### Session-Scoped Data

Share data across screens within a session:

```kotlin
val sessionModule = module {
    scope<UserSession> {
        scoped { ShoppingCart() }
        scoped { UserPreferences() }
    }
}

@Composable
fun ShopApp() {
    KoinScope(scopeOf<UserSession>()) {
        NavHost(/*...*/) {
            composable("catalog") { CatalogScreen() }
            composable("cart") { CartScreen() }
        }
    }
}

@Composable
fun CartScreen() {
    // Same cart instance across all screens in session
    val cart = koinInject<ShoppingCart>()
}
```

### Shared ViewModel Scope

Share a ViewModel and its dependencies across related screens:

```kotlin
val appModule = module {
    scope<CheckoutFlow> {
        scoped<CheckoutState>()
        viewModel<CheckoutViewModel>()
    }
}

@Composable
fun CheckoutFlow() {
    KoinScope(scopeOf<CheckoutFlow>()) {
        NavHost(/*...*/) {
            composable("cart") { CartScreen() }
            composable("shipping") { ShippingScreen() }
            composable("payment") { PaymentScreen() }
            composable("confirmation") { ConfirmationScreen() }
        }
    }
}

// All screens share the same CheckoutViewModel instance
@Composable
fun CartScreen() {
    val viewModel = koinViewModel<CheckoutViewModel>()
}
```

## Best Practices

1. **Use `KoinNavigationScope` for per-screen dependencies** - automatic lifecycle with navigation

2. **Prefer managed scopes over `UnboundKoinScope`** - avoid manual cleanup

3. **Define navigation scopes in modules** - cleaner than inline scope creation
   ```kotlin
   module {
       navigationScope {
           scoped<MyRepository>()
       }
   }
   ```

4. **Use `KoinScope` for multi-screen flows** - checkout, onboarding, wizards

5. **Combine with ViewModel for complex state** - scope holds shared state, ViewModel handles UI logic

## Next Steps

- **[Dynamic Modules](/docs/reference/koin-compose/compose-modules)** - Load modules dynamically
- **[Compose Overview](/docs/reference/koin-compose/compose)** - Setup and basic injection
- **[Core Scopes](/docs/reference/koin-core/scopes)** - Scope concepts

---

### Reference Koin Compose Compose Testing (`docs/reference/koin-compose/compose-testing.md`)

---
title: Testing Composables
---

# Testing Composables with Koin

This guide covers testing strategies for Compose applications using Koin, from Android Studio previews to comprehensive unit tests.

## KoinApplicationPreview

Use `KoinApplicationPreview` for Android Studio previews with Koin dependencies:

```kotlin
@Preview
@Composable
fun UserScreenPreview() {
    KoinApplicationPreview(application = {
        modules(module {
            viewModel { UserViewModel(FakeUserRepository()) }
        })
    }) {
        UserScreen()
    }
}
```

### Multiple Previews

```kotlin
@Preview(name = "Light Mode")
@Preview(name = "Dark Mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(name = "Large Font", fontScale = 1.5f)
@Composable
fun UserCardPreviews() {
    KoinApplicationPreview(application = {
        modules(previewModule)
    }) {
        UserCard(user = sampleUser)
    }
}

val previewModule = module {
    single<UserRepository> { FakeUserRepository() }
    viewModel { UserViewModel(get()) }
}
```

### Preview with Different States

```kotlin
@Preview(name = "Loading")
@Composable
fun LoadingPreview() {
    KoinApplicationPreview(application = {
        modules(module {
            viewModel { UserViewModel(LoadingRepository()) }
        })
    }) {
        UserScreen()
    }
}

@Preview(name = "Error")
@Composable
fun ErrorPreview() {
    KoinApplicationPreview(application = {
        modules(module {
            viewModel { UserViewModel(ErrorRepository()) }
        })
    }) {
        UserScreen()
    }
}

@Preview(name = "Success")
@Composable
fun SuccessPreview() {
    KoinApplicationPreview(application = {
        modules(module {
            viewModel { UserViewModel(SuccessRepository(sampleUsers)) }
        })
    }) {
        UserScreen()
    }
}
```

## Unit Testing with ComposeTestRule

### Basic Setup

```kotlin
class UserScreenTest : KoinTest {

    @get:Rule
    val koinTestRule = KoinTestRule.create {
        modules(module {
            single<UserRepository> { FakeUserRepository() }
            viewModel { UserViewModel(get()) }
        })
    }

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun displaysUserList() {
        composeTestRule.setContent {
            UserScreen()
        }

        composeTestRule.onNodeWithText("Alice").assertIsDisplayed()
        composeTestRule.onNodeWithText("Bob").assertIsDisplayed()
    }
}
```

### Testing User Interactions

```kotlin
@Test
fun clickingUserShowsDetails() {
    composeTestRule.setContent {
        UserScreen()
    }

    // Click on a user
    composeTestRule.onNodeWithText("Alice").performClick()

    // Verify navigation or state change
    composeTestRule.onNodeWithText("alice@example.com").assertIsDisplayed()
}

@Test
fun searchFiltersUsers() {
    composeTestRule.setContent {
        UserScreen()
    }

    // Enter search query
    composeTestRule.onNodeWithTag("searchField").performTextInput("Ali")

    // Verify filtered results
    composeTestRule.onNodeWithText("Alice").assertIsDisplayed()
    composeTestRule.onNodeWithText("Bob").assertDoesNotExist()
}
```

### Testing with ViewModel States

```kotlin
@Test
fun showsLoadingIndicator() {
    val loadingRepository = object : UserRepository {
        override suspend fun getUsers(): List<User> {
            delay(Long.MAX_VALUE) // Never completes
            return emptyList()
        }
    }

    startKoin {
        modules(module {
            single<UserRepository> { loadingRepository }
            viewModel { UserViewModel(get()) }
        })
    }

    composeTestRule.setContent {
        UserScreen()
    }

    composeTestRule.onNodeWithTag("loadingIndicator").assertIsDisplayed()

    stopKoin()
}

@Test
fun showsErrorMessage() {
    val errorRepository = object : UserRepository {
        override suspend fun getUsers(): List<User> {
            throw IOException("Network error")
        }
    }

    startKoin {
        modules(module {
            single<UserRepository> { errorRepository }
            viewModel { UserViewModel(get()) }
        })
    }

    composeTestRule.setContent {
        UserScreen()
    }

    composeTestRule.waitUntil(5000) {
        composeTestRule
            .onAllNodesWithText("Network error")
            .fetchSemanticsNodes()
            .isNotEmpty()
    }

    stopKoin()
}
```

## Mocking Dependencies

### Using MockK

```kotlin
class UserScreenMockTest : KoinTest {

    private val mockRepository = mockk<UserRepository>()

    @get:Rule
    val koinTestRule = KoinTestRule.create {
        modules(module {
            single { mockRepository }
            viewModel { UserViewModel(get()) }
        })
    }

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun loadsUsersOnStart() = runTest {
        coEvery { mockRepository.getUsers() } returns listOf(
            User("Test User", "test@example.com")
        )

        composeTestRule.setContent {
            UserScreen()
        }

        composeTestRule.waitUntil(5000) {
            composeTestRule
                .onAllNodesWithText("Test User")
                .fetchSemanticsNodes()
                .isNotEmpty()
        }

        coVerify { mockRepository.getUsers() }
    }

    @Test
    fun refreshCallsRepository() = runTest {
        coEvery { mockRepository.getUsers() } returns emptyList()

        composeTestRule.setContent {
            UserScreen()
        }

        // Trigger refresh
        composeTestRule.onNodeWithTag("refreshButton").performClick()

        coVerify(exactly = 2) { mockRepository.getUsers() }
    }
}
```

### Using Fake Implementations

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Testing Navigation

```kotlin
class NavigationTest : KoinTest {

    @get:Rule
    val koinTestRule = KoinTestRule.create {
        modules(testModule)
    }

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun navigatesToDetail() {
        lateinit var navController: NavHostController

        composeTestRule.setContent {
            navController = rememberNavController()
            AppNavigation(navController)
        }

        // Navigate to detail
        composeTestRule.onNodeWithText("View Details").performClick()

        // Verify navigation
        assertEquals("detail/123", navController.currentDestination?.route)
    }

    @Test
    fun backNavigationWorks() {
        lateinit var navController: NavHostController

        composeTestRule.setContent {
            navController = rememberNavController()
            AppNavigation(navController)
        }

        // Navigate forward
        composeTestRule.onNodeWithText("View Details").performClick()

        // Navigate back
        composeTestRule.onNodeWithContentDescription("Back").performClick()

        // Verify back at home
        assertEquals("home", navController.currentDestination?.route)
    }
}
```

## Testing with Coroutines

```kotlin
class CoroutineTest : KoinTest {

    @get:Rule
    val koinTestRule = KoinTestRule.create {
        modules(module {
            single<UserRepository> { FakeUserRepository() }
            viewModel { UserViewModel(get()) }
        })
    }

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun asyncOperationCompletes() = runTest {
        composeTestRule.setContent {
            UserScreen()
        }

        // Wait for async operation
        composeTestRule.waitUntil(timeoutMillis = 5000) {
            composeTestRule
                .onAllNodesWithTag("userList")
                .fetchSemanticsNodes()
                .isNotEmpty()
        }

        // Verify result
        composeTestRule.onNodeWithText("Alice").assertIsDisplayed()
    }
}
```

## Multiplatform Testing

For Compose Multiplatform, create expect/actual test helpers:

```kotlin
// commonTest
expect fun createTestComposeRule(): ComposeTestRule

// androidTest
actual fun createTestComposeRule(): ComposeTestRule = createComposeRule()

// Common test
class CommonUserScreenTest : KoinTest {

    @get:Rule
    val composeTestRule = createTestComposeRule()

    @Test
    fun displaysContent() {
        startKoin {
            modules(testModule)
        }

        composeTestRule.setContent {
            UserScreen()
        }

        // Assertions...

        stopKoin()
    }
}
```

## Best Practices

1. **Use KoinTestRule** - handles setup/teardown automatically
   ```kotlin
   @get:Rule
   val koinTestRule = KoinTestRule.create { modules(testModule) }
   ```

2. **Prefer Fakes over Mocks** - more predictable, easier to understand

3. **Test one behavior at a time** - focused tests are easier to maintain

4. **Use semantic test tags** - make tests resilient to UI changes
   ```kotlin
   Modifier.testTag("submitButton")
   ```

5. **Wait for async operations** - use `waitUntil` for async state
   ```kotlin
   composeTestRule.waitUntil(5000) { condition }
   ```

6. **Clean up Koin** - if not using rules, call `stopKoin()` in `@After`

## Next Steps

- **[Compose Overview](/docs/reference/koin-compose/compose)** - Setup and basic injection
- **[Testing Reference](/docs/reference/koin-test/testing)** - General Koin testing
- **[Module Verification](/docs/reference/koin-test/verify)** - Verify module configuration

---

### Reference Koin Compose Compose Viewmodel (`docs/reference/koin-compose/compose-viewmodel.md`)

---
title: ViewModel in Compose
---

# ViewModel in Compose

Koin provides several APIs for injecting ViewModels in Compose applications. This guide covers all ViewModel injection patterns.

:::info
For declaring ViewModels in modules, see [Core ViewModel](/docs/reference/koin-core/viewmodel). This page focuses on retrieving ViewModels in Compose.
:::

## Setup

```kotlin
// Compose Multiplatform (or Android)
implementation("io.insert-koin:koin-compose-viewmodel:$koin_version")

// Android convenience (includes koin-compose + koin-compose-viewmodel)
implementation("io.insert-koin:koin-androidx-compose:$koin_version")

// With Navigation integration
implementation("io.insert-koin:koin-compose-viewmodel-navigation:$koin_version")
```

:::info
All ViewModel APIs are in `koin-compose-viewmodel`. The `koin-androidx-compose` package includes it automatically.
:::

## Declaring ViewModels

### Compiler Plugin DSL

```kotlin
class UserViewModel(
    private val repository: UserRepository
) : ViewModel()

val appModule = module {
    viewModel<UserViewModel>()
}
```

### Annotations

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

### Classic DSL

```kotlin
val appModule = module {
    viewModelOf(::UserViewModel)
    // or with lambda
    viewModel { UserViewModel(get()) }
}
```

## ViewModel Injection APIs

### koinViewModel() - Basic Injection

The primary API for injecting ViewModels in Compose:

```kotlin
@Composable
fun UserScreen() {
    val viewModel = koinViewModel<UserViewModel>()
    // Use viewModel...
}
```

**Best practice** - inject as default parameter for testability:

```kotlin
@Composable
fun UserScreen(
    viewModel: UserViewModel = koinViewModel()
) {
    val state by viewModel.state.collectAsState()
    // UI...
}
```

### koinNavViewModel() - With Navigation Arguments

When using Navigation Compose, use `koinNavViewModel()` to automatically receive navigation arguments via `SavedStateHandle`:

```kotlin
// Route with arguments
NavHost(navController, startDestination = "list") {
    composable("detail/{itemId}") { backStackEntry ->
        DetailScreen()
    }
}

// ViewModel receives arguments automatically
class DetailViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    val itemId: String = savedStateHandle["itemId"] ?: ""
}

@Composable
fun DetailScreen(
    viewModel: DetailViewModel = koinNavViewModel()
) {
    // viewModel.itemId is populated from navigation arguments
}
```

### koinActivityViewModel() - Activity-Scoped (Android)

Share a ViewModel across all Composables within the same Activity:

```kotlin
@Composable
fun ScreenA() {
    // Same instance across Activity
    val sharedVM = koinActivityViewModel<SharedViewModel>()
}

@Composable
fun ScreenB() {
    // Same instance as ScreenA
    val sharedVM = koinActivityViewModel<SharedViewModel>()
}
```

:::note
Available in `koin-androidx-compose` starting from version 4.1.
:::

### sharedKoinViewModel() - Navigation Graph Scoped

Share a ViewModel within a navigation graph (experimental):

```kotlin
navigation<Route.BookGraph>(startDestination = Route.BookList) {
    composable<Route.BookList> { backStackEntry ->
        val sharedVM = backStackEntry.sharedKoinViewModel<BookSharedViewModel>(navController)
        BookListScreen(sharedVM)
    }
    composable<Route.BookDetail> { backStackEntry ->
        // Same instance within BookGraph
        val sharedVM = backStackEntry.sharedKoinViewModel<BookSharedViewModel>(navController)
        BookDetailScreen(sharedVM)
    }
}
```

## ViewModel with Parameters

### Using @InjectedParam

Mark runtime parameters with `@InjectedParam`:

```kotlin
class DetailViewModel(
    @InjectedParam val itemId: String,
    private val repository: DetailRepository
) : ViewModel()

// Compiler Plugin DSL
val appModule = module {
    viewModel<DetailViewModel>()
}
```

Inject with parameters:

```kotlin
@Composable
fun DetailScreen(itemId: String) {
    val viewModel = koinViewModel<DetailViewModel> {
        parametersOf(itemId)
    }
}
```

Use the `key` parameter to create distinct ViewModel instances per identifier (e.g., navigation arguments):

```kotlin
@Composable
fun DetailScreen(newsId: String) {
    val viewModel = koinViewModel<DetailViewModel>(key = newsId) {
        parametersOf(newsId)
    }
}
```

The `key` ensures each unique `newsId` gets its own ViewModel instance, which is important for screens that can be on the back stack with different arguments.

### Classic DSL with Parameters

```kotlin
val appModule = module {
    viewModel { params ->
        DetailViewModel(
            itemId = params.get(),
            repository = get()
        )
    }
}
```

## SavedStateHandle

Koin automatically provides `SavedStateHandle` to ViewModels:

```kotlin
@KoinViewModel
class MyViewModel(
    private val handle: SavedStateHandle,
    private val repository: UserRepository
) : ViewModel() {
    // Access navigation arguments
    val userId: String? = handle["userId"]

    // Persist state across process death
    var query by handle.saveable { mutableStateOf("") }
}
```

```kotlin
val appModule = module {
    viewModel<MyViewModel>()  // SavedStateHandle injected automatically
}
```

:::info
`SavedStateHandle` is injected from either ViewModel `CreationExtras` or Navigation `BackStackEntry`, depending on context.
:::

## ViewModel Scope

Scope dependencies to ViewModel lifecycle using `viewModelScope`:

### Compiler Plugin DSL

```kotlin
val appModule = module {
    viewModelScope {
        scoped<UserCache>()
        scoped<UserRepository>()
        viewModel<UserViewModel>()
    }
}
```

### Annotations

```kotlin
@ViewModelScope
class UserCache

@ViewModelScope
class UserRepository(private val cache: UserCache)

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

### Classic DSL

```kotlin
val appModule = module {
    viewModelScope {
        scoped { UserCache() }
        scoped { UserRepository(get()) }
        viewModel { UserViewModel(get()) }
    }
}
```

:::caution Requires the `viewModelScopeFactory()` option
Declaring a ViewModel inside `viewModelScope { }` requires enabling `options(viewModelScopeFactory())` in your Koin configuration — otherwise `koinViewModel()` fails with `No definition found … on scope '['_root_']'`. See [ViewModel Scope](/docs/reference/koin-core/viewmodel#viewmodel-scope) for details.
:::

## Quick Reference

| API | Use Case | Package |
|-----|----------|---------|
| `koinViewModel()` | Basic ViewModel injection | `koin-compose-viewmodel` |
| `koinNavViewModel()` | With Navigation arguments | `koin-compose-viewmodel-navigation` |
| `koinActivityViewModel()` | Shared across Activity (Android) | `koin-androidx-compose` |
| `sharedKoinViewModel()` | Shared within nav graph | `koin-compose-viewmodel-navigation` |

## Best Practices

1. **Inject as default parameters** - enables testing without Koin
   ```kotlin
   @Composable
   fun MyScreen(viewModel: MyViewModel = koinViewModel())
   ```

2. **Use koinNavViewModel() with Navigation** - automatic argument handling

3. **Prefer viewModelScope for ViewModel-specific dependencies** - clean lifecycle management

4. **Don't inject ViewModels in callbacks** - inject at Composable level
   ```kotlin
   // Bad
   Button(onClick = { val vm = koinViewModel<MyVM>() })

   // Good
   val vm = koinViewModel<MyVM>()
   Button(onClick = { vm.doSomething() })
   ```

## Next Steps

- **[Compose Lifecycle](/docs/reference/koin-compose/compose-lifecycle)** - State and recomposition
- **[Core ViewModel](/docs/reference/koin-core/viewmodel)** - ViewModel declaration DSL
- **[Android ViewModel](/docs/reference/koin-android/viewmodel)** - Android-specific features

---

### Reference Koin Compose Compose (`docs/reference/koin-compose/compose.md`)

---
title: Koin for Compose
---

# Koin for Compose

Koin provides full support for Jetpack Compose and Compose Multiplatform applications with dedicated packages for dependency injection.

## Packages Overview

| Package | Use Case |
|---------|----------|
| `koin-compose` | Base Compose API (multiplatform) |
| `koin-compose-viewmodel` | ViewModel injection (multiplatform) |
| `koin-compose-viewmodel-navigation` | ViewModel + Navigation 2.x |
| `koin-compose-navigation3` | Navigation 3 integration (multiplatform) |
| `koin-androidx-compose` | Android convenience (includes koin-compose + koin-compose-viewmodel) |

:::info
All Compose APIs are defined in `koin-compose` and `koin-compose-viewmodel`. The `koin-androidx-compose` package is a convenience wrapper that includes both for Android projects.
:::

### Which Package Should I Use?

**For Android-only projects:**
```kotlin
// Option 1: Android convenience package (includes koin-compose + koin-compose-viewmodel)
implementation("io.insert-koin:koin-androidx-compose:$koin_version")

// Option 2: Use multiplatform packages directly
implementation("io.insert-koin:koin-compose:$koin_version")
implementation("io.insert-koin:koin-compose-viewmodel:$koin_version")

// Optional: Navigation integration
implementation("io.insert-koin:koin-androidx-compose-navigation:$koin_version")
```

**For Compose Multiplatform projects:**
```kotlin
commonMain.dependencies {
    implementation("io.insert-koin:koin-compose:$koin_version")
    implementation("io.insert-koin:koin-compose-viewmodel:$koin_version")

    // Optional: Navigation integration
    implementation("io.insert-koin:koin-compose-viewmodel-navigation:$koin_version")
}
```

## Platform Support

| Platform | Compose Type | Status |
|----------|-------------|--------|
| Android | Jetpack Compose | Full support |
| iOS | Compose Multiplatform | Full support |
| Desktop | Compose Desktop | Full support |
| Web | Compose for Web | Experimental |

## Starting Koin

### Option 1: startKoin (Android only or External Setup)

Initialize Koin outside Compose for full control:

```kotlin
// Android Application class
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyApplication)
            androidLogger()
            modules(appModule)
        }
    }
}

// Compose UI uses Koin automatically
@Composable
fun App() {
    val viewModel = koinViewModel<MyViewModel>()
}
```

**Use when:** You need full control over Koin lifecycle, custom configuration, or integration with other frameworks.

### Option 2: KoinApplication (Compose-Managed)

Let Compose handle Koin setup automatically:

```kotlin
@Composable
fun App() {
    KoinApplication(configuration = koinConfiguration {
        modules(appModule)
    }) {
        MyScreen()
    }
}
```

**Advantages:**
- No external setup required (no Application class needed)
- Android Context injected automatically
- Handles start/stop based on composition lifecycle
- Manages configuration changes on Android

**Use when:** You want the simplest setup with less control.

Automatically injects `androidContext` and `androidLogger` on Android.

:::note
`KoinMultiplatformApplication` is deprecated. Use `KoinApplication` with `koinConfiguration` instead.
:::

## Basic Injection

### koinInject() - Get Dependencies

Inject any Koin-managed dependency:

```kotlin
@Composable
fun UserScreen() {
    val repository = koinInject<UserRepository>()
    // Use repository...
}
```

**Best practice** - inject as default parameter:

```kotlin
@Composable
fun UserScreen(
    repository: UserRepository = koinInject()
) {
    // Testable without Koin
}
```

### koinViewModel() - Get ViewModels

Inject ViewModels with proper lifecycle management:

```kotlin
@Composable
fun UserScreen() {
    val viewModel = koinViewModel<UserViewModel>()
    val state by viewModel.state.collectAsState()
}
```

:::info
See [ViewModel in Compose](/docs/reference/koin-compose/compose-viewmodel) for all ViewModel APIs.
:::

### With Parameters

Pass runtime parameters:

```kotlin
@Composable
fun DetailScreen(itemId: String) {
    val viewModel = koinViewModel<DetailViewModel> {
        parametersOf(itemId)
    }
}
```

For better performance with frequent recomposition:

```kotlin
@Composable
fun DetailScreen(itemId: String) {
    val viewModel = koinViewModel<DetailViewModel>(
        parameters = parametersOf(itemId)
    )
}
```

## Defining Modules

### Compiler Plugin DSL

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

### Annotations

```kotlin
@Singleton
class UserRepository

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

### Classic DSL

```kotlin
val appModule = module {
    singleOf(::UserRepository)
    viewModelOf(::UserViewModel)
}
```

## Quick Reference

| Function | Purpose |
|----------|---------|
| `koinInject<T>()` | Inject any dependency |
| `koinViewModel<T>()` | Inject ViewModel |
| `koinNavViewModel<T>()` | ViewModel with Navigation args |
| `koinActivityViewModel<T>()` | Activity-scoped ViewModel (Android) |
| `rememberKoinModules()` | Load modules with composition |
| `KoinScope {}` | Create scoped context |

## Documentation

| Topic | Description |
|-------|-------------|
| **[ViewModel](/docs/reference/koin-compose/compose-viewmodel)** | All ViewModel injection APIs |
| **[Lifecycle & State](/docs/reference/koin-compose/compose-lifecycle)** | Recomposition, state, side effects |
| **[Dynamic Modules](/docs/reference/koin-compose/compose-modules)** | rememberKoinModules, lazy loading |
| **[Scopes](/docs/reference/koin-compose/compose-scopes)** | KoinScope, KoinNavigationScope, UnboundKoinScope |
| **[Testing](/docs/reference/koin-compose/compose-testing)** | Previews, unit tests |
| **[Isolated Context](/docs/reference/koin-compose/isolated-context)** | SDK isolation |
| **[Navigation 3](/docs/reference/koin-compose/navigation3)** | Type-safe navigation (multiplatform) |

## Related

- **[Core ViewModel](/docs/reference/koin-core/viewmodel)** - ViewModel declaration DSL
- **[Android ViewModel](/docs/reference/koin-android/viewmodel)** - Android-specific features
- **[KMP Setup](/docs/reference/koin-core/kmp-setup)** - Multiplatform configuration

---

### Reference Koin Compose Isolated Context (`docs/reference/koin-compose/isolated-context.md`)

---
title: Isolated Context
---

# Isolated Context in Compose

Koin's isolated context allows you to run a separate Koin instance that doesn't interfere with the host application's Koin configuration. This is essential for SDKs, libraries, and white-label applications.

## Use Cases

- **SDK Development** - Your SDK has its own dependencies without affecting the host app
- **White-Label Apps** - Multiple app variants with different configurations
- **Testing** - Isolated test configurations
- **Feature Modules** - Self-contained feature modules with their own DI

## Creating an Isolated Context

### Define the Context Holder

Create an object to hold your isolated Koin instance:

```kotlin
object MySDKKoinContext {
    val koinApp = koinApplication {
        // Your SDK modules
        modules(
            sdkCoreModule,
            sdkNetworkModule,
            sdkRepositoryModule
        )
    }

    // Convenience accessor
    val koin: Koin get() = koinApp.koin
}
```

### SDK Module Example

```kotlin
val sdkCoreModule = module {
    single<SDKConfiguration>()
    single<SDKLogger>()
}

val sdkNetworkModule = module {
    single<SDKApiClient>()
    single<SDKAuthManager>()
}

val sdkRepositoryModule = module {
    single<SDKDataRepository>()
}
```

## Using with Compose

### KoinIsolatedContext

Wrap your SDK's Compose UI with `KoinIsolatedContext`:

```kotlin
@Composable
fun MySDKScreen() {
    KoinIsolatedContext(context = MySDKKoinContext.koinApp) {
        // All Koin APIs use the isolated context
        SDKContent()
    }
}

@Composable
private fun SDKContent() {
    // These resolve from the isolated context
    val config = koinInject<SDKConfiguration>()
    val viewModel = koinViewModel<SDKViewModel>()

    Column {
        Text("SDK Version: ${config.version}")
        SDKFeatureScreen(viewModel)
    }
}
```

### Nested Contexts

You can nest isolated contexts:

```kotlin
@Composable
fun HostApp() {
    // Host app's Koin context (from startKoin)
    val hostService = koinInject<HostService>()

    Column {
        Text("Host App")

        // SDK uses its own isolated context
        KoinIsolatedContext(context = MySDKKoinContext.koinApp) {
            MySDKScreen()
        }

        // Back to host context
        AnotherHostScreen()
    }
}
```

## Lifecycle Management

### Manual Initialization

Initialize your SDK context when needed:

```kotlin
object MySDK {
    private var _koinContext: KoinApplication? = null
    val koinContext: KoinApplication
        get() = _koinContext ?: error("SDK not initialized")

    fun initialize(config: SDKConfig) {
        _koinContext = koinApplication {
            modules(
                module {
                    single { config }
                },
                sdkCoreModule,
                sdkNetworkModule
            )
        }
    }

    fun shutdown() {
        _koinContext?.close()
        _koinContext = null
    }
}
```

### Usage with Manual Lifecycle

```kotlin
// Host app initializes SDK
class HostApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        // Host app's Koin
        startKoin {
            androidContext(this@HostApplication)
            modules(hostAppModule)
        }

        // SDK's isolated Koin
        MySDK.initialize(SDKConfig(apiKey = "xxx"))
    }

    override fun onTerminate() {
        super.onTerminate()
        MySDK.shutdown()
    }
}

// In Compose
@Composable
fun SDKFeature() {
    KoinIsolatedContext(context = MySDK.koinContext) {
        SDKScreen()
    }
}
```

## Accessing Both Contexts

Sometimes you need to access both host and SDK dependencies:

```kotlin
@Composable
fun BridgeScreen() {
    // Get from host context (outside KoinIsolatedContext)
    val hostAnalytics = koinInject<HostAnalytics>()

    KoinIsolatedContext(context = MySDKKoinContext.koinApp) {
        // Get from SDK context (inside KoinIsolatedContext)
        val sdkService = koinInject<SDKService>()

        // Use both
        SDKFeature(
            service = sdkService,
            onEvent = { hostAnalytics.track(it) }
        )
    }
}
```

## Complete SDK Example

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Best Practices

1. **Initialize early** - Set up isolated context before Compose renders

2. **Clean up resources** - Call `close()` on the KoinApplication when done

3. **Don't share instances** - Keep SDK and host dependencies separate

4. **Use interface boundaries** - Communicate via callbacks or interfaces, not shared Koin instances

5. **Document initialization** - Make SDK setup requirements clear to host app developers

## Next Steps

- **[Compose Overview](/docs/reference/koin-compose/compose)** - Basic Compose setup
- **[Context Isolation](/docs/reference/koin-core/context-isolation)** - Core isolation concepts
- **[Scopes](/docs/reference/koin-compose/compose-scopes)** - Scope management in Compose

---

### Reference Koin Compose Navigation3 (`docs/reference/koin-compose/navigation3.md`)

---
title: Navigation 3
---

# Navigation 3

Koin provides integration with [AndroidX Navigation 3](https://developer.android.com/guide/navigation/navigation-3) for type-safe, multiplatform navigation with dependency injection.

## What is Navigation 3?

Navigation 3 is Jetpack's new navigation library designed specifically for Compose:

- **Full back stack control** - Navigate by adding/removing items from a list
- **Type-safe routes** - Routes are Kotlin classes with `@Serializable`
- **Adaptive layouts** - Display multiple destinations simultaneously (list-detail)
- **Automatic animations** - Built-in transition support

## Setup

### Multiplatform Projects

```kotlin
// shared/build.gradle.kts
commonMain.dependencies {
    implementation("io.insert-koin:koin-compose-navigation3:$koin_version")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version")
}
```

### Android-only Projects

```kotlin
dependencies {
    implementation("io.insert-koin:koin-compose-navigation3:$koin_version")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version")
}
```

Apply the serialization plugin:

```kotlin
plugins {
    kotlin("plugin.serialization")
}
```

### Platform Support

| Platform | Status |
|----------|--------|
| Android | Full support |
| iOS | Full support |
| Desktop | Full support |
| Web | Full support |

## Core Concepts

### Routes as Kotlin Classes

Define type-safe routes using `@Serializable`:

```kotlin
@Serializable
data object HomeRoute

@Serializable
data object ProfileRoute

@Serializable
data class DetailRoute(val itemId: String)

@Serializable
data class SettingsRoute(val section: String? = null)
```

### Back Stack

Navigation 3 uses a simple list-based back stack:

```kotlin
// Basic back stack
val backStack = remember { mutableStateListOf<Any>(HomeRoute) }

// Persistent back stack (survives config changes)
val backStack = rememberNavBackStack(HomeRoute)

// Navigate forward
backStack.add(DetailRoute("123"))

// Navigate back
backStack.removeLastOrNull()
```

### NavDisplay

`NavDisplay` renders the back stack with animations:

```kotlin
NavDisplay(
    backStack = backStack,
    onBack = { backStack.removeLastOrNull() },
    entryProvider = { route -> /* NavEntry */ }
)
```

## Koin Integration

### Declaring Navigation Entries

Use the `navigation<T>` DSL in your modules:

```kotlin
val appModule = module {
    // Dependencies
    single<ApiClient>()
    viewModel<HomeViewModel>()
    viewModel<DetailViewModel>()

    // Navigation entries with Koin injection
    navigation<HomeRoute> { route ->
        HomeScreen(viewModel = koinViewModel())
    }

    navigation<DetailRoute> { route ->
        DetailScreen(
            itemId = route.itemId,
            viewModel = koinViewModel { parametersOf(route.itemId) }
        )
    }

    navigation<ProfileRoute> { route ->
        ProfileScreen(viewModel = koinViewModel())
    }
}
```

### Using koinEntryProvider

Retrieve all navigation entries from Koin:

```kotlin
@Composable
fun App() {
    val backStack = rememberNavBackStack(HomeRoute)
    val entryProvider = koinEntryProvider<Any>()

    NavDisplay(
        backStack = backStack,
        onBack = { backStack.removeLastOrNull() },
        entryProvider = entryProvider
    )
}
```

:::tip Pass your route type when `NavDisplay` is typed
`koinEntryProvider<T>()` is generic — `T` is the route type. If you leave it as `koinEntryProvider<Any>()` but `NavDisplay` becomes typed (for example via a typed `SceneStrategy` like `rememberSupportingPaneSceneStrategy<Route>()`), the compiler infers `NavDisplay<Route>` and expects `(Route) -> NavEntry<Route>`, so an `Any`-typed provider won't match:

```
Argument type mismatch: actual type is 'Function1<Any, NavEntry<Any>>',
but 'Function1<Route, NavEntry<Route>>' was expected.
```

Pass your route type to match:

```kotlin
val sceneStrategy = rememberSupportingPaneSceneStrategy<Route>()
val entryProvider = koinEntryProvider<Route>()   // (Route) -> NavEntry<Route>

NavDisplay(
    backStack = backStack,
    onBack = onBack,
    sceneStrategy = sceneStrategy,
    entryProvider = entryProvider,               // ✅ matches NavDisplay<Route>
)
```

(equivalently, `val entryProvider: EntryProvider<Route> = koinEntryProvider()` — the type argument is inferred from the expected type.)
:::

### Complete Example

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Scoped Navigation

Declare navigation entries within Koin scopes:

```kotlin
val appModule = module {
    // Activity-retained scope (survives config changes)
    activityRetainedScope {
        scoped { UserSession() }
        viewModel<ProfileViewModel>()

        navigation<ProfileRoute> { route ->
            ProfileScreen(viewModel = koinViewModel())
        }
    }

    // Custom scope
    scope<CheckoutFlow> {
        scoped { CheckoutState() }
        viewModel<CheckoutViewModel>()

        navigation<CartRoute> { route ->
            CartScreen(viewModel = koinViewModel())
        }

        navigation<PaymentRoute> { route ->
            PaymentScreen(viewModel = koinViewModel())
        }
    }
}
```

## ViewModel Integration

### With Navigation Arguments

Pass route data to ViewModels:

```kotlin
@Serializable
data class DetailRoute(val itemId: String, val fromSearch: Boolean = false)

class DetailViewModel(
    val route: DetailRoute,
    private val repository: Repository
) : ViewModel() {
    val item = repository.getItem(route.itemId)
}

val appModule = module {
    viewModelOf(::DetailViewModel)

    navigation<DetailRoute> { route ->
        DetailScreen(
            viewModel = koinViewModel { parametersOf(route) }
        )
    }
}
```

### With Entry Decorators

Use decorators for ViewModel state retention:

```kotlin
NavDisplay(
    backStack = backStack,
    onBack = { backStack.removeLastOrNull() },
    entryDecorators = listOf(
        rememberSaveableStateHolderNavEntryDecorator(),
        rememberViewModelStoreNavEntryDecorator()
    ),
    entryProvider = entryProvider {
        entry<DetailRoute> { route ->
            val viewModel = koinViewModel<DetailViewModel> {
                parametersOf(route)
            }
            DetailScreen(viewModel)
        }
    }
)
```

## Animations

### Default Transitions

```kotlin
NavDisplay(
    backStack = backStack,
    onBack = { backStack.removeLastOrNull() },
    entryProvider = entryProvider,
    // Forward navigation animation
    transitionSpec = {
        slideInHorizontally(initialOffsetX = { it }) togetherWith
        slideOutHorizontally(targetOffsetX = { -it })
    },
    // Back navigation animation
    popTransitionSpec = {
        slideInHorizontally(initialOffsetX = { -it }) togetherWith
        slideOutHorizontally(targetOffsetX = { it })
    }
)
```

### Per-Route Animations

```kotlin
navigation<ModalRoute>(
    metadata = NavDisplay.transitionSpec {
        slideInVertically(initialOffsetY = { it }) togetherWith
        ExitTransition.KeepUntilTransitionsFinished
    } + NavDisplay.popTransitionSpec {
        EnterTransition.None togetherWith
        slideOutVertically(targetOffsetY = { it })
    }
) { route ->
    ModalScreen()
}
```

## Adaptive Layouts

### List-Detail Pattern

Use scene strategies for adaptive layouts:

```kotlin
@Composable
fun App() {
    val backStack = rememberNavBackStack(ConversationList)
    val listDetailStrategy = rememberListDetailSceneStrategy<Any>()

    NavDisplay(
        backStack = backStack,
        onBack = { backStack.removeLastOrNull() },
        sceneStrategy = listDetailStrategy,
        entryProvider = entryProvider {
            entry<ConversationList>(
                metadata = ListDetailSceneStrategy.listPane()
            ) {
                ConversationListScreen()
            }

            entry<ConversationDetail>(
                metadata = ListDetailSceneStrategy.detailPane()
            ) { route ->
                ConversationDetailScreen(route.id)
            }
        }
    )
}
```

### With Koin Modules

```kotlin
val appModule = module {
    navigation<ConversationList>(
        metadata = ListDetailSceneStrategy.listPane()
    ) {
        ConversationListScreen(
            onItemClick = { get<Navigator>().goTo(it) }
        )
    }

    navigation<ConversationDetail>(
        metadata = ListDetailSceneStrategy.detailPane()
    ) { route ->
        ConversationDetailScreen(route.id)
    }
}
```

## Android Extensions

### Lazy Entry Provider

```kotlin
class MainActivity : ComponentActivity() {
    // Lazy initialization
    private val entryProvider by entryProvider<Any>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val backStack = rememberNavBackStack(HomeRoute)

            NavDisplay(
                backStack = backStack,
                onBack = { backStack.removeLastOrNull() },
                entryProvider = entryProvider
            )
        }
    }
}
```

### Eager Entry Provider

```kotlin
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val entryProvider = getEntryProvider<Any>()

        setContent {
            NavDisplay(
                backStack = backStack,
                entryProvider = entryProvider,
                onBack = { backStack.removeLastOrNull() }
            )
        }
    }
}
```

## API Reference

### DSL Functions

| Function | Description |
|----------|-------------|
| `Module.navigation<T> { }` | Declare navigation entry at module level |
| `ScopeDSL.navigation<T> { }` | Declare navigation entry within a scope |

### Composable Functions

| Function | Description |
|----------|-------------|
| `koinEntryProvider<T>()` | Get aggregated entry provider from Koin |

### Android Extensions

| Function | Description |
|----------|-------------|
| `entryProvider<T>()` | Lazy entry provider delegate |
| `getEntryProvider<T>()` | Eager entry provider |

## Migration from Navigation 2.x

### Before (Navigation 2.x)

```kotlin
NavHost(navController, startDestination = "home") {
    composable("home") {
        HomeScreen(viewModel = koinViewModel())
    }
    composable("detail/{id}") { backStackEntry ->
        val id = backStackEntry.arguments?.getString("id")
        DetailScreen(id = id, viewModel = koinViewModel())
    }
}
```

### After (Navigation 3)

```kotlin
// Type-safe routes
@Serializable data object HomeRoute
@Serializable data class DetailRoute(val id: String)

// Module declaration
val appModule = module {
    navigation<HomeRoute> { HomeScreen(viewModel = koinViewModel()) }
    navigation<DetailRoute> { route ->
        DetailScreen(id = route.id, viewModel = koinViewModel())
    }
}

// Usage
val backStack = rememberNavBackStack(HomeRoute)
NavDisplay(
    backStack = backStack,
    onBack = { backStack.removeLastOrNull() },
    entryProvider = koinEntryProvider()
)
```

## Resources

- [Navigation 3 Official Guide](https://developer.android.com/guide/navigation/navigation-3)
- [Nav3 Recipes Repository](https://github.com/android/nav3-recipes)
- [Koin Compose Documentation](/docs/reference/koin-compose/compose)

---

### Reference Koin Core Advanced Patterns (`docs/reference/koin-core/advanced-patterns.md`)

---
title: Advanced Patterns
---

# Advanced Patterns

This guide covers advanced dependency injection patterns for complex scenarios.

## External Library Binding

For third-party libraries where you can't add annotations, use builder functions with `create()`:

```kotlin
// Builder functions - Koin resolves parameters automatically
fun createOkHttpClient(): OkHttpClient =
    OkHttpClient.Builder()
        .connectTimeout(30, TimeUnit.SECONDS)
        .build()

fun createRetrofit(client: OkHttpClient): Retrofit =
    Retrofit.Builder()
        .baseUrl("https://api.example.com/")
        .client(client)
        .build()

fun createApiService(retrofit: Retrofit): ApiService =
    retrofit.create(ApiService::class.java)

val networkModule = module {
    single { create(::createOkHttpClient) }
    single { create(::createRetrofit) }
    single { create(::createApiService) }
}
```

Or with Annotations using `@Module` functions:

```kotlin
@Module
class NetworkModule {
    @Single
    fun provideOkHttpClient(): OkHttpClient =
        OkHttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS).build()

    @Single
    fun provideRetrofit(client: OkHttpClient): Retrofit =
        Retrofit.Builder().baseUrl("https://api.example.com/").client(client).build()

    @Single
    fun provideApiService(retrofit: Retrofit): ApiService =
        retrofit.create(ApiService::class.java)
}
```

## Collections of Dependencies

### Multiple Implementations

Aggregate multiple implementations of an interface using qualifiers:

```kotlin
interface PaymentProcessor {
    fun process(amount: Double): Boolean
    fun getName(): String
}

class CreditCardProcessor : PaymentProcessor { ... }
class PayPalProcessor : PaymentProcessor { ... }
class CryptoProcessor : PaymentProcessor { ... }
```

#### Compiler Plugin DSL

Use `@Named` qualifier annotations on classes:

```kotlin
@Named("creditCard")
class CreditCardProcessor : PaymentProcessor { ... }

@Named("paypal")
class PayPalProcessor : PaymentProcessor { ... }

@Named("crypto")
class CryptoProcessor : PaymentProcessor { ... }

class PaymentManager(
    @Named("creditCard") creditCard: PaymentProcessor,
    @Named("paypal") paypal: PaymentProcessor,
    @Named("crypto") crypto: PaymentProcessor
) {
    private val processors = listOf(creditCard, paypal, crypto)
}
```

```kotlin
val paymentModule = module {
    single<CreditCardProcessor>()
    single<PayPalProcessor>()
    single<CryptoProcessor>()
    single<PaymentManager>()
}
```

#### Annotations

```kotlin
@Module
class PaymentModule {
    @Single
    @Named("creditCard")
    fun provideCreditCard(): PaymentProcessor = CreditCardProcessor()

    @Single
    @Named("paypal")
    fun providePayPal(): PaymentProcessor = PayPalProcessor()

    @Single
    @Named("crypto")
    fun provideCrypto(): PaymentProcessor = CryptoProcessor()

    @Single
    fun providePaymentManager(
        @Named("creditCard") creditCard: PaymentProcessor,
        @Named("paypal") paypal: PaymentProcessor,
        @Named("crypto") crypto: PaymentProcessor
    ): PaymentManager = PaymentManager(listOf(creditCard, paypal, crypto))
}
```

#### Classic DSL

```kotlin
val paymentModule = module {
    single(named("creditCard")) { CreditCardProcessor() }
    single(named("paypal")) { PayPalProcessor() }
    single(named("crypto")) { CryptoProcessor() }

    single {
        PaymentManager(
            listOf(
                get(named("creditCard")),
                get(named("paypal")),
                get(named("crypto"))
            )
        )
    }
}
```

## Generic Types

Koin preserves generic type information:

```kotlin
interface Repository<T> {
    suspend fun get(id: String): T
    suspend fun save(item: T)
}

@Singleton
class UserRepository : Repository<User> { ... }

@Singleton
class ProductRepository : Repository<Product> { ... }
```

```kotlin
// DSL
val repositoryModule = module {
    single<Repository<User>> { UserRepository() }
    single<Repository<Product>> { ProductRepository() }
}

// Injection - types are distinct
val userRepo: Repository<User> = get()
val productRepo: Repository<Product> = get()
```

## Provider Pattern

Create factories for runtime instances when you need to create objects with runtime parameters:

```kotlin
@Factory
class DialogFactory(private val context: Context) {
    fun createConfirmDialog(title: String, onConfirm: () -> Unit): AlertDialog =
        AlertDialog.Builder(context)
            .setTitle(title)
            .setPositiveButton("OK") { _, _ -> onConfirm() }
            .create()

    fun createErrorDialog(message: String): AlertDialog =
        AlertDialog.Builder(context)
            .setTitle("Error")
            .setMessage(message)
            .create()
}

// Usage
class MyScreen(private val dialogFactory: DialogFactory) {
    fun showConfirmation() {
        dialogFactory.createConfirmDialog("Confirm") { /* action */ }.show()
    }
}
```

## Decorator Pattern

Stack behaviors using delegation:

```kotlin
interface NotificationService {
    fun send(message: String)
}

@Singleton
class BasicNotificationService : NotificationService {
    override fun send(message: String) { /* send */ }
}

class LoggingNotificationDecorator(
    private val delegate: NotificationService,
    private val logger: Logger
) : NotificationService {
    override fun send(message: String) {
        logger.log("Sending: $message")
        delegate.send(message)
    }
}

class RateLimitedNotificationDecorator(
    private val delegate: NotificationService,
    private val rateLimiter: RateLimiter
) : NotificationService {
    override fun send(message: String) {
        if (rateLimiter.tryAcquire()) delegate.send(message)
    }
}
```

Stack decorators in module:

```kotlin
fun createLogger(): Logger = ConsoleLogger()
fun createRateLimiter(): RateLimiter = TokenBucketRateLimiter()

val notificationModule = module {
    single { BasicNotificationService() }
    single { create(::createLogger) }
    single { create(::createRateLimiter) }

    single<NotificationService> {
        RateLimitedNotificationDecorator(
            delegate = LoggingNotificationDecorator(
                delegate = get<BasicNotificationService>(),
                logger = get()
            ),
            rateLimiter = get()
        )
    }
}
```

## Next Steps

- **[Definitions](/docs/reference/koin-core/definitions)** - Basic definition types
- **[Qualifiers](/docs/reference/koin-core/qualifiers)** - Named and typed qualifiers
- **[Troubleshooting](/docs/reference/koin-core/troubleshooting)** - Debug and fix issues

---

### Reference Koin Core Context Isolation (`docs/reference/koin-core/context-isolation.md`)

---
title: Context Isolation
---

Context isolation allows SDK makers to use Koin without conflicting with the host application's Koin instance.

:::info
For general Koin setup, see **[Starting Koin](/docs/reference/koin-core/starting-koin)**.
:::

## When to Use Context Isolation

- **SDK/Library Development** - Your library uses Koin internally
- **Avoiding Conflicts** - Host app may also use Koin
- **Encapsulation** - Keep your DI container private

## Creating an Isolated Context

Instead of using `startKoin` (which registers in GlobalContext), use `koinApplication`:

```kotlin
// Isolated Koin context for your SDK
object MySdkKoinContext {

    private val koinApp = koinApplication {
        modules(sdkModule)
    }

    val koin = koinApp.koin
}

val sdkModule = module {
    single<SdkService>()
    single<SdkRepository>()
}
```

## Custom KoinComponent

Create a custom `KoinComponent` that uses your isolated context:

```kotlin
internal interface SdkKoinComponent : KoinComponent {
    // Override to use isolated context
    override fun getKoin(): Koin = MySdkKoinContext.koin
}

// Usage in your SDK classes
class MySdkClass : SdkKoinComponent {
    private val service: SdkService by inject()  // Uses isolated context
}
```

## Testing Isolated Context

Override `getKoin()` in tests to use the isolated context:

```kotlin
class SdkTest : KoinTest {
    override fun getKoin(): Koin = MySdkKoinContext.koin

    @Before
    fun setUp() {
        val testModule = module {
            single<SdkService> { MockSdkService() }
        }
        koin.loadModules(listOf(testModule))
    }

    @After
    fun tearDown() {
        koin.unloadModules(listOf(testModule))
    }
}
```

## See Also

- **[Starting Koin](/docs/reference/koin-core/starting-koin)** - Standard Koin setup
- **[Compose Isolated Context](/docs/reference/koin-compose/isolated-context)** - Isolation in Compose apps

---

### Reference Koin Core Definitions (`docs/reference/koin-core/definitions.md`)

---
title: Definitions
---

# Definitions

Definitions declare how Koin creates and manages your dependencies. This guide covers all definition types using both DSL and Annotations.

## Definition Types

| Type | DSL | Annotation | Lifecycle | Use Case |
|------|-----|------------|-----------|----------|
| Singleton | `single()` | `@Singleton` | One instance for app lifetime | Services, repositories, databases |
| Factory | `factory()` | `@Factory` | New instance each time | Presenters, use cases, stateful objects |
| Scoped | `scoped()` | `@Scoped` | One instance per scope | Activity-bound, session-bound objects |
| ViewModel | `viewModel()` | `@KoinViewModel` | Android ViewModel lifecycle | ViewModels |

## Declaring Definitions

### Compiler Plugin DSL (Recommended)

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

val appModule = module {
    // Singleton
    single<Database>()
    single<UserRepository>()

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

    // ViewModel
    viewModel<UserViewModel>()
}
```

### Annotations

```kotlin
@Singleton  // or @Single
class Database

@Singleton
class UserRepository(private val database: Database)

@Factory
class UserPresenter(private val repository: UserRepository)

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

### Classic DSL

```kotlin
val appModule = module {
    // With constructor reference (autowiring)
    singleOf(::Database)
    singleOf(::UserRepository)
    factoryOf(::UserPresenter)
    viewModelOf(::UserViewModel)

    // With lambda (manual wiring)
    single { Database() }
    single { UserRepository(get()) }
    factory { UserPresenter(get()) }
    viewModel { UserViewModel(get()) }
}
```

## Definition Comparison

| Concept | Compiler Plugin DSL | Classic DSL | Annotation |
|---------|---------------------|-------------|------------|
| Singleton | `single<MyClass>()` | `singleOf(::MyClass)` | `@Singleton` / `@Single` |
| Factory | `factory<MyClass>()` | `factoryOf(::MyClass)` | `@Factory` |
| Scoped | `scoped<MyClass>()` | `scopedOf(::MyClass)` | `@Scoped` |
| ViewModel | `viewModel<MyVM>()` | `viewModelOf(::MyVM)` | `@KoinViewModel` |
| Worker | `worker<MyWorker>()` | `workerOf(::MyWorker)` | `@KoinWorker` |

:::info
The compiler plugin is analysing your class and function parameters, to generate the right call to Koin with `get()` function you don't have to write anymore.
:::

## Single (Singleton)

Creates one instance that's reused throughout the app:

```kotlin
// DSL
single<DatabaseHelper>()

// Annotation
@Singleton
class DatabaseHelper
```

Both create the same result - a single instance shared across all consumers.

## Factory

Creates a new instance each time:

```kotlin
// DSL
factory<UserPresenter>()

// Annotation
@Factory
class UserPresenter(private val repository: UserRepository)
```

## Scoped

Creates one instance per scope:

```kotlin
// DSL
scope<MyActivity> {
    scoped<ActivityPresenter>()
}

// Annotation
@Scoped(MyActivityScope::class)
class ActivityPresenter
```

## ViewModel

Android ViewModel with proper lifecycle:

```kotlin
// DSL
viewModel<UserViewModel>()

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

## Interface Binding

### Compiler Plugin DSL

```kotlin
single<UserRepositoryImpl>() bind UserRepository::class

// Multiple bindings
single<MyServiceImpl>() binds arrayOf(ServiceA::class, ServiceB::class)
```

### Classic DSL

```kotlin
singleOf(::UserRepositoryImpl) bind UserRepository::class

// Or with lambda
single<UserRepository> { UserRepositoryImpl(get()) }
```

### Annotations

**Interface binding is automatic** when your class implements an interface:

```kotlin
@Singleton
class UserRepositoryImpl(
    private val database: Database
) : UserRepository  // Automatically binds to UserRepository
```

For explicit binding:

```kotlin
@Singleton
@Binds(UserRepository::class)
class UserRepositoryImpl : UserRepository
```

## Qualifiers (Named Definitions)

When you have multiple definitions of the same type. See also [Injection with Qualifiers](/docs/reference/koin-core/injection#injection-with-qualifiers) for retrieval.

### Compiler Plugin DSL

With Compiler Plugin DSL, you need to annotate with `@Named` to use string qualifier (like you were previously using `named()`)

```kotlin
@Named("local")
class LocalDatabase : Database

@Named("remote")
class RemoteDatabase : Database

class UserRepository(
    @Named("local") private val localDb: Database,
    @Named("remote") private val remoteDb: Database
)

single<LocalDatabase>()
single<RemoteDatabase>()
single<UserRepository>()

// Usage
val localDb: Database = get(named("local"))
```

### Classic DSL

```kotlin
single<Database>(named("local")) { LocalDatabase() }
single<Database>(named("remote")) { RemoteDatabase() }

// Usage
val localDb: Database = get(named("local"))
```

### Annotations

```kotlin
@Singleton
@Named("local")
class LocalDatabase : Database

@Singleton
@Named("remote")
class RemoteDatabase : Database

// In a consumer
@Singleton
class UserRepository(
    @Named("local") private val localDb: Database,
    @Named("remote") private val remoteDb: Database
)
```

## Injected Parameters

Pass parameters at injection time:

### Compiler Plugin DSL

Use the `@InjectedParam` to indicate that a parameter will be served by injected parameters.

```kotlin
class UserPresenter(
    @InjectedParam userId : String,
    repository : UserRepository
)

factory<UserPresenter>()
```

### Classic DSL

```kotlin
class UserPresenter(
    userId : String,
    repository : UserRepository
)

factory { params ->
    UserPresenter(
        userId = params.get(),
        repository = get()
    )
}
```

### Annotations

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

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

## Optional Dependencies

### Compiler Plugin DSL

```kotlin
class MyService(
    val required: RequiredDep,
    val optional: OptionalDep?  // Resolved with getOrNull()
)

single<MyService>()
```

### Classic DSL

```kotlin
single {
    MyService(
        required = get(),
        optional = getOrNull()
    )
}
```

### Annotations

Nullable parameters are handled automatically:

```kotlin
@Singleton
class MyService(
    val required: RequiredDep,
    val optional: OptionalDep?  // Resolved with getOrNull()
)
```

## Lazy Injection

Defer instance creation:

### Compiler Plugin DSL

```kotlin
class MyService(
    val lazyDep: Lazy<HeavyDependency>  // Deferred creation
)

single<MyService>()
```

### Classic DSL

```kotlin
single {
    MyService(
        lazyDep = inject()  // Lazy<Dependency>
    )
}
```

### Annotations

```kotlin
@Singleton
class MyService(
    val lazyDep: Lazy<HeavyDependency>  // Deferred creation
)
```

## Properties

Inject configuration values:

### Compiler Plugin DSL

```kotlin
class ApiClient(
    @Property("api_url") val url: String,
    @Property("api_key") val key: String
)

single<ApiClient>()
```

### Classic DSL

```kotlin
single {
    ApiClient(
        url = getProperty("api_url"),
        key = getProperty("api_key", "default")
    )
}
```

### Annotations

```kotlin
@Singleton
class ApiClient(
    @Property("api_url") val url: String,
    @Property("api_key") val key: String
)
```

## Callbacks

### onClose Callback

Execute code when instance is released:

```kotlin
single {
    Database()
} onClose {
    it?.close()  // Called when Koin stops or scope closes
}
```

### createdAtStart

Create instance eagerly at startup:

```kotlin
// Compiler Plugin DSL
single<ConfigManager>() withOptions {
    createdAtStart()
}

// Classic DSL
single(createdAtStart = true) {
    ConfigManager()
}
```

## Definition Override

### Default: Last Wins

```kotlin
val prodModule = module {
    single<ApiService> { ProductionApi() }
}

val testModule = module {
    single<ApiService> { MockApi() }  // Overrides production
}

startKoin {
    modules(prodModule, testModule)
}
```

### Explicit Override

In strict mode, mark overrides explicitly:

```kotlin
val testModule = module {
    single<ApiService> { MockApi() }.override()
}

startKoin {
    allowOverride(false)
    modules(prodModule, testModule)
}
```

## Safe DSL Patterns

The Koin Compiler Plugin transforms DSL definitions at compile time — auto-wiring constructor parameters and validating them. Here are the key patterns:

### Function Builders with create()

Use `create(::function)` to wrap external libraries you don't own. The function parameters are auto-resolved from the DI container:

```kotlin
import org.koin.dsl.module
import org.koin.plugin.module.dsl.create

// Builder functions — parameters resolved by Koin
fun database(context: Context): AppDatabase =
    Room.databaseBuilder(context, AppDatabase::class.java, "my-db").build()

fun topicDao(db: AppDatabase): TopicDao = db.topicDao()
fun newsDao(db: AppDatabase): NewsResourceDao = db.newsResourceDao()

val databaseModule = module {
    single { create(::database) }
    single { create(::topicDao) }
    single { create(::newsDao) }
}
```

This is the recommended pattern for Room databases, Retrofit services, OkHttp clients, and other external libraries.

### Module Composition with includes()

Organize modules by layer and compose them:

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

val networkModule = module {
    includes(dispatchersModule)

    single { create(::json) }
    single<AppHttpClient>()
    single<DemoNetworkDataSource>() bind NetworkDataSource::class
}

private fun json(): Json = Json { ignoreUnknownKeys = true }
```

### App Module — Composing Everything

The app module includes all feature modules and declares ViewModels and use cases:

```kotlin
import org.koin.dsl.module
import org.koin.plugin.module.dsl.*
import org.koin.androidx.scope.dsl.activityScope

val appModule = module {
    includes(
        dispatchersModule,
        databaseModule,
        dataStoreModule,
        networkModule,
        dataModule,
        syncModule
    )

    // Domain use cases — factory (new instance each time)
    factory<GetFollowableTopicsUseCase>()
    factory<GetSearchContentsUseCase>()

    // ViewModels
    viewModel<MainActivityViewModel>()
    viewModel<HomeViewModel>()
    viewModel<BookmarksViewModel>()

    // Activity-scoped definitions
    activityScope {
        scoped<ActivityTracker>()
    }
}
```

### Custom Qualifiers in DSL

Qualifier annotations work with `create(::function)` too:

```kotlin
import org.koin.dsl.module
import org.koin.plugin.module.dsl.create

val dispatchersModule = module {
    single { create(::dispatcherIO) }
    single { create(::dispatcherDefault) }
    single { create(::coroutineScope) }
}

@Dispatcher(NiaDispatchers.IO)
fun dispatcherIO(): CoroutineDispatcher = Dispatchers.IO

@Dispatcher(NiaDispatchers.Default)
fun dispatcherDefault(): CoroutineDispatcher = Dispatchers.Default

fun coroutineScope(
    @Dispatcher(NiaDispatchers.Default) default: CoroutineDispatcher
) = CoroutineScope(SupervisorJob() + default)
```

### Worker with DSL

```kotlin
import org.koin.dsl.module
import org.koin.plugin.module.dsl.*
import org.koin.dsl.bind

val syncModule = module {
    single<WorkManagerSyncManager>() bind SyncManager::class
    worker<SyncWorker>()
}
```

### Complete Pattern: Repository with Interface Binding

```kotlin
import org.koin.dsl.module
import org.koin.dsl.bind
import org.koin.plugin.module.dsl.single

val dataModule = module {
    includes(databaseModule, dataStoreModule, networkModule)

    single<OfflineFirstNewsRepository>() bind NewsRepository::class
    single<OfflineFirstTopicsRepository>() bind TopicsRepository::class
    single<OfflineFirstUserDataRepository>() bind UserDataRepository::class
}
```

All these definitions are validated at compile time by the Koin Compiler Plugin — missing dependencies, qualifier mismatches, and broken call sites are caught at build time. See [Compile-Time Safety](/docs/reference/koin-compiler/compile-safety).

## Best Practices

1. **Prefer Constructor Injection** - Makes code testable without Koin
2. **Use `single` for stateless services** - Repositories, clients, helpers
3. **Use `factory` for stateful objects** - Presenters, use cases with state
4. **Use `scoped` for lifecycle-bound objects** - Activity, Fragment, Session
5. **Minimize qualifiers** - Use different interfaces instead when possible
6. **Bind to interfaces** - Depend on abstractions, not implementations
7. **Use `create(::builder)` for external libraries** - Safer dependency resolution

## Next Steps

- **[Injection](/docs/reference/koin-core/injection)** - Retrieve dependencies
- **[Qualifiers](/docs/reference/koin-core/qualifiers)** - Named and typed qualifiers
- **[Advanced Patterns](/docs/reference/koin-core/advanced-patterns)** - Collections, decorators, external libraries
- **[Scopes](/docs/reference/koin-core/scopes)** - Manage lifecycle

---

### Reference Koin Core Dsl Update (`docs/reference/koin-core/dsl-update.md`)

---
title: Autowire DSL
---

Koin offers an autowire DSL that allows you to target a class constructor directly and automatically wire dependencies.

:::tip
If you're using the **Koin Compiler Plugin**, consider using the [Compiler Plugin DSL](/docs/setup/compiler-plugin) which provides similar auto-wiring with additional compile-time safety.
:::

## Classic Autowire DSL

For a given class `ClassA` with dependencies:

```kotlin
class ClassA(val b: ClassB, val c: ClassC)
class ClassB()
class ClassC()
```

Declare components targeting the class constructor:

```kotlin
import org.koin.dsl.*

module {
    singleOf(::ClassA)
    singleOf(::ClassB)
    singleOf(::ClassC)
}
```

No need to specify dependencies with `get()` function!

:::info
Use `::` before your class name to target the constructor.
:::

:::note
Your constructor is filled automatically with all required dependencies. Avoid using default values as Koin will try to resolve all parameters.
:::

## Comparison with Compiler Plugin DSL

| Classic Autowire | Compiler Plugin |
|------------------|-----------------|
| `singleOf(::ClassA)` | `single<ClassA>()` |
| `factoryOf(::ClassA)` | `factory<ClassA>()` |
| `scopedOf(::ClassA)` | `scoped<ClassA>()` |
| Package: `org.koin.dsl` | Package: `org.koin.plugin.module.dsl` |

The Compiler Plugin DSL provides the same auto-wiring capability with additional compile-time verification.

## Available Keywords

The following autowire keywords are available to build your definition from constructor:

* `factoryOf` - equivalent of `factory { }` - factory definition
* `singleOf` - equivalent of `single { }` - single definition
* `scopedOf` - equivalent of `scoped { }` - scoped definition

:::info
Be sure to not use any default value in your constructor, as Koin will try to fill every parameter with it.
:::

## DSL Options

Any autowire DSL definition can also open some options within a lambda:

```kotlin
module {
    singleOf(::ClassA) {
        // definition options
        named("my_qualifier")
        bind<InterfaceA>()
        createdAtStart()
    }
}
```

Usual options and DSL keywords are available in this lambda:

* `named("a_qualifier")` - give a String qualifier to the definition
* `named<MyType>()` - give a Type qualifier to the definition
* `bind<MyInterface>()` - add type to bind for given definition
* `binds(listOf(...))` - add types list for given definition
* `createdAtStart()` - create single instance at Koin start

You can also use `bind` or `binds` operator, without any need of lambda:

```kotlin
module {
    singleOf(::ClassA) bind InterfaceA::class
}
```

## Injected Parameters

With autowire DSL declarations, you can still use injected parameters. Koin will look in injected parameters and current dependencies to try to inject your constructor.

Like following:

```kotlin
class MyFactory(val id : String)
```

declared with autowire DSL:

```kotlin
module {
    factoryOf(::MyFactory)
}
```

can be injected like this:

```kotlin
val id = "a_factory_id"
val factory = koin.get<MyFactory> { parametersOf(id)}
```

## Reflection Based DSL (Deprecated since 3.2)

:::caution
Koin Reflection DSL is now deprecated. Please use Koin Autowire DSL above
:::

---

### Reference Koin Core Dsl (`docs/reference/koin-core/dsl.md`)

---
title: Koin DSL
---

Quick reference for Koin DSL. For detailed guides see **[Core - Definitions](/docs/reference/koin-core/definitions)** and **[Core - Modules](/docs/reference/koin-core/modules)**.

## DSL Approaches

| Approach | Syntax | Package |
|----------|--------|---------|
| **Classic DSL** | `single { Class(get()) }` | `org.koin.dsl` |
| **Classic Autowire** | `singleOf(::Class)` | `org.koin.dsl` |
| **Compiler Plugin** | `single<Class>()` | `org.koin.plugin.module.dsl` |

:::tip
The **Compiler Plugin DSL** provides auto-wiring and compile-time safety. See [Compiler Plugin Setup](/docs/setup/compiler-plugin).
:::

## Application DSL

A `KoinApplication` instance represents your configured Koin container. This lets you set up logging, load properties, and register modules.

### Creating a KoinApplication

Choose between two approaches:

* `koinApplication { }` - Creates a standalone `KoinApplication` instance
* `startKoin { }` - Creates a `KoinApplication` and registers it in the `GlobalContext`

```kotlin
// Standalone instance (useful for testing or custom contexts)
val koinApp = koinApplication {
    modules(myModule)
}

// Global instance (standard approach for applications)
startKoin {
    logger()
    modules(myModule)
}
```

### Configuration Functions

Within `koinApplication` or `startKoin`, you can use:

* `logger()` - Set the logging level and Logger implementation (default: EmptyLogger)
* `modules()` - Load modules into the container (accepts list or vararg)
* `properties()` - Load a HashMap of properties
* `fileProperties()` - Load properties from a file
* `environmentProperties()` - Load properties from OS environment variables
* `createEagerInstances()` - Instantiate all definitions marked with `createdAtStart`
* `allowOverride(Boolean)` - Enable/disable definition overriding (default: true since 3.1.0)

### Global vs Local Context

The key difference between `koinApplication` and `startKoin`:

- **`startKoin`** - Registers the container in `GlobalContext`, making it accessible via `KoinComponent`, `by inject()`, and other global APIs
- **`koinApplication`** - Creates an isolated instance you control directly

```kotlin
// Global context - standard usage
startKoin {
    logger()
    modules(appModule)
}

// Later, anywhere in your app:
class MyClass : KoinComponent {
    val service: Service by inject() // Uses GlobalContext
}
```

```kotlin
// Local context - advanced usage (testing, multi-context apps)
val customKoin = koinApplication {
    modules(testModule)
}.koin

val service = customKoin.get<Service>() // Use specific instance
```

### Starting Koin

A complete Koin setup example:

```kotlin
startKoin {
    // Configure logging
    logger(Level.INFO)

    // Load properties
    environmentProperties()

    // Declare modules
    modules(
        networkModule,
        databaseModule,
        repositoryModule,
        viewModelModule
    )

    // Create eager singletons
    createEagerInstances()
}
```

## Module DSL

For comprehensive module and definition documentation, see:
- **[Definitions](/docs/reference/koin-core/definitions)** - All definition types with DSL and Annotations
- **[Modules](/docs/reference/koin-core/modules)** - Module organization and composition
- **[Definitions Reference](/docs/reference/koin-core/definitions)** - Quick lookup table

### Quick Reference

| Definition | Classic Lambda | Classic Autowire | Compiler Plugin |
|------------|----------------|------------------|-----------------|
| Singleton | `single { Class(get()) }` | `singleOf(::Class)` | `single<Class>()` |
| Factory | `factory { Class(get()) }` | `factoryOf(::Class)` | `factory<Class>()` |
| Scoped | `scoped { Class(get()) }` | `scopedOf(::Class)` | `scoped<Class>()` |
| ViewModel | `viewModel { VM(get()) }` | `viewModelOf(::VM)` | `viewModel<VM>()` |
| Function Builder | `single { fn(get()) }` | — | `single { create(::fn) }` |

### Basic Module

```kotlin
val myModule = module {
    single<Database>()
    single<UserRepository>()
    factory<UserPresenter>()
}
```

### Module Composition

```kotlin
val appModule = module {
    includes(networkModule, databaseModule)
    single<AppConfig>()
}

startKoin {
    modules(appModule)
}
```

See **[Modules - includes()](/docs/reference/koin-core/modules#module-composition-with-includes)** for details.

---

--- METRICS ---
- Files Extracted: 66
- Estimated Token Budget: ~125426 tokens
- Canonical Reference: https://codewiki.google/github.com/InsertKoinIO/koin
