Koin Dependency Injection Framework

repository·main·Indexed 27 days ago

https://github.com/insertkoinio/koin

A pragmatic and lightweight dependency injection framework for Kotlin and Kotlin Multiplatform (KMP). Koin provides a powerful DSL and an annotation-driven approach, both supported by a dedicated compiler plugin that ensures compile-time safety for dependency graphs. It is compatible with JVM, Android, Compose, iOS, Desktop, Web (JS/Wasm), and Ktor. The framework supports dynamic module loading and offers an official IDE plugin for IntelliJ IDEA and Android Studio.

Tokens
129.7K
Snippets
469
Records
577
Agent score
93%

What's inside Koin

  1. Overview of Koin Dependency Injection

    main
    Koin is a pragmatic, lightweight dependency injection framework designed for Kotlin and Kotlin Multiplatform (KMP) developers. It provides a simple and powerful DSL for managing dependencies across various targets.
  2. Compare Koin with Hilt/Dagger

    main
    Koin offers a choice between a DSL and Annotations, both of which are powered by the same Compiler Plugin for compile-safety. Unlike Hilt/Dagger, which is strictly static, Koin provides runtime flexibility, allowing for dynamic module loading, unloading, and lazy background loading.
  3. Understand Android Scope Lifecycles

    main

    Koin provides Android-specific scopes to align dependency lifecycles with Android components. This prevents memory leaks and ensures proper resource management.

    Scope Types

    Scope TypeLifetimeSurvives RotationDSLAnnotation
    ApplicationEntire app✅ Yessingle { }@Singleton
    ActivityActivity lifecycle❌ NoactivityScope { }@ActivityScope
    Activity RetainedUntil finish()✅ YesactivityRetainedScope { }@ActivityRetainedScope
    FragmentFragment lifecycle❌ NofragmentScope { }@FragmentScope
    ViewModelViewModel lifecycle✅ YesviewModelScope { }@ViewModelScope

    Scope Hierarchy

    Child scopes can access parent scope definitions, but parents cannot access child scopes:

    Application Scope $\rightarrow$ Activity Retained Scope $\rightarrow$ Activity Scope $\rightarrow$ Fragment Scope.

    Note: ViewModel Scope can access parent scopes but cannot access Activity or Fragment scopes to prevent memory leaks.

  4. Understand Koin Compile-Time Safety levels

    main

    The Koin Compiler Plugin validates your dependency graph at three distinct levels during compilation to catch missing dependencies, qualifier mismatches, and broken call sites before runtime.

    1. A2 — Per-Module (Early Feedback): Validates a module's definitions against its own definitions, explicitly included modules (via @Module(includes = [...])), and sibling modules sharing the same @Configuration label.
    2. A3 — Full Graph (Complete Guarantee): Validates the entire assembled graph at startKoin<T>(), including cross-module dependencies and definitions from external JARs.
    3. A4 — Call-Site Validation: Intercepts every koinViewModel<T>(), get<T>(), and inject<T>() call to ensure the requested type T exists in the assembled graph, providing exact file, line, and column numbers on failure.
  5. Choose a Koin dependency injection style

    main

    Koin offers two primary ways to define dependencies, both of which are first-class citizens and fully supported by the Koin Compiler Plugin for compile-time safety.

    DSL Style

    Use the Kotlin DSL for a pure Kotlin approach. This is useful for developers who prefer explicit module definitions.

    Annotation Style

    Use annotations for a more familiar pattern (similar to Hilt or Dagger) with less ceremony. This style is ideal for automatic component scanning and module discovery.

    // DSL Style
    val appModule = module {
        single<Database>()
        single<ApiClient>()
        single<UserRepository>()
        viewModel<UserViewModel>()
    }
    
    // Annotation Style
    @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()
  6. Summary of Instrumented Testing Strategies

    main

    Key takeaways for implementing instrumented tests with Koin in Android:

    • Configuration: Use a Custom Test Application or Test Rules to manage Koin configuration.
    • Module Overriding: Use override = true or provide test-specific modules to replace production dependencies.
    • Test Doubles: Prefer Fakes over Mocks for better performance in instrumented environments.
    • Isolation: Ensure strict test isolation by cleaning up dependencies between tests.
    • Compose Integration: KoinContext allows Koin to work seamlessly with Jetpack Compose testing.
    • Verification: Use the Koin Compiler Plugin (compile-time) or verify() (runtime) to catch configuration errors early.
  7. Choose a Koin approach: DSL or Annotations

    main

    Koin provides two primary ways to define dependencies. You can choose based on your preference for a Kotlin DSL or a more annotation-driven style:

    1. Koin Compiler Plugin (Recommended): Provides compile-time safety for both DSL and Annotations.
      • DSL: Uses functions like single<T>(), factory<T>(), and viewModel<T>().
      • Annotations: Uses @Singleton, @Factory, and @KoinViewModel. It auto-detects dependencies and provides compile-time safety.
    2. Classic DSL: Uses syntax like singleOf(::MyService) or single { MyService(get()) }. This is fully supported and works with any Kotlin version.

    Note: The KSP Processor (koin-ksp-compiler) is deprecated. You should migrate to the Koin Compiler Plugin.

  8. Understand Dependency Injection (DI) patterns

    main

    Dependency Injection 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 architecture.

    Three ways to provide dependencies:

    1. Constructor Injection (Recommended): Dependencies are passed through the constructor. This is the preferred approach in Koin because it makes dependencies explicit, immutable, and allows for testing without requiring Koin in unit tests.
    2. Field Injection: Dependencies are injected into class properties. This is useful for Android framework classes (like Activity, Fragment, or Service) where you do not control the constructor.
    3. Method Injection: Dependencies are passed through methods. This is typically used for optional dependencies or dependencies that change during an object's lifetime.
  9. R8 / ProGuard compatibility with Koin

    main

    Koin's core dependency resolution is R8-safe. Functions like get<T>(), inject<T>(), and the *Of builders (e.g., singleOf, factoryOf, viewModelOf) resolve dependencies at compile time using reified types. On Android/JVM, Koin keys the registry by Class.getName(), which is stable under R8.

    Because Koin does not use runtime reflection over your constructors, you do not need to add ProGuard -keep rules for your Koin definitions, ViewModels, or their constructors on Koin's behalf. Koin automatically ships consumer-rules.pro in its Android AARs to handle its own internals.

  10. Explore Koin Compiler Plugin features and references

    main

    The Koin Compiler Plugin offers several ways to interact with the dependency injection framework:

    • DSL Usage: Use the Koin DSL for defining modules. Refer to the DSL Reference for complete documentation.
    • Annotations Usage: Use Koin Annotations for a more declarative approach. Refer to the Annotations Reference for complete documentation.
    • Migration: If you are upgrading from KSP to the Compiler Plugin, follow the Migration Guide.
  11. Understand Koin Definition Types

    main

    Koin uses different definition types to manage the lifecycle and creation of dependencies. Choose the type based on your use case:

    TypeDSLAnnotationLifecycleUse Case
    Singletonsingle()@SingletonOne instance for app lifetimeServices, repositories, databases
    Factoryfactory()@FactoryNew instance each timePresenters, use cases, stateful objects
    Scopedscoped()@ScopedOne instance per scopeActivity-bound, session-bound objects
    ViewModelviewModel()@KoinViewModelAndroid ViewModel lifecycleViewModels
  12. Choose a Koin starting method

    main

    Select a starting method based on your application type:

    • startKoin { }: Use for standard applications. This registers Koin in the GlobalContext.
    • koinApplication { }: Use for testing or building SDKs where you need an isolated Koin instance.
    • koinConfiguration { }: Use for configuration within Compose or Ktor.
    • startKoin<T>(): Use for typed startup when using the Koin Compiler Plugin.