### Kotlin Multiplatform Setup with KSP Source: https://context7.com/evant/kotlin-inject/llms.txt Configure KSP for Kotlin Multiplatform projects using the `kotlin-inject-runtime-kmp` artifact. This example shows how to set up dependencies and source directories for KSP generation. ```kotlin // build.gradle.kts (KMP) plugins { id("org.jetbrains.kotlin.multiplatform") id("com.google.devtools.ksp") } kotlin { androidTarget() iosX64(); iosArm64(); iosSimulatorArm64() sourceSets { commonMain { dependencies { implementation("me.tatarka.inject:kotlin-inject-runtime-kmp:0.9.0") } // Option 1: generate into commonMain kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin") } } } dependencies { // Option 1: KSP generates into common source set kspCommonMainMetadata("me.tatarka.inject:kotlin-inject-compiler-ksp:0.9.0") // Option 2: KSP generates into each target source set add("kspAndroid", "me.tatarka.inject:kotlin-inject-compiler-ksp:0.9.0") add("kspIosX64", "me.tatarka.inject:kotlin-inject-compiler-ksp:0.9.0") add("kspIosArm64", "me.tatarka.inject:kotlin-inject-compiler-ksp:0.9.0") add("kspIosSimulatorArm64", "me.tatarka.inject:kotlin-inject-compiler-ksp:0.9.0") } // KSP2: ensure commonMain metadata is processed before targets tasks.withType().configureEach { if (name != "kspCommonMainKotlinMetadata") dependsOn("kspCommonMainKotlinMetadata") } ``` -------------------------------- ### KmpComponentCreate for Shared Source Sets (iOS Example) Source: https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md Demonstrates using `@KmpComponentCreate` in a shared source set (e.g., `ios`) to allow component creation from that shared set, even when the actual `create` functions are generated in target-specific source sets. ```kotlin // common source set @Component abstract class MyKmpComponent // android source set val myKmpComponent: MyKmpComponent = MyKmpComponent::class.create() // ios source set // the actual createKmp functions will only be generated in the targets that depend on the ios source set @KmpComponentCreate expect fun MyKmpComponent.Companion.createKmp(): MyKmpComponent val myKmpComponent: MyKmpComponent = MyKmpComponent.createKmp() ``` -------------------------------- ### Mocking Dependencies in Tests Source: https://github.com/evant/kotlin-inject/blob/main/docs/testing.md While mocking libraries can simplify setup, they have significant downsides. This includes test code fragility and potential discrepancies between mocked and real behavior. ```kotlin @Test fun test_profile_screen() { val mockUserAccountRepository = mockk() every { mockUserAccountRepository.getUserInfo(any()) } returns UserInfo( userId = "123", userName = "Tamra", accountStatus = SUBSCRIBED, ) val profileScreen = ProfileScreen(mockUserAccountRepository) // test screen } ``` -------------------------------- ### Example Dependency Graph Source: https://github.com/evant/kotlin-inject/blob/main/docs/architecture.md Illustrates the dependency resolution process for a set of injected classes, forming a Directed-Acyclic-Graph (DAG). ```kotlin @Inject class Foo() @Inject class Bar(foo: Foo) @Inject class Baz(bar: Bar, foo: Foo) ``` ```text Baz─►Bar─►Foo │ ▲ └─────────┘ ``` -------------------------------- ### Setup Kotlin Inject with Gradle and KSP Source: https://context7.com/evant/kotlin-inject/llms.txt Add the KSP plugin and kotlin-inject dependencies to your build.gradle.kts file. Ensure repositories are correctly configured. ```kotlin pluginManagement { repositories { gradlePluginPortal() mavenCentral() } } // build.gradle.kts (JVM / Android) plugins { id("org.jetbrains.kotlin.jvm") version "2.2.20" id("com.google.devtools.ksp") version "2.3.4" } repositories { mavenCentral() google() } dependencies { ksp("me.tatarka.inject:kotlin-inject-compiler-ksp:0.9.0") implementation("me.tatarka.inject:kotlin-inject-runtime:0.9.0") } ``` -------------------------------- ### Configure KSP Options in build.gradle.kts Source: https://github.com/evant/kotlin-inject/blob/main/README.md Pass options to the KSP processor in your `build.gradle.kts` file to enable specific functionalities. Example shows how to enable graph dumping. ```kotlin ksp { arg("me.tatarka.inject.dumpGraph", "true") } ``` -------------------------------- ### Using Test Components in Tests Source: https://github.com/evant/kotlin-inject/blob/main/docs/testing.md Instantiate test components to provide dependencies for your tests. This example shows how to use default fakes and override specific ones for custom test scenarios. ```kotlin class ProfileScreenTest { @Test fun test_using_default_fakes() { val component = TestComponent::class.create() val profileScreen = component.profileScreen // test screen } @Test fun test_using_custom_fake() { val component = TestComponent::class.create(TestApplicationComponent::class.create(TestFakes( accountService = object : FakeAccountService() { override fun getAccount(accountId: String): Account = throw Exception("failed to get account") } ))) val profileScreen = component.profileScreen // test screen } @Component abstract class TestComponent(@Component val parent: TestApplicationComponent = TestApplicationComponent::class.create()) { abstract val profileScreen: ProfileScreen } } ``` -------------------------------- ### Generated Actual Function for KmpComponentCreate Source: https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md This is an example of the code generated by kotlin-inject when using the `@KmpComponentCreate` annotation. It provides the `actual fun` implementation for creating the component. ```kotlin actual fun createKmp(): MyKmpComponent = MyKmpComponent::class.create() ``` -------------------------------- ### Component Arguments for Passing Values Source: https://context7.com/evant/kotlin-inject/llms.txt Use constructor arguments on a @Component class to pass externally created instances into the graph. Annotate the argument with @get:Provides to make it available as a dependency. ```kotlin @Component abstract class RequestComponent( @get:Provides val userId: String, @get:Provides val requestId: UUID ) { abstract val handler: RequestHandler } // Pass values at creation time val component = RequestComponent::class.create( userId = "user-42", requestId = UUID.randomUUID() ) val handler = component.handler ``` -------------------------------- ### Compose Integration with Function Injection Source: https://github.com/evant/kotlin-inject/blob/main/docs/android.md Integrates kotlin-inject with Jetpack Compose by injecting composable functions. This example shows how to provide a composable entry point to an Activity. ```kotlin typealias Home = @Composable () -> Unit @Inject @Composable fun Home(repo: HomeRepository) { // ... } @Component abstract class ApplicationComponent() { abstract val home: Home } class MyActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate() val home = ApplicationComponent().home setContent { home() } } } ``` -------------------------------- ### Create Component Instance and Access Dependencies Source: https://github.com/evant/kotlin-inject/blob/main/README.md Demonstrates how to create an instance of a generated component and access its dependencies. Ensure the component is defined before use. ```kotlin val appComponent = AppComponent::class.create() val repo = appComponent.repo ``` -------------------------------- ### Instantiate App Component with Real Network Source: https://github.com/evant/kotlin-inject/blob/main/README.md Shows how to create an instance of `AppComponent` by providing a `RealNetworkComponent`. This is typically used in a production environment where the real API implementation is required. ```kotlin AppComponent::class.create(RealNetworkComponent::class.create()) ``` -------------------------------- ### Instantiate App Component with Test Network Source: https://github.com/evant/kotlin-inject/blob/main/README.md Demonstrates creating an `AppComponent` instance using a `TestNetworkComponent`. This is useful in testing scenarios where mock or fake implementations of dependencies are needed. ```kotlin AppComponent::class.create(TestNetworkComponent::class.create()) ``` -------------------------------- ### KmpComponentCreate with Extension Function Source: https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md You can use `@KmpComponentCreate` with an extension function on the `Companion` object. This can be useful for namespacing and organizing your component creation functions. ```kotlin @KmpComponentCreate expect fun MyKmpComponent.Companion.createKmp(): MyKmpComponent ``` -------------------------------- ### Create Component with Constructor Arguments Source: https://github.com/evant/kotlin-inject/blob/main/README.md Instantiates a component that has constructor arguments. Pass the required instances to the create function. ```kotlin MyComponent::class.create(Foo()) ``` -------------------------------- ### Simplify Component Creation with KmpComponentCreate Source: https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md Use the `@KmpComponentCreate` annotation on an `expect fun` to have kotlin-inject automatically generate the `actual fun` for each target source set, simplifying component instantiation. ```kotlin @Component abstract class MyKmpComponent @KmpComponentCreate expect fun createKmp(): MyKmpComponent ``` -------------------------------- ### Configure Kotlin Inject with KSP in Gradle Source: https://github.com/evant/kotlin-inject/blob/main/README.md Sets up the build environment for kotlin-inject using KSP. Add these configurations to your settings.gradle and build.gradle files. ```groovy pluginManagement { repositories { gradlePluginPortal() mavenCentral() } } repositories { mavenCentral() google() } dependencies { ksp("me.tatarka.inject:kotlin-inject-compiler-ksp:0.9.0") implementation("me.tatarka.inject:kotlin-inject-runtime:0.9.0") } ``` -------------------------------- ### Generate Companion Extensions with KSP Option Source: https://github.com/evant/kotlin-inject/blob/main/README.md Enable `me.tatarka.inject.generateCompanionExtensions=true` to generate `create()` methods on the companion object, allowing `MyComponent.create()` instead of `MyComponent::class.create()`. Requires an explicit companion object for the component. ```kotlin @Component abstract class MyComponent { companion object } ``` -------------------------------- ### Component Creation with Expect/Actual Functions Source: https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md When a Component is declared in the common source set and requires platform-specific bindings, use `expect fun` in the common source set and `actual fun` in each target source set to call the target's `create` function. ```kotlin // common source set @Component abstract class MyKmpComponent expect fun createKmp(): MyKmpComponent // each target source set actual fun createKmp(): MyKmpComponent = MyKmpComponent::class.create() ``` -------------------------------- ### Declare Component with Constructor Arguments Source: https://github.com/evant/kotlin-inject/blob/main/README.md Shows how to define a component that accepts constructor arguments, which can be provided to the dependency graph. Use @Provides to make them available. ```kotlin @Component abstract class MyComponent(@get:Provides protected val foo: Foo) ``` -------------------------------- ### Define Component and Dependencies in Kotlin Source: https://github.com/evant/kotlin-inject/blob/main/README.md Defines a component with abstract properties and provides dependencies using @Provides and @Inject annotations. Use this to set up your dependency graph. ```kotlin @Component abstract class AppComponent { abstract val repo: Repository @Provides protected fun jsonParser(): JsonParser = JsonParser() protected val RealHttp.bind: Http @Provides get() = this } interface Http @Inject class RealHttp : Http @Inject class Api(private val http: Http, private val jsonParser: JsonParser) @Inject class Repository(private val api: Api) ``` -------------------------------- ### Generated Actual Function for Extension KmpComponentCreate Source: https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md This shows the generated `actual fun` when `@KmpComponentCreate` is used with an extension function on the `Companion` object. It correctly implements the creation logic. ```kotlin @KmpComponentCreate actual fun MyKmpComponent.Companion.createKmp(): MyKmpComponent = MyKmpComponent::class.create() ``` -------------------------------- ### Enable Javax Annotations with KSP Option Source: https://github.com/evant/kotlin-inject/blob/main/README.md Use the `me.tatarka.inject.enableJavaxAnnotations=true` KSP option to allow `@javax.inject.*` annotations. This is useful for migrating existing code or abstracting the injection library. ```kotlin ksp { arg("me.tatarka.inject.enableJavaxAnnotations", "true") } ``` -------------------------------- ### Using ViewModel Helper with SavedStateHandle in Fragment Source: https://github.com/evant/kotlin-inject/blob/main/docs/android.md Shows how to use the `viewModel` helper function with a factory that accepts a `SavedStateHandle` for injecting ViewModels in a Fragment. ```kotlin @Inject class HomeFragment(homeViewModel: (SavedStateHandle) -> HomeViewModel) : Fragment() { private val viewModel by viewModel(homeViewModel) } ``` -------------------------------- ### Using ViewModel Helper in Fragment Source: https://github.com/evant/kotlin-inject/blob/main/docs/android.md Demonstrates the usage of the custom `viewModel` helper function within a Fragment for injecting a ViewModel created via a factory function. ```kotlin @Inject class HomeFragment(homeViewModel: () -> HomeViewModel) : Fragment() { private val viewModel by viewModel(homeViewModel) } ``` -------------------------------- ### Instantiate Composed Components Source: https://github.com/evant/kotlin-inject/blob/main/README.md Creates instances of parent and child components when using component composition. The parent must be created before the child. ```kotlin val parent = ParentComponent::class.create() val child = ChildComponent::class.create(parent) ``` -------------------------------- ### Configure KSP Common Source Set and Tasks Source: https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md Add the generated KSP source directory to the common main source set and configure KSP compilation tasks. Ensure that KSP tasks depend on the metadata generation task for correctness. ```kotlin kotlin { // add your project's targets here like in the snippet above commonMain { kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin") } } // KspAATask should be used for KSP2 // For KSP1 use KotlinCompilationTask instead tasks.withType().configureEach { if (name != "kspCommonMainKotlinMetadata") { dependsOn("kspCommonMainKotlinMetadata") } } ``` -------------------------------- ### Concrete Implementations of Network Component Source: https://github.com/evant/kotlin-inject/blob/main/README.md Provides two concrete implementations (`RealNetworkComponent` and `TestNetworkComponent`) of the abstract `NetworkComponent`. Each implementation provides a specific instance of the `Api` dependency. ```kotlin @Component abstract class RealNetworkComponent : NetworkComponent() { override fun api(): Api = RealApi() } @Component abstract class TestNetworkComponent : NetworkComponent() { override fun api(): Api = FakeApi() } ``` -------------------------------- ### Define Abstract Network Component with Providers Source: https://github.com/evant/kotlin-inject/blob/main/README.md Defines an abstract base component (`NetworkComponent`) with scoped providers (`@NetworkScope`). This allows for multiple concrete implementations of the network component, useful for different environments like production and testing. ```kotlin @NetworkScope abstract class NetworkComponent { @NetworkScope @Provides abstract fun api(): Api } ``` -------------------------------- ### Direct Constructor Call for Simple Tests Source: https://github.com/evant/kotlin-inject/blob/main/docs/testing.md For uncomplicated dependencies, you can instantiate classes directly in tests. This bypasses kotlin-inject but requires manual dependency provision. ```kotlin @Inject @ApplicationScope class UserAccountsRepository(accountService: AccountService, userService: UserService) @Inject class ProfileScreen(userAccountsRepository: UserAccountsRepository) @Test fun test_profile_screen() { val userAccountRepository = UserAccountRepository(FakeAccountService(), FakeUserService()) val profileScreen = ProfileScreen(userAccountRepository) // test screen } ``` -------------------------------- ### Defining Test Fakes with @Provides Source: https://github.com/evant/kotlin-inject/blob/main/docs/testing.md Create a test component that provides fake implementations of dependencies. This allows for controlled testing by replacing specific services with fakes. ```kotlin class TestFakes( @get:Provides val accountService: AccountService = FakeAccountService(), @get:Provides val userService: UserService = FakeUserService(), ) @Component @ApplicationScope abstract class TestApplicationComponent(@Component val fakes: TestFakes = TestFakes()) ``` -------------------------------- ### Implement Singleton and Scoped Instances with @Scope Source: https://context7.com/evant/kotlin-inject/llms.txt Annotate components, @Provides functions, and @Inject classes with a custom scope annotation (e.g., @ApplicationScope) to ensure instances live as long as the component. By default, new instances are created on each request. ```kotlin @Scope @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER) annotation class ApplicationScope @ApplicationScope @Component abstract class ApplicationComponent { abstract val userRepository: UserRepository @ApplicationScope @Provides protected fun database(): Database = Database("app.db") } @ApplicationScope @Inject class UserRepository(private val db: Database) // UserRepository is created once and reused for the lifetime of ApplicationComponent ``` -------------------------------- ### Run Detekt for Static Code Analysis Source: https://github.com/evant/kotlin-inject/blob/main/CONTRIBUTING.md Execute Detekt to check for code style and potential issues. Use the `--auto-correct` flag to fix formatting errors automatically. ```bash ./gradlew detekt ``` ```bash ./gradlew detekt --auto-correct ``` -------------------------------- ### KSP Processor Options Source: https://context7.com/evant/kotlin-inject/llms.txt Configure KSP processor options in build.gradle.kts to enable features like javax.inject annotation support, companion object extensions for component creation, or dependency graph dumping for debugging. ```kotlin ksp { // Enable javax.inject annotation support (useful when migrating from Dagger) arg("me.tatarka.inject.enableJavaxAnnotations", "true") // Generate create() on companion object instead of MyComponent::class // Requires: companion object declared in each @Component class arg("me.tatarka.inject.generateCompanionExtensions", "true") // Print the resolved dependency graph during compilation (debugging) arg("me.tatarka.inject.dumpGraph", "true") } // With generateCompanionExtensions=true: @Component abstract class MyComponent { companion object // required } val component = MyComponent.create() // instead of MyComponent::class.create() ``` -------------------------------- ### Apply Kotlin Multiplatform and KSP Plugins Source: https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md Replace the kotlin-jvm plugin with the kotlin-multiplatform and KSP plugins in your Gradle build file. ```kotlin plugins { id("org.jetbrains.kotlin.multiplatform") id("com.google.devtools.ksp") } ``` -------------------------------- ### Compose Components with Parent-Child Relationship Source: https://github.com/evant/kotlin-inject/blob/main/README.md Illustrates how to create a child component that depends on a parent component, allowing for composed dependency graphs. Ensure the parent component is created first. ```kotlin @Component abstract class ParentComponent { @Provides fun provideFoo(): Foo = ... } @Component abstract class ChildComponent(@Component val parent: ParentComponent) { abstract val foo: Foo } ``` -------------------------------- ### Type Alias Support for Dependency Injection Source: https://github.com/evant/kotlin-inject/blob/main/README.md Demonstrates using type aliases (`Dep1`, `Dep2`) to differentiate between instances of the same type (`Dep`) for injection. Note: This approach is being deprecated in favor of qualifiers. ```kotlin typealias Dep1 = Dep typealias Dep2 = Dep @Component abstract class MyComponent { @Provides fun dep1(): Dep1 = Dep("one") @Provides fun dep2(): Dep2 = Dep("two") @Provides fun provides(dep1: Dep1, dep2: Dep2): Thing = Thing(dep1, dep2) } @Inject class InjectedClass(dep1: Dep1, dep2: Dep2) ``` -------------------------------- ### Assisted Injection with Function Parameters Source: https://github.com/evant/kotlin-inject/blob/main/README.md Use functions with assisted parameters to create dependencies that require additional arguments. Mark these arguments with `@Assisted` and ensure the function signature matches. ```kotlin @Inject class Foo(bar: Bar, @Assisted arg1: String, @Assisted arg2: String) @Inject class MyClass(fooCreator: (arg1: String, arg2: String) -> Foo) { init { val foo = fooCreator("1", "2") } } ``` -------------------------------- ### Component Inheritance for Shared Interfaces Source: https://context7.com/evant/kotlin-inject/llms.txt Define @Provides on an abstract class or interface and have multiple concrete component implementations extend it. This pattern is recommended for swapping production and test bindings. ```kotlin // Shared contract @NetworkScope abstract class NetworkComponent { @NetworkScope @Provides abstract fun api(): ApiService } // Production @Component abstract class RealNetworkComponent : NetworkComponent() { override fun api(): ApiService = RealApiService() } // Test @Component abstract class FakeNetworkComponent : NetworkComponent() { override fun api(): ApiService = FakeApiService() } @Component abstract class AppComponent(@Component val network: NetworkComponent) // Production val app = AppComponent::class.create(RealNetworkComponent::class.create()) // Tests val app = AppComponent::class.create(FakeNetworkComponent::class.create()) ``` -------------------------------- ### Configure KSP Compiler Dependencies Source: https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md Add these dependencies to your top-level `dependencies` block to configure KSP for code generation. Choose between generating code into the common source set or each individual KMP target source set. ```kotlin dependencies { // 1. Configure code generation into the common source set kspCommonMainMetadata(libs.kotlinInject.compiler) // 2. Configure code generation into each KMP target source set add("kspAndroid", libs.kotlinInject.compiler) add("kspIosX64", libs.kotlinInject.compiler) add("kspIosArm64", libs.kotlinInject.compiler) add("kspIosSimulatorArm64", libs.kotlinInject.compiler) // add more targets here... } ``` -------------------------------- ### Use Qualifiers for Dependency Injection Source: https://github.com/evant/kotlin-inject/blob/main/README.md Illustrates how to use the custom `@Named` qualifier to inject specific instances of `Dep` into `MyComponent` and `InjectedClass`. This ensures the correct dependency is provided when multiple versions exist. ```kotlin @Component abstract class MyComponent { @Provides fun dep1(): @Named("one") Dep = Dep("one") @Provides fun dep2(): @Named("two") Dep = Dep("two") @Provides fun provides(@Named("one") dep1: Dep, @Named("two") dep2: Dep): Thing = Thing(dep1, dep2) } @Inject class InjectedClass(@Named("one") dep1: Dep, @Named("two") dep2: Dep) ``` -------------------------------- ### Using Type Aliases for Function Injection in Classes Source: https://github.com/evant/kotlin-inject/blob/main/README.md Demonstrates injecting a type-aliased function (`myFunction`) into a class (`MyClass`) and providing it in a component (`MyComponent`). This allows the class to use the injected function. ```kotlin @Inject class MyClass(val myFunction: myFunction) @Component abstract class MyComponent { abstract val myFunction: myFunction } ``` -------------------------------- ### Implement Custom FragmentFactory for Injection Source: https://github.com/evant/kotlin-inject/blob/main/docs/android.md Leverage a custom FragmentFactory to enable constructor injection for Fragments. This requires providing factory functions for each Fragment type within the FragmentFactory. ```kotlin @Inject class InjectFragmentFactory( private val homeFragment: () -> HomeFragment, private val settingsFragment: () -> SettingsFragment, ) : FragmentFactory() { override fun instantiate(classLoader: ClassLoader, className: String): Fragment { return when (className) { name -> homeFragment() name -> settingsFragment() else -> super.instantiate(classLoader, className) } } private inline fun name() = C::class.qualifiedName } @Component abstract class MainActivityComponent(@Component val parent: ApplicationComponent) { abstract val fragmentFactory: InjectFragmentFactory } class MainActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.fragmentFactory = MainActivityComponent::class.create(ApplicationComponent.getInstance(this)) .fragmentFactory super.onCreate(savedInstanceState) } } @Inject class HomeFragment(private val imageLoader: ImageLoader) : Fragment() @Inject class SettingsFragment(private val imageLoader: ImageLoader) : Fragment() ``` -------------------------------- ### Configure Kotlin Multiplatform Targets Source: https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md Define the targets for your multiplatform project, including Android and various iOS architectures. Framework binaries are configured for iOS targets. ```kotlin kotlin { androidTarget() listOf( iosX64(), iosArm64(), iosSimulatorArm64() ).forEach { it.binaries.framework { baseName = "shared" } } // add your project's other targets... } ``` -------------------------------- ### FragmentFactory for Constructor Injection in Fragments with Kotlin-Inject Source: https://context7.com/evant/kotlin-inject/llms.txt Provide a custom FragmentFactory via kotlin-inject to enable constructor injection in Fragment subclasses. ```kotlin @Inject class InjectFragmentFactory( private val homeFragment: () -> HomeFragment, private val settingsFragment: () -> SettingsFragment, ) : FragmentFactory() { override fun instantiate(classLoader: ClassLoader, className: String): Fragment = when (className) { HomeFragment::class.qualifiedName -> homeFragment() SettingsFragment::class.qualifiedName -> settingsFragment() else -> super.instantiate(classLoader, className) } } @Inject class HomeFragment(private val imageLoader: ImageLoader) : Fragment() @Inject class SettingsFragment(private val prefs: UserPreferences) : Fragment() @Component abstract class MainActivityComponent(@Component val app: ApplicationComponent) { abstract val fragmentFactory: InjectFragmentFactory } class MainActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.fragmentFactory = MainActivityComponent::class.create(ApplicationComponent.getInstance(this)) .fragmentFactory super.onCreate(savedInstanceState) } } ``` -------------------------------- ### Debug Kotlin-Inject Compiler Source: https://github.com/evant/kotlin-inject/blob/main/CONTRIBUTING.md Run integration tests for the KSP compiler with debugging enabled. Attach a remote run configuration in IntelliJ to the specified port. ```bash ./gradlew :integration-tests:ksp:test --no-daemon -Dorg.gradle.debug=true -Dkotlin.compiler.execution.strategy=in-process ``` -------------------------------- ### Helper for ViewModel Injection with Factory Function Source: https://github.com/evant/kotlin-inject/blob/main/docs/android.md A helper extension function for Fragments that simplifies injecting ViewModels using a single factory function. It integrates with the existing `viewModels` delegate. ```kotlin import androidx.fragment.app.viewModels /** * [viewModels] helper that allows you to pass a single factory function. */ inline fun Fragment.viewModel(crossinline factory: () -> VM): Lazy = viewModels { viewModelFactory { addInitializer(VM::class) { factory() } } } /** * [viewModels] helper that allows you to pass a single factory function using a [SavedStateHandle]. */ inline fun Fragment.viewModel(crossinline factory: (SavedStateHandle) -> VM): Lazy = viewModels { viewModelFactory { addInitializer(VM::class) { factory(createSavedStateHandle()) } } } ``` -------------------------------- ### Android Activity/Fragment Integration with Kotlin-Inject Source: https://context7.com/evant/kotlin-inject/llms.txt Wrap logic in injectable classes to avoid field injection in Android Activities. Use component scopes aligned with Android lifecycle objects. ```kotlin // Instead of field injection in Activity, wrap logic in an injectable class @Inject class HomeScreen( private val imageLoader: ImageLoader, private val analytics: Analytics ) { fun loadBanner(view: ImageView) = imageLoader.load(view, "https://cdn.example.com/banner.png") fun trackView() = analytics.screen("Home") } @Component abstract class ActivityComponent(@Component val app: ApplicationComponent) { abstract val homeScreen: HomeScreen } class HomeActivity : AppCompatActivity() { private lateinit var screen: HomeScreen override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) screen = ActivityComponent::class.create( ApplicationComponent.getInstance(this) ).homeScreen screen.trackView() } } ``` -------------------------------- ### Function Injection with Explicit Arguments Source: https://github.com/evant/kotlin-inject/blob/main/README.md Illustrates how to define a type-aliased function that accepts explicit arguments. These arguments can be passed when the function is invoked, providing flexibility in how the function is used. ```kotlin typealias myFunction = (String) -> String @Inject fun myFunction(dep: Dep, arg: String): String = ... ``` -------------------------------- ### Injecting ViewModels in Compose Source: https://github.com/evant/kotlin-inject/blob/main/docs/android.md Demonstrates injecting ViewModels within a Composable function using factory functions, including support for `SavedStateHandle`. ```kotlin @Inject class HomeViewModel(private val repository: HomeRepository) : ViewModel() @Inject class OtherViewModel(private val repository: OtherRepository, @Assisted handle: SavedStateHandle): ViewModel() @Inject @Composable fun Home(homeViewModel: () -> HomeViewModel, otherViewModel: (SavedStateHandle) -> OtherViewModel) { val homeViewModel = viewModel { homeViewModel() } val otherViewModel = viewModel { otherViewModel(createSavedStateHandle()) } ... } ``` -------------------------------- ### Add Kotlin-Inject Runtime Dependency Source: https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md Include the `kotlin-inject-runtime-kmp` dependency in the commonMain source set for multiplatform projects. This runtime includes the KmpComponentCreate annotation. ```kotlin sourceSets { commonMain { dependencies { implementation("me.tatarka.inject:kotlin-inject-runtime-kmp:0.8.0") } } } ``` -------------------------------- ### Refactor Platform Classes for Injection Source: https://github.com/evant/kotlin-inject/blob/main/docs/android.md When you cannot control the construction of Android platform classes, pull dependencies and related logic into a separate injectable class. This approach also aids in testability. ```kotlin class MyActivity : Activity() { @Inject lateinit var imageLoader: ImageLoader @Inject lateinit var analytics: Analytics override fun onCreate(savedInstanceState: Bundle?) { super.onCreate() setContentView(R.layout.my_layout) // ... imageLoader.loadImage(imageView, "https://...") } override fun onResume() { super.onResume() analytics.screenView("My Screen") } } ``` ```kotlin @Inject class MyScreen(private val imageLoader: ImageLoader, private val analytics: Analytics) { fun loadImage(imageView: ImageView) { imageLoader.loadImage(imageView, "https://...") } fun onResume() { analytics.screenView("My Screen") } } @Component abstract class ActivityComponent(@Component val parent: ApplicationComponent) { abstract val myScreen: MyScreen } class MyActivity : Activity() { private lateinit var myScreen: MyScreen override fun onCreate(savedInstanceState: Bundle?) { super.onCreate() myScreen = ActivityComponent::class.create(ApplicationComponent.getInstance(this)).myScreen setContentView(R.layout.my_layout) // ... myScreen.loadImage(imageView) } override fun onResume() { super.onResume() myScreen.onResume() } } ``` -------------------------------- ### Create Child Components with @Component Source: https://context7.com/evant/kotlin-inject/llms.txt Define a child component that accepts a parent component as a constructor argument annotated with @Component. The child gains access to all dependencies provided by the parent. ```kotlin @Component abstract class ApplicationComponent { @Provides fun database(): Database = Database("app.db") } @Component abstract class FeatureComponent(@Component val app: ApplicationComponent) { // Can inject Database provided by ApplicationComponent abstract val featureRepository: FeatureRepository } val app = ApplicationComponent::class.create() val feature = FeatureComponent::class.create(app) val repo = feature.featureRepository ``` -------------------------------- ### Inject Function for Dependency Creation Source: https://github.com/evant/kotlin-inject/blob/main/README.md Inject a function to delay dependency creation or provide additional parameters manually. The function takes no arguments and returns the dependency. ```kotlin @Inject class Foo @Inject class MyClass(fooCreator: () -> Foo) { init { val foo = fooCreator() } } ``` -------------------------------- ### App Component with Network Component Dependency Source: https://github.com/evant/kotlin-inject/blob/main/README.md Defines an `AppComponent` that depends on a `NetworkComponent`. This demonstrates how to compose components, allowing the `AppComponent` to utilize the dependencies provided by the `NetworkComponent`. ```kotlin @Component abstract class AppComponent(@Component val network: NetworkComponent) ``` -------------------------------- ### Assisted Injection with @Assisted Source: https://context7.com/evant/kotlin-inject/llms.txt Use @Assisted to mark constructor parameters that must be supplied at creation time. Inject a function (AssistedParam) -> T to create instances with these parameters. ```kotlin @Inject class DownloadTask( private val httpClient: HttpClient, // from the graph @Assisted val url: String, // provided at call site @Assisted val destination: Path // provided at call site ) @Inject class DownloadManager( private val createTask: (url: String, destination: Path) -> DownloadTask ) { fun download(url: String, dest: Path) { val task = createTask(url, dest) task.start() } } ``` -------------------------------- ### Function / Provider Injection with () -> T Source: https://context7.com/evant/kotlin-inject/llms.txt Inject a function type () -> T to obtain a factory that creates a new instance each time it's called. This is useful for deferred creation or multiple independent instances. ```kotlin @Inject class Worker(private val taskId: String) @Inject class WorkerPool(private val workerFactory: () -> Worker) { fun spawnWorker(): Worker = workerFactory() // new Worker instance each time } ``` -------------------------------- ### Multi-binding into a Map Source: https://github.com/evant/kotlin-inject/blob/main/README.md Aggregates multiple `Foo` instances into a `Map` using the `@IntoMap` annotation. Each provided `Foo` is paired with a key (String) that will be used as the map key. ```kotlin @Component abstract class MyComponent { abstract val fooMap: Map @IntoMap @Provides protected fun provideFoo1(): Pair = "1" to Foo("1") @IntoMap @Provides protected fun provideFoo2(): Pair = "2" to Foo("2") } ``` -------------------------------- ### Declare Dependency Container with @Component Source: https://context7.com/evant/kotlin-inject/llms.txt Annotate a class with @Component to define the root of the dependency graph. Abstract properties and functions expose types, and KSP generates a concrete implementation. Instantiate using the create() extension. ```kotlin @Component abstract class AppComponent { // Expose Repository to callers abstract val repo: Repository // Provide an external type that has no @Inject annotation @Provides protected fun jsonParser(): JsonParser = JsonParser() // Bind implementation to interface using extension-property sugar protected val RealHttp.bind: Http @Provides get() = this } interface Http @Inject class RealHttp : Http @Inject class Api(private val http: Http, private val jsonParser: JsonParser) @Inject class Repository(private val api: Api) // Instantiate and use val appComponent = AppComponent::class.create() val repo: Repository = appComponent.repo ``` -------------------------------- ### Differentiate Multiple Instances with @Qualifier Source: https://context7.com/evant/kotlin-inject/llms.txt Declare a @Qualifier annotation (e.g., @Named) and apply it at both providing and consuming sites to manage multiple distinct instances of the same type. ```kotlin @Qualifier @Target( AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE, ) annotation class Named(val value: String) @Component abstract class ConfigComponent { @Provides fun primaryDb(): @Named("primary") Database = Database("primary.db") @Provides fun cacheDb(): @Named("cache") Database = Database("cache.db") } @Inject class RepositoryManager( @Named("primary") val primary: Database, @Named("cache") val cache: Database ) ``` -------------------------------- ### VariantComponent for Build Variants Source: https://github.com/evant/kotlin-inject/blob/main/docs/android.md Defines a `VariantComponent` interface to provide different dependencies based on build types or flavors. This allows for variant-specific implementations. ```kotlin // debug/java/com.example.inject/VariantComponent.kt interface VariantComponent { val DebugClient.bind: Client @Provides get() = this } // release/java/com.example.inject/VariantComponent.kt interface VariantComponent { val ReleaseClient.bind: Client @Provides get() = this } // main/java/com.example.inject/ApplicationComponent.kt @Component abstract class ApplicationComponent : VariantComponent ``` -------------------------------- ### Default Arguments for Dependencies Source: https://context7.com/evant/kotlin-inject/llms.txt If a dependency is present in the graph, it will be injected; if absent, the default value is used. This allows the same @Inject class to function in multiple component configurations. ```kotlin @Inject class Analytics(val tracker: Tracker = NoOpTracker()) @Component abstract class ProductionComponent { abstract val analytics: Analytics @Provides fun tracker(): Tracker = FirebaseTracker() // injected } @Component abstract class TestComponent { abstract val analytics: Analytics // No Tracker provided → NoOpTracker() default is used } ProductionComponent::class.create().analytics.tracker // FirebaseTracker TestComponent::class.create().analytics.tracker // NoOpTracker ``` -------------------------------- ### Collect Multiple Bindings into Set and Map with @IntoSet/@IntoMap Source: https://context7.com/evant/kotlin-inject/llms.txt Use @IntoSet and @IntoMap to aggregate multiple provided values into a Set or Map that can be injected. This is useful for plugins or handlers. ```kotlin @Component abstract class PluginComponent { // Inject the full set of plugins abstract val plugins: Set // Inject the full map of handlers abstract val handlers: Map @IntoSet @Provides protected fun plugin1(): Plugin = LoggingPlugin() @IntoSet @Provides protected fun plugin2(): Plugin = MetricsPlugin() @IntoSet @Provides protected fun plugin3(): Plugin = TracingPlugin() @IntoMap @Provides protected fun handler1(): Pair = "auth" to AuthHandler() @IntoMap @Provides protected fun handler2(): Pair = "data" to DataHandler() } val component = PluginComponent::class.create() component.plugins.forEach { it.initialize() } // all three plugins component.handlers["auth"]?.handle(request) // AuthHandler ``` -------------------------------- ### Shared Dependencies Between Application and Test Components Source: https://github.com/evant/kotlin-inject/blob/main/docs/testing.md Define an interface for common dependencies that can be implemented by both the application and test components. This ensures consistency for shared services. ```kotlin interface CommonComponent { @ApplicationScope val globalScope: CoroutineScope @Provides get() = CoroutineScope(Job()) } @Component @ApplicationScope abstract class ApplicationComponent : CommonComponent @Component @ApplicationScope abstract class TestApplicationComponent(@Component val fakes: TestFakes = TestFakes()) : CommonComponent ``` -------------------------------- ### ViewModel Injection with Kotlin-Inject Source: https://context7.com/evant/kotlin-inject/llms.txt Inject a factory lambda to create ViewModel instances, ensuring they are retained correctly across configuration changes. ```kotlin @Inject class HomeViewModel(private val repo: HomeRepository) : ViewModel() @Inject class DetailViewModel( private val repo: DetailRepository, @Assisted val handle: SavedStateHandle ) : ViewModel() @Inject class HomeFragment( private val homeViewModel: () -> HomeViewModel, private val detailViewModel: (SavedStateHandle) -> DetailViewModel ) : Fragment() { private val vm: HomeViewModel by viewModels { viewModelFactory { addInitializer(HomeViewModel::class) { homeViewModel() } } } private val detailVm: DetailViewModel by viewModels { viewModelFactory { addInitializer(DetailViewModel::class) { detailViewModel(createSavedStateHandle()) } } } } ``` -------------------------------- ### Lazy Injection with Lazy Source: https://context7.com/evant/kotlin-inject/llms.txt Inject Lazy to defer dependency construction until first access. The instance is then cached and reused for subsequent accesses. ```kotlin @Inject class HeavyService(private val config: Config) @Inject class AppController(private val heavyService: Lazy) { // HeavyService is not created until this property is accessed val service: HeavyService by heavyService fun handleRequest() { service.process() // constructed here on first call } } ``` -------------------------------- ### Android Build Variant Bindings with VariantComponent Source: https://context7.com/evant/kotlin-inject/llms.txt Split variant-specific bindings into a `VariantComponent` interface for each build variant. This allows different implementations of dependencies for debug and release builds. ```kotlin // debug/kotlin/.../VariantComponent.kt interface VariantComponent { val DebugHttpClient.bind: HttpClient @Provides get() = this } ``` ```kotlin // release/kotlin/.../VariantComponent.kt interface VariantComponent { val ProductionHttpClient.bind: HttpClient @Provides get() = this } ``` ```kotlin // main/kotlin/.../ApplicationComponent.kt @Component abstract class ApplicationComponent : VariantComponent ``` -------------------------------- ### Testing with Fakes in Kotlin-Inject Source: https://context7.com/evant/kotlin-inject/llms.txt Define a test component that accepts a fakes holder. Default fakes are used automatically, and specific tests can override individual ones. ```kotlin // Fakes holder — all fakes have defaults class TestFakes( @get:Provides val accountService: AccountService = FakeAccountService(), @get:Provides val userService: UserService = FakeUserService(), ) @Component @ApplicationScope abstract class TestApplicationComponent( @Component val fakes: TestFakes = TestFakes() ) class ProfileScreenTest { @Test fun uses_default_fakes() { val component = TestComponent::class.create() val screen = component.profileScreen // ... } @Test fun override_single_fake() { val fakes = TestFakes( accountService = object : FakeAccountService() { override fun getAccount(id: String) = throw IOException("network error") } ) val component = TestComponent::class.create( TestApplicationComponent::class.create(fakes) ) val screen = component.profileScreen // ... } @Component abstract class TestComponent( @Component val parent: TestApplicationComponent = TestApplicationComponent::class.create() ) { abstract val profileScreen: ProfileScreen } } ``` -------------------------------- ### Lazy Injection with Lazy Source: https://github.com/evant/kotlin-inject/blob/main/README.md Inject `Lazy` to construct and reuse an instance lazily. Access the instance using `by lazyFoo`. ```kotlin @Inject class Foo @Inject class MyClass(lazyFoo: Lazy) { val foo by lazyFoo } ``` -------------------------------- ### Inject into Top-Level Functions with Type Aliases Source: https://github.com/evant/kotlin-inject/blob/main/README.md Shows how to use type aliases for injecting dependencies into top-level functions. The function is annotated with `@Inject`, and a type alias with the same name is created. ```kotlin typealias myFunction = () -> Unit @Inject fun myFunction(dep: Dep) { } ``` -------------------------------- ### Constructor Injection with @Inject Source: https://context7.com/evant/kotlin-inject/llms.txt Annotate a class with @Inject to enable constructor injection. Kotlin Inject automatically resolves all constructor parameters from the dependency graph. ```kotlin @Inject class Database(private val config: DatabaseConfig) @Inject class UserRepository(private val db: Database) @Inject class AuthService(private val userRepo: UserRepository) @Component abstract class AppComponent { abstract val authService: AuthService @Provides fun databaseConfig(): DatabaseConfig = DatabaseConfig(url = "jdbc:sqlite:app.db") } val component = AppComponent::class.create() val auth: AuthService = component.authService ``` -------------------------------- ### Default Arguments for Injected Parameters Source: https://github.com/evant/kotlin-inject/blob/main/README.md Utilize default arguments for injected parameters. If the type is present in the graph, it will be injected; otherwise, the default value will be used. ```kotlin @Inject class MyClass(val dep: Dep = Dep("default")) @Component abstract class ComponentWithDep { abstract val myClass: MyClass @Provides fun dep(): Dep = Dep("injected") } @Component abstract class ComponentWithoutDep { abstract val myClass: MyClass } ComponentWithDep::class.create().myClass.dep // Dep("injected") ComponentWithoutDep::class.create().myClass.dep // Dep("default") ``` -------------------------------- ### Apply Scope to Component and Providers Source: https://github.com/evant/kotlin-inject/blob/main/README.md Applies the custom `@MyScope` annotation to both a component (`MyComponent`) and a provider method (`provideFoo`). This ensures that instances provided by this component are scoped to its lifecycle. ```kotlin @MyScope @Component abstract class MyComponent { @MyScope @Provides protected fun provideFoo(): Foo = ... ``` -------------------------------- ### Multi-binding into a Set Source: https://github.com/evant/kotlin-inject/blob/main/README.md Collects multiple `Foo` instances into a `Set` using the `@IntoSet` annotation. The component provides individual `Foo` instances, which are then aggregated into the set. ```kotlin @Component abstract class MyComponent { abstract val allFoos: Set @IntoSet @Provides protected fun provideFoo1(): Foo = Foo("1") @IntoSet @Provides protected fun provideFoo2(): Foo = Foo("2") } ``` -------------------------------- ### Injecting ViewModel Factory Function Source: https://github.com/evant/kotlin-inject/blob/main/docs/android.md Inject a function that creates the ViewModel to ensure it's retained across configuration changes. This is useful when ViewModels require dependencies. ```kotlin @Inject class HomeViewModel(private val repository: HomeRepository) : ViewModel() @Inject class HomeFragment(homeViewModel: () -> HomeViewModel) : Fragment() { private val viewModel by viewModels { viewModelFactory { addInitializer(HomeViewModel::class) { homeViewModel() } } } } ``` -------------------------------- ### Named Assisted Factory Interface Source: https://context7.com/evant/kotlin-inject/llms.txt Use a typed interface annotated with @AssistedFactory to inject factories with named parameters and default values, improving code clarity over raw functions. ```kotlin @Inject class Notification(@Assisted val message: String, @Assisted val level: Level = Level.INFO) @AssistedFactory interface NotificationFactory { fun create(message: String, level: Level = Level.INFO): Notification } @Inject class NotificationService(private val factory: NotificationFactory) { fun warn(msg: String) = factory.create(msg, Level.WARN) fun info(msg: String) = factory.create(msg) } ``` -------------------------------- ### Injecting ViewModel with SavedStateHandle Source: https://github.com/evant/kotlin-inject/blob/main/docs/android.md Inject a function that creates the ViewModel, including a SavedStateHandle, for ViewModels that require it. This allows for state restoration. ```kotlin @Inject class HomeViewModel(private val repository: HomeRepository, @Assisted handle: SavedStateHandle) : ViewModel() @Inject class HomeFragment(homeViewModel: (SavedStateHandle) -> HomeViewModel) : Fragment() { private val viewModel by viewModels { viewModelFactory { addInitializer(HomeViewModel::class) { homeViewModel(createSavedStateHandle()) } } } } ``` -------------------------------- ### Provide External Dependencies with @Provides Source: https://context7.com/evant/kotlin-inject/llms.txt Use @Provides on functions or properties within a @Component to supply types that cannot be annotated with @Inject, such as third-party classes or interfaces. The return type determines the provided type. ```kotlin @Component abstract class NetworkComponent { // Provide a third-party OkHttpClient @Provides protected fun okHttpClient(): OkHttpClient = OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .build() // Bind an implementation to an interface @Provides protected fun retrofit(client: OkHttpClient): ApiService = Retrofit.Builder() .baseUrl("https://api.example.com/") .client(client) .build() .create(ApiService::class.java) } @Inject class UserService(private val api: ApiService) ``` -------------------------------- ### Android Jetpack Compose Integration with @Inject and @Composable Source: https://context7.com/evant/kotlin-inject/llms.txt Integrate function injection with Compose by annotating a composable function with both `@Inject` and `@Composable`. This allows dependency injection into UI components. ```kotlin typealias HomeScreen = @Composable () -> Unit @Inject @Composable fun HomeScreen(repo: HomeRepository) { val items by repo.items.collectAsState() LazyColumn { items(items) { Text(it.title) } } } @Component abstract class AppComponent { abstract val homeScreen: HomeScreen } class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val homeScreen = AppComponent::class.create().homeScreen setContent { homeScreen() } } } ``` -------------------------------- ### Define a Custom Scope Annotation Source: https://github.com/evant/kotlin-inject/blob/main/README.md Defines a custom scope annotation (`@MyScope`) that can be applied to components and dependencies to control their lifecycle. This annotation must be annotated with `@Scope` and specify valid targets. ```kotlin @Scope @Target(CLASS, FUNCTION, PROPERTY_GETTER) annotation class MyScope ```