### Quick Start Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/index.md Demonstrates the basic setup for kotlin-inject-anvil, including defining a scope, contributing a component and bindings, merging contributions, and instantiating the component at runtime. ```kotlin // 1. Define a scope marker object AppScope // 2. Contribute a component interface @ContributesTo(AppScope::class) interface DatabaseComponent { @Provides fun provideDb(): Database = Database() } // 3. Contribute bindings @Inject @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) class UserRepositoryImpl : UserRepository // 4. Merge contributions into a component @MergeComponent(AppScope::class) @SingleIn(AppScope::class) interface AppComponent // 5. Instantiate at runtime val component = AppComponent::class.create() val db: Database = component.provideDb() ``` -------------------------------- ### GitHub Actions CI/CD Workflow Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/configuration.md Example GitHub Actions workflow for building and testing a project, including Java setup and Gradle execution. ```yaml name: Build and Test on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: 17 distribution: temurin - uses: gradle/gradle-build-action@v2 - run: ./gradlew build # KSP runs automatically during build ``` -------------------------------- ### Application-Level Singleton Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/SingleIn.md An example demonstrating the creation of application-level singletons for Logger and Database using @SingleIn. These instances are created once and reused throughout the application's lifecycle. ```kotlin object AppScope @Inject @SingleIn(AppScope::class) class Logger { fun log(message: String) = println("LOG: $message") } @Inject @SingleIn(AppScope::class) class Database { init { println("Connecting to database") } } @MergeComponent(AppScope::class) @SingleIn(AppScope::class) interface AppComponent // Usage val component = AppComponent::class.create() // Logger and Database are created once and reused val service1 = Service1(component.logger) val service2 = Service2(component.logger) // service1 and service2 share the same Logger instance ``` -------------------------------- ### Install Release to Maven Local Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/RELEASING.md Publish artifacts to the local Maven repository using Gradle. ```bash ./gradlew publishToMavenLocal ``` -------------------------------- ### Simple Provider Interface Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesTo.md Shows a basic contribution of a component interface with provider methods for application-specific identifiers and versions. ```kotlin // Define a scope marker object AppScope // Contribute a component interface with provider methods @ContributesTo(AppScope::class) interface AppIdComponent { @Provides fun provideAppId(): String = "com.example.app" @Provides fun provideVersion(): String = "1.0.0" } // Later, in another module... @MergeComponent(AppScope::class) interface AppComponent // The generated AppComponent will inherit from AppIdComponent // and can provide both String bindings ``` -------------------------------- ### Validate Project Setup for Kotlin-Inject Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/configuration.md A Gradle task to validate the project setup, checking Kotlin version, KSP plugin application, and Kotlin-Inject presence in the classpath. ```gradle task validateSetup { doLast { println "Kotlin version: ${kotlin_version}" println "KSP plugin applied: " + plugins.hasPlugin('com.google.devtools.ksp') println "kotlin-inject in classpath: " configurations.kspCommonMainMetadata.files.each { println " - $it.name" } } } ``` -------------------------------- ### Basic Component and Binding Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/README.md Demonstrates contributing a component interface with a provider method and a binding for a class. These are automatically merged into the final component. ```kotlin import software.amazon.lastmile.kotlin.inject.anvil.* import software.amazon.lastmile.kotlin.inject.anvil.scope.AppScope import software.amazon.lastmile.kotlin.inject.anvil.scope.SingleIn @ContributesTo(AppScope::class) interface AppIdComponent { @Provides fun provideAppId(): String = "demo app" } @Inject @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) class RealAuthenticator : Authenticator // The final kotlin-inject component. @MergeComponent(AppScope::class) @SingleIn(AppScope::class) interface AppComponent // Instantiate the component at runtime. val component = AppComponent::class.create() ``` -------------------------------- ### Test Component Setup Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/configuration.md Define test components using `@ContributesTo` with `replaces` to override production bindings, and `@MergeComponent` for the test application component. ```kotlin @ContributesTo( scope = AppScope::class, replaces = [DatabaseComponent::class] ) interface TestDatabaseComponent { @Provides fun provideDatabase(): Database = InMemoryDatabase() } @MergeComponent(AppScope::class) interface TestAppComponent ``` -------------------------------- ### Test Override Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesTo.md Demonstrates how to replace a production provider with a test implementation using the 'replaces' parameter for dependency injection testing. ```kotlin // Production provider @ContributesTo(AppScope::class) interface DatabaseComponent { @Provides fun provideDatabase(): Database = ProdDatabase() } // Test override (replaces the production provider) @ContributesTo( scope = AppScope::class, replaces = [DatabaseComponent::class], ) interface TestDatabaseComponent { @Provides fun provideDatabase(): Database = InMemoryDatabase() } // When testing, the test provider is used instead ``` -------------------------------- ### Sorted Processing Order Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/CompositeSymbolProcessor.md Demonstrates how processors are automatically sorted by their fully qualified class name, ensuring deterministic ordering. The execution order will be alphabetical based on class names. ```kotlin val processors = listOf( ZoneProcessor(), // "com.example.ZoneProcessor" AlphaProcessor(), // "com.example.AlphaProcessor" BetaProcessor(), // "com.example.BetaProcessor" ) val composite = CompositeSymbolProcessor(processors) // Execution order: AlphaProcessor, BetaProcessor, ZoneProcessor (alphabetical) ``` -------------------------------- ### Example: Factory Parameters as Subcomponent Constructor Parameters Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesSubcomponent.md Shows how parameters defined in the factory interface are automatically included as constructor parameters and @Provides bindings in the generated subcomponent. ```kotlin @ContributesSubcomponent(UserScope::class) interface UserComponent { @ContributesSubcomponent.Factory(AppScope::class) interface Factory { fun createUserComponent(userId: String, token: String): UserComponent } } // Generated subcomponent includes: abstract class UserComponentFinal( @Component val parentComponent: AppComponent, @get:Provides val userId: String, @get:Provides val token: String, ) : UserComponent, UserComponentMerged ``` -------------------------------- ### Multi-Module Architecture Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesTo.md Illustrates how @ContributesTo works across different modules to automatically include component interfaces in a merged component. ```kotlin Module A (core): @MergeComponent(AppScope::class) interface AppComponent Module B (auth): @ContributesTo(AppScope::class) interface AuthComponent { @Provides fun provideAuthenticator(): Authenticator = RealAuthenticator() } Module C (database): @ContributesTo(AppScope::class) interface DataComponent { @Provides fun provideUserDb(): UserDatabase = SqliteUserDatabase() } When AppComponent is merged, both AuthComponent and DataComponent are automatically included as super types. ``` -------------------------------- ### Thread-Safe Singleton Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/SingleIn.md Provides an example of a singleton class designed for thread safety using a ReentrantReadWriteLock, as recommended for shared singleton instances. ```kotlin @SingleIn(AppScope::class) class SharedState { private val lock = ReentrantReadWriteLock() // Use lock for thread-safe access } ``` -------------------------------- ### Singleton Creation and Reuse Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/SingleIn.md Demonstrates how to use @SingleIn to create a singleton DatabaseConnection that is reused across different injected classes. The instance is created only once per scope. ```kotlin @Inject @SingleIn(AppScope::class) class DatabaseConnection { init { println("Creating DB connection") } } // First injection @Inject class ServiceA(val db: DatabaseConnection) // Second injection @Inject class ServiceB(val db: DatabaseConnection) // Usage val component = AppComponent::class.create() val serviceA = ServiceA(component.db) // Creates DatabaseConnection val serviceB = ServiceB(component.db) // Reuses same DatabaseConnection instance ``` -------------------------------- ### Basic Contribution Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesTo.md Demonstrates how to use @ContributesTo to include an interface in a merged component. The generated component will inherit from the contributed interface. ```kotlin @ContributesTo(AppScope::class) interface AuthComponent { @Provides fun provideAuthenticator(): Authenticator = RealAuthenticator() } @MergeComponent(AppScope::class) interface AppComponent // The generated component will have AuthComponent as a super type ``` -------------------------------- ### Gradle Setup with KSP Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/REFERENCE.md Configures the Gradle build for Kotlin Inject Anvil using the KSP plugin and specifies compiler and runtime dependencies. ```gradle plugins { alias(libs.plugins.ksp) } dependencies { kspCommonMainMetadata "software.amazon.lastmile.kotlin.inject.anvil:compiler:$version" commonMainImplementation "software.amazon.lastmile.kotlin.inject.anvil:runtime:$version" commonMainImplementation "software.amazon.lastmile.kotlin.inject.anvil:runtime-optional:$version" } ``` -------------------------------- ### MergeScope Example: Equality and Hashing Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/MergeScope.md Demonstrates that two `MergeScope` instances are considered equal and have the same hash code if their underlying `type` properties are equal. ```kotlin val scope1 = MergeScope(appScopeType) val scope2 = MergeScope(appScopeType) scope1 == scope2 // true scope1.hashCode() == scope2.hashCode() // true ``` -------------------------------- ### Component with Parameters Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/README.md Define a component with parameters by using abstract classes and @get:Provides for constructor parameters. The component can then be instantiated with the required arguments. ```kotlin @MergeComponent(AppScope::class) @SingleIn(AppScope::class) abstract class AppComponent( @get:Provides val userId: String, ) val component = AppComponent::class.create("userId") ``` -------------------------------- ### MergeScope Example: Scope Marker and Contributions Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/MergeScope.md Illustrates how `AppScope` is defined as a marker, how contributions target it using `@ContributesTo` and `@ContributesBinding`, and how a component merges it using `@MergeComponent`. ```kotlin // Scope marker object AppScope // Contribution targets this scope @ContributesTo(AppScope::class) interface Component1 @ContributesBinding(AppScope::class) class Service1 // Component merges this scope @MergeComponent(AppScope::class) interface AppComponent // MergeScope internally represents AppScope for matching ``` -------------------------------- ### Define a Scoped Component Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/README.md This example shows how to define a component interface that is scoped to `AppScope` using `@MergeComponent` and `@SingleIn`. ```kotlin @MergeComponent(AppScope::class) @SingleIn(AppScope::class) // scope for kotlin-inject interface AppComponent ``` -------------------------------- ### Session-Scoped Singleton Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/SingleIn.md Demonstrates how to use @SingleIn with a custom scope (UserScope) to create session-specific singletons for UserSession and UserRepository. Services within the same session share these instances. ```kotlin object UserScope @Inject @SingleIn(UserScope::class) class UserSession(val userId: String) { val userData = mutableMapOf() } @Inject @SingleIn(UserScope::class) class UserRepository(val session: UserSession) { fun getUser() = session.userData["user"] } @ContributesBinding(UserScope::class) @SingleIn(UserScope::class) class UserRepositoryImpl(val session: UserSession) : UserRepository // Each user session gets its own instances // All services in that session share them ``` -------------------------------- ### Simple Subcomponent Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesSubcomponent.md Illustrates the basic setup of a subcomponent with its own bindings and a factory. This snippet shows how to define a UserComponent that provides a UserService and is contributed to the UserScope, with its factory being part of the AppComponent. ```kotlin // Scope markers object AppScope object UserScope // Library module (no dependency on app) @ContributesSubcomponent(UserScope::class) @SingleIn(UserScope::class) interface UserComponent { @Provides fun provideUserService(): UserService = UserService() val userService: UserService @ContributesSubcomponent.Factory(AppScope::class) interface Factory { fun createUserComponent(): UserComponent } } @ContributesTo(UserScope::class) interface UserBindingsComponent { @Provides fun provideUserRepository(): UserRepository = UserRepositoryImpl() } // App module @MergeComponent(AppScope::class) @SingleIn(AppScope::class) abstract class AppComponent { abstract fun userComponentFactory(): UserComponent.Factory } // Usage val appComponent = AppComponent::class.create() val userComponent = appComponent.userComponentFactory().createUserComponent() val service: UserService = userComponent.userService ``` -------------------------------- ### Multi-Module Subcomponent Setup Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesSubcomponent.md Shows how to structure subcomponents across multiple modules. The AppComponent in the core module defines a factory for UserComponent, while UserModule and FeatureModule (libraries) contribute their respective bindings and components to the UserScope. ```kotlin object AppScope object UserScope @MergeComponent(AppScope::class) interface AppComponent { fun userComponentFactory(): UserComponent.Factory } @ContributesSubcomponent(UserScope::class) interface UserComponent { @ContributesSubcomponent.Factory(AppScope::class) interface Factory { fun createUserComponent(): UserComponent } } @ContributesTo(UserScope::class) interface FeatureComponent { @Provides fun provideFeatureService(): FeatureService = FeatureService() val featureService: FeatureService } @Inject @ContributesBinding(UserScope::class) class FeatureRepository : Repository // When AppComponent is merged: // - UserComponent is generated with all contributions to UserScope // - FeatureComponent and FeatureRepository are merged into UserComponent // - No explicit dependencies between UserModule and FeatureModule ``` -------------------------------- ### Example: Declared Subcomponent and Factory Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesSubcomponent.md Illustrates how to declare a subcomponent and its factory using @ContributesSubcomponent. The factory's scope parameter links it to the parent component. ```kotlin // Declared: @ContributesSubcomponent(LoggedInScope::class) interface LoggedInComponent { @ContributesSubcomponent.Factory(AppScope::class) interface Factory { fun createLoggedInComponent(): LoggedInComponent } } // Generated in parent scope: interface AppComponentMerged : LoggedInComponent.Factory { override fun createLoggedInComponent(): LoggedInComponent { return LoggedInComponentFinal.create(this) } } ``` -------------------------------- ### Define a Custom Annotation Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/README.md This example shows how to define a custom annotation `@MyCustomAnnotation` that can be used as a trigger for custom KSP symbol processors. ```kotlin @Target(CLASS) @ContributingAnnotation // see below for details annotation class MyCustomAnnotation ``` -------------------------------- ### Process Symbols in Stages Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/CompositeSymbolProcessor.md This example defines three distinct SymbolProcessors for different stages: metadata collection, code generation, and validation. The StagedProcessorProvider then combines these into a CompositeSymbolProcessor for sequential execution. ```kotlin // First stage: collect metadata class MetadataCollector : SymbolProcessor { override fun process(resolver: Resolver): List { // Analyze all @Annotated classes // Store metadata for use by later processors return emptyList() } } // Second stage: generate code class CodeGenerator : SymbolProcessor { override fun process(resolver: Resolver): List { // Use metadata from MetadataCollector // Generate components and bindings return emptyList() } } // Third stage: validate class ValidatorProcessor : SymbolProcessor { override fun process(resolver: Resolver): List { // Validate generated code // Report errors return emptyList() } } @AutoService(SymbolProcessorProvider::class) class StagedProcessorProvider : SymbolProcessorProvider { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { return CompositeSymbolProcessor(listOf( MetadataCollector(), CodeGenerator(), ValidatorProcessor(), )) } } ``` -------------------------------- ### Component Generation Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/MergeComponent.md Illustrates the transformation from a declared @MergeComponent interface to a generated concrete kotlin-inject component, including the creation of a 'create()' extension function. ```kotlin // Declared: @MergeComponent(AppScope::class) @SingleIn(AppScope::class) interface AppComponent // Generated (simplified): @Component @SingleIn(AppScope::class) class KotlinInjectAppComponent : AppComponent, ContributedComponent1, ContributedComponent2 { // All provider methods from contributions } // Extension function: fun KClass.create(): AppComponent = KotlinInjectAppComponent::class.create() ``` -------------------------------- ### MergeScope Example: Scope Matching Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/MergeScope.md Shows that contributions and components are matched based on the equality of their `MergeScope` types, requiring consistency across annotations. ```kotlin // All of these must use the same scope type @ContributesTo(AppScope::class) // MergeScope(AppScope type) @ContributesBinding(AppScope::class) // MergeScope(AppScope type) @MergeComponent(AppScope::class) // MergeScope(AppScope type) // Contributions are merged because scopes match ``` -------------------------------- ### Replacement Mechanism Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesTo.md Illustrates using the 'replaces' parameter to substitute one contributed interface with another, useful for testing or conditional bindings. ```kotlin @ContributesTo( scope = AppScope::class, replaces = [ProdAuthComponent::class], ) interface TestAuthComponent { @Provides fun provideAuthenticator(): Authenticator = FakeAuthenticator() } ``` -------------------------------- ### MergeScope Example Usage in Custom Processor Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/MergeScope.md Demonstrates how to extract and use MergeScope within a custom KSP SymbolProcessor to identify target scopes for generated components. ```kotlin class CustomProcessor : SymbolProcessor, ContextAware { override fun process(resolver: Resolver): List { // Find all @MyAnnotation classes return resolver.getSymbolsWithAnnotation(MyAnnotation::class) .mapNotNull { it as? KSClassDeclaration } .map { // Extract scope val mergeScope = clazz.scope() logger.info("Class ${clazz.simpleName.asString()} targets scope: ${mergeScope.type}") // Generate component for this scope generateComponent(clazz, mergeScope) null // Processed } .toList() } } ``` -------------------------------- ### Symbol Collection Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/CompositeSymbolProcessor.md Illustrates how CompositeSymbolProcessor collects all unprocessed symbols from multiple processors. The returned list contains symbols from all individual processors. ```kotlin class Processor1 : SymbolProcessor { override fun process(resolver: Resolver): List { return listOf(unprocessedSymbol1, unprocessedSymbol2) } } class Processor2 : SymbolProcessor { override fun process(resolver: Resolver): List { return listOf(unprocessedSymbol3) } } val composite = CompositeSymbolProcessor(listOf(Processor1(), Processor2())) val unprocessed = composite.process(resolver) // unprocessed contains: unprocessedSymbol1, unprocessedSymbol2, unprocessedSymbol3 ``` -------------------------------- ### JSR-330 Compatibility Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/SingleIn.md Shows how @SingleIn on JVM/Android is compatible with JSR-330 frameworks like Dagger 2. The same annotation can be used for both kotlin-inject and Dagger 2. ```kotlin @SingleIn(AppScope::class) class Repository { // This is valid for both kotlin-inject and Dagger 2 on JVM } ``` -------------------------------- ### Multiple Scopes Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/SingleIn.md Illustrates using @SingleIn with different scopes (AppScope, FeatureScope) to manage singletons at application and feature levels. AppConfig is a singleton in AppScope, while FeatureState is a singleton in FeatureScope. ```kotlin object AppScope object FeatureScope @Inject @SingleIn(AppScope::class) class AppConfig { val appName = "MyApp" } @Inject @SingleIn(FeatureScope::class) class FeatureState { val isEnabled = true } @ContributesTo(AppScope::class) interface AppComponent { @Provides fun provideAppConfig(): AppConfig = AppConfig() } @ContributesSubcomponent(FeatureScope::class) interface FeatureComponent { @ContributesSubcomponent.Factory(AppScope::class) interface Factory { fun createFeature(): FeatureComponent } } // AppConfig is a singleton in app scope // FeatureState is a singleton in feature scope // Each feature gets its own FeatureState instance ``` -------------------------------- ### Minimal Test Case for Kotlin-Inject Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/errors.md A minimal setup for testing Kotlin-Inject components. It includes a scope, a component interface, and an application interface, designed to be compiled to check basic functionality. ```kotlin // Minimal: Single class, single annotation object TestScope @ContributesTo(TestScope::class) interface TestComponent @MergeComponent(TestScope::class) interface App // Try to compile ``` -------------------------------- ### Registering a Custom KSP Processor Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/errors.md To ensure your custom processor runs, mark its annotation with @ContributingAnnotation, register the processor provider with @AutoService, and add the processor to the KSP classpath. This setup is crucial for code generation. ```kotlin // Mark annotation @ContributingAnnotation @Target(CLASS) annotation class MyAnnotation // Register processor @AutoService(SymbolProcessorProvider::class) class MyProcessorProvider : SymbolProcessorProvider { override fun create(env: SymbolProcessorEnvironment) = MyProcessor() } // Add to KSP classpath dependencies { kspCommonMainMetadata "com.example:my-processor:1.0" } ``` -------------------------------- ### Chaining Subcomponents Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesSubcomponent.md Demonstrates how subcomponents can be nested, allowing a child component to have its own @ContributesSubcomponent declarations with another parent. This is useful for creating deeply hierarchical dependency injection structures. ```kotlin object AppScope object UserScope @ContributesSubcomponent(UserScope::class) interface UserComponent { fun screenComponentFactory(): ScreenComponent.Factory @ContributesSubcomponent.Factory(AppScope::class) interface Factory { fun createUserComponent(userId: String): UserComponent } } @ContributesSubcomponent(ScreenScope::class) @SingleIn(ScreenScope::class) interface ScreenComponent { val screenService: ScreenService @ContributesSubcomponent.Factory(UserScope::class) interface Factory { fun createScreenComponent(screenId: String): ScreenComponent } } // Usage val appComponent = AppComponent::class.create() val userComponent = appComponent.userComponentFactory().createUserComponent("user1") val screenComponent = userComponent.screenComponentFactory().createScreenComponent("screen1") ``` -------------------------------- ### Get Factory Functions from Class Declaration Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md Obtains a sequence of all abstract methods within a class, typically used for factory interfaces in dependency injection setups. ```kotlin fun KSClassDeclaration.factoryFunctions(): Sequence ``` ```kotlin val factories = clazz.factoryFunctions() // Sequence of abstract fun ``` -------------------------------- ### Basic ContextAware Implementation Pattern Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md Demonstrates the basic pattern for implementing the ContextAware interface within a custom SymbolProcessor, including logger initialization. ```kotlin class MyProcessor(environment: SymbolProcessorEnvironment) : SymbolProcessor, ContextAware { override val logger = environment.logger override fun process(resolver: Resolver): List { // Use context-aware helpers checkIsPublic(myClass) val scope = myClass.scope() // ... rest of processing } } ``` -------------------------------- ### Getting Symbols with Multiple Annotations Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md The getSymbolsWithAnnotations extension function on Resolver finds all symbols annotated with any of the provided KClasses. ```kotlin fun Resolver.getSymbolsWithAnnotations(vararg annotations: KClass<*>): Sequence ``` -------------------------------- ### Custom Annotation for Route Handlers Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributingAnnotation.md Example of defining a custom @Route annotation and a processor that generates @ContributesTo components for route handlers. Usage of the custom annotation and the resulting generated code are shown. ```kotlin // Define your annotation @ContributingAnnotation @Target(CLASS) annotation class Route(val path: String) // Custom processor generates contributions class RouteProcessor : SymbolProcessor { override fun process(resolver: Resolver): List { resolver.getSymbolsWithAnnotation("com.example.Route") .forEach { symbol -> // Generate code like: // @ContributesTo(AppScope::class) // interface GeneratedRouteComponent { // @Provides fun provide...: Handler = ... // } } return emptyList() } } // Usage @Route("/api/users") class UserHandler // Generated by your processor: @ContributesTo(AppScope::class) interface GeneratedUserRouteComponent { @Provides fun provideUserHandler(): UserHandler = UserHandler() } // Automatically merged into AppComponent ``` -------------------------------- ### Getting Symbols with a Specific Annotation Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md The getSymbolsWithAnnotation extension function on Resolver finds all symbols annotated with a given KClass. ```kotlin fun Resolver.getSymbolsWithAnnotation(annotation: KClass<*>): Sequence ``` -------------------------------- ### Basic Application Component with AppScope Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/AppScope.md Shows how to define application-level singletons like Logger and Database using @SingleIn(AppScope::class) and contribute them to an AppComponent. ```kotlin @Inject @SingleIn(AppScope::class) class Logger { fun log(message: String) = println(message) } @Inject @SingleIn(AppScope::class) class Database { fun query(sql: String) = listOf("result1", "result2") } @ContributesTo(AppScope::class) interface AppComponent { val logger: Logger val database: Database } @MergeComponent(AppScope::class) @SingleIn(AppScope::class) interface AppComponent // Usage val component = AppComponent::class.create() val logger: Logger = component.logger // Reused across app ``` -------------------------------- ### Getting Required Parameter Name Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md The requireName extension function retrieves the name of a KSValueParameter. It throws an exception if the name is not available. ```kotlin fun KSValueParameter.requireName(): String ``` -------------------------------- ### Best Practice: App-Level Singletons Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/AppScope.md Illustrates the best practice of using AppScope for true app-level singletons like a Logger, and avoiding dependencies on short-lived services like UserPreferences which might depend on user context. ```kotlin @SingleIn(AppScope::class) // For app-level services class Logger object UserScope // For user-scoped services @SingleIn(UserScope::class) class UserService ``` -------------------------------- ### Basic Single-Module Project Structure Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/configuration.md Illustrates a typical directory structure for a basic single-module project using Kotlin Inject Anvil, showing the placement of source files within the commonMain and commonTest directories. ```text myapp/ ├── src/ │ ├── commonMain/kotlin/ │ │ ├── com/example/AppScope.kt │ │ ├── com/example/AppComponent.kt │ │ ├── com/example/User.kt │ │ └── com/example/AuthComponent.kt │ └── commonTest/kotlin/ ├── build.gradle └── settings.gradle ``` -------------------------------- ### Subcomponent with Parameters Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesSubcomponent.md Demonstrates how to define a subcomponent factory that accepts parameters. This allows for creating instances of the subcomponent with specific data, such as user IDs and tokens, which can then be used within the subcomponent's bindings. ```kotlin object AppScope object UserScope @ContributesSubcomponent(UserScope::class) @SingleIn(UserScope::class) interface UserComponent { val userRepository: UserRepository @ContributesSubcomponent.Factory(AppScope::class) interface Factory { fun createUserComponent(userId: String, token: String): UserComponent } } @ContributesTo(UserScope::class) interface UserRepositoryComponent { @Provides fun provideUserRepository(userId: String): UserRepository { return UserRepositoryImpl(userId) // userId is bound in UserComponent } } // Usage val appComponent = AppComponent::class.create() val userComponent = appComponent.userComponentFactory() .createUserComponent(userId = "user123", token = "secret") // userId and token are now available as @Provides in UserComponent ``` -------------------------------- ### Multi-Module Architecture Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/REFERENCE.md Illustrates the recommended directory structure for a multi-module project using Kotlin Inject Anvil. ```kotlin core/ ├── AppScope.kt └── AppComponent.kt # @MergeComponent auth/ └── AuthComponent.kt # @ContributesTo(AppScope::class) database/ └── DatabaseComponent.kt # @ContributesTo(AppScope::class) app/ └── main.kt # Use AppComponent::class.create() ``` -------------------------------- ### Assisted Injection with AppScope Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/AppScope.md Shows how to use assisted injection within a class scoped to AppScope. Demonstrates a CacheManager singleton and a DataService that uses both the singleton and a factory for creating string values. ```kotlin @Inject @SingleIn(AppScope::class) class CacheManager(val logger: Logger) { private val cache = mutableMapOf() fun put(key: String, value: String) { logger.log("Caching: $key") cache[key] = value } } @Inject class DataService( val cache: CacheManager, // Reused across app cacheFactory: (String) -> String, // Created per request ) @MergeComponent(AppScope::class) interface AppComponent ``` -------------------------------- ### Get Fully-Qualified Name of Declaration Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md Retrieves the fully-qualified name of a KSDeclaration. Throws an exception if the name is not available, ensuring a complete identifier. ```kotlin fun KSDeclaration.requireQualifiedName(): String ``` ```kotlin val fqName = clazz.requireQualifiedName() // Returns: "com.example.MyClass" ``` -------------------------------- ### Defining Custom Scopes and Components Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/AppScope.md Illustrates how to define custom scopes like AppScope and FeatureScope, and how to contribute components to them. Shows how dependencies from parent scopes are available in child scopes. ```kotlin object FeatureScope @ContributesTo(FeatureScope::class) interface FeatureComponent { @Provides fun provideFeature(config: Config): Feature = Feature(config) // Config from AppScope is available here } @MergeComponent(AppScope::class) interface AppComponent @ContributesSubcomponent(FeatureScope::class) interface FeatureComponent { @ContributesSubcomponent.Factory(AppScope::class) interface Factory { fun create(): FeatureComponent } } val appComponent = AppComponent::class.create() val featureComponent = appComponent.featureComponentFactory().create() // FeatureComponent can access AppScope singletons ``` -------------------------------- ### Type Error: Only interfaces can be contributed Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/errors.md The @ContributesTo annotation can only be applied to interfaces. This example shows how to fix the error by changing the annotated type to an interface. ```kotlin // ❌ Wrong @ContributesTo(AppScope::class) class MyComponent // ✅ Correct @ContributesTo(AppScope::class) interface MyComponent ``` -------------------------------- ### Merge Contributions into Component Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/REFERENCE.md Merges all contributions into a component interface, typically the root component. This example shows how to create an instance of the merged component. ```kotlin @MergeComponent(AppScope::class) @SingleIn(AppScope::class) interface AppComponent val component = AppComponent::class.create() ``` -------------------------------- ### Component Inheritance Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/MergeComponent.md Illustrates how @MergeComponent preserves explicitly declared super types and adds contributed super types to the generated component. ```kotlin @MergeComponent(AppScope::class) abstract class AppComponent : BaseComponent, DatabaseComponent { abstract val logger: Logger } // Generated component includes: // - All methods/properties from BaseComponent // - All contributions to AppScope // - Must implement abstract property logger ``` -------------------------------- ### Getting Required Containing File Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md The requireContainingFile extension function retrieves the KSFile that contains a given KSDeclaration. It throws an exception if the file cannot be determined. ```kotlin fun KSDeclaration.requireContainingFile(): KSFile ``` -------------------------------- ### Component Instantiation with Parameters Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/MergeComponent.md Shows how to instantiate a component generated by @MergeComponent when the annotated abstract class has constructor parameters. These parameters are passed to the 'create()' function. ```kotlin // With parameters @MergeComponent(AppScope::class) abstract class AppComponent( @get:Provides val userId: String, ) val component = AppComponent::class.create("user123") ``` -------------------------------- ### Testing with AppScope Replacements Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/AppScope.md Demonstrates how to use AppScope in tests by replacing production components with test implementations. This allows for isolated testing of components that depend on AppScope singletons. ```kotlin @MergeComponent(AppScope::class) interface AppComponent // Production setup @ContributesTo(AppScope::class) interface DatabaseComponent { @Provides fun provideDb(): Database = ProdDatabase() } // Test setup (in test source set) @ContributesTo( scope = AppScope::class, replaces = [DatabaseComponent::class] ) interface TestDatabaseComponent { @Provides fun provideDb(): Database = InMemoryDatabase() } // Same AppScope, different implementations depending on build variant ``` -------------------------------- ### Getting Inner Class Names Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md The innerClassNames extension function generates a string of inner class names from a KSClassDeclaration, optionally using a separator. ```kotlin fun KSClassDeclaration.innerClassNames(separator: String = ""): String ``` -------------------------------- ### Best Practice: Hierarchical Scopes Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/AppScope.md Demonstrates the use of hierarchical scopes, with AppScope at the root and custom scopes like UserScope and ScreenScope for child contexts. This promotes a clear structure for managing dependencies. ```kotlin // App root @MergeComponent(AppScope::class) interface AppComponent // User context (child of app) @ContributesSubcomponent(UserScope::class) interface UserComponent // Screen context (child of user) @ContributesSubcomponent(ScreenScope::class) interface ScreenComponent ``` -------------------------------- ### Define a Scope Marker Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/README.md Scope classes are simple markers used to connect contributions with merged components. This example shows a basic `AppScope` object. ```kotlin object AppScope ``` -------------------------------- ### Multi-Layer Scoping with AppScope Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/AppScope.md Demonstrates a multi-layer scoping hierarchy using AppScope for the application lifetime, UserScope for user sessions, and ScreenScope for screen states. ```kotlin // App scope (entire app lifetime) @SingleIn(AppScope::class) @Inject class AppConfig { val appName = "MyApp" val version = "1.0" } // User scope (login to logout) object UserScope @SingleIn(UserScope::class) @Inject class UserSession(val userId: String) // Screen scope (screen shown to dismissed) object ScreenScope @SingleIn(ScreenScope::class) @Inject class ScreenState @MergeComponent(AppScope::class) interface AppComponent { fun userComponentFactory(): UserComponent.Factory } @ContributesSubcomponent(UserScope::class) interface UserComponent { fun screenComponentFactory(): ScreenComponent.Factory @ContributesSubcomponent.Factory(AppScope::class) interface Factory { fun create(userId: String): UserComponent } } // AppConfig is a singleton in app scope // UserSession is a singleton in user scope // ScreenState is a singleton in screen scope ``` -------------------------------- ### Multi-binding Support with Set Contribution Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesBinding.md Demonstrates how to use the 'multibinding = true' parameter to contribute a binding to a Set multi-binding. ```kotlin @Inject @ContributesBinding(AppScope::class, multibinding = true) class LoggingInterceptor : Interceptor @MergeComponent(AppScope::class) interface AppComponent { val interceptors: Set // Collects all @IntoSet contributions } ``` -------------------------------- ### Getting Required Kotlin Inject Scope Annotation Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md The requireKotlinInjectScope function retrieves the kotlin-inject scope annotation from a KSClassDeclaration. It throws an exception if the annotation is not found. ```kotlin fun requireKotlinInjectScope(clazz: KSClassDeclaration): KSAnnotation ``` -------------------------------- ### Platform-specific Component Creation Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/configuration.md Provide the `actual` implementation for the component creation function in platform-specific source sets (e.g., androidMain, iosMain). ```kotlin @MergeComponent.CreateComponent actual fun create(): AppComponent { return KotlinInjectAppComponent::class.create() } ``` -------------------------------- ### Getting Required Qualified Name of a Declaration Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md The requireQualifiedName extension function retrieves the fully qualified name of a KSDeclaration. It throws an exception if the name is not available. ```kotlin fun KSDeclaration.requireQualifiedName(): String ``` -------------------------------- ### Component Instantiation with No Parameters Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/MergeComponent.md Demonstrates how to instantiate a component generated by @MergeComponent when the annotated interface has no constructor parameters. ```kotlin // No parameters @MergeComponent(AppScope::class) interface AppComponent val component = AppComponent::class.create() ``` -------------------------------- ### Find Generated Component Files Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/errors.md Provides a command to search for generated component files within the `.gradle/generated` directory. This is useful for inspecting the output of the code generation process. ```bash find .gradle/generated -name "*.kt" | grep -i component ``` -------------------------------- ### Get Symbols Annotated with Multiple Classes Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md Finds all symbols annotated with any of the provided annotation KClasses. This allows for querying symbols that match one of several criteria. ```kotlin fun Resolver.getSymbolsWithAnnotations(vararg annotations: KClass<*>): Sequence ``` ```kotlin val allContributions = resolver.getSymbolsWithAnnotations( ContributesTo::class, ContributesBinding::class, ) ``` -------------------------------- ### Binding Error: Class does not implement bound type Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/errors.md Ensure that the class being bound implements the type specified in `boundType`. This example corrects the `boundType` to match the implemented interface. ```kotlin // ❌ Wrong interface Service interface Logger @Inject @ContributesBinding(AppScope::class, boundType = Logger::class) class ServiceImpl : Service // Doesn't implement Logger // ✅ Correct @Inject @ContributesBinding(AppScope::class, boundType = Service::class) class ServiceImpl : Service ``` -------------------------------- ### Define Kotlin, KSP, and Kotlin-Inject Versions Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/errors.md Sets up common versions for Kotlin, KSP, and Kotlin-Inject libraries in the Gradle build script to manage dependencies and resolve version conflicts. ```gradle ext { kotlin_version = '1.9.20' ksp_version = '1.9.20-1.0.13' kotlin_inject_version = '0.8.0' kotlin_inject_anvil_version = '0.2.0' } ``` -------------------------------- ### Scope Error: All scopes on annotations must be the same Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/errors.md When a class has multiple scope annotations, they must all specify the same scope value. This example shows how to correct differing scope values. ```kotlin // ❌ Wrong @Inject @SingleIn(AppScope::class) @ContributesBinding(UserScope::class) // Different scope! class MyService // ✅ Correct @Inject @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) // Same scope class MyService ``` -------------------------------- ### Error Handling with ContextAware Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md Illustrates the recommended pattern for error handling using ContextAware, which logs errors with context and throws exceptions for clear feedback. ```kotlin // Good: Uses ContextAware for errors check(isValid, symbol) { "Detailed error message with context" } // Logs the error and throws, providing clear feedback ``` -------------------------------- ### MergeScope Example: Extracting Fully Qualified Name Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/MergeScope.md Illustrates how to retrieve the fully qualified name of a scope from a `MergeScope` object using KSP's `KSType` information. ```kotlin val mergeScope = clazz.scope() val fqName = mergeScope.type.declaration.requireQualifiedName() // Example: "com.example.AppScope" ``` -------------------------------- ### GitLab CI/CD Build Job Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/configuration.md Basic GitLab CI configuration for building a project using a Gradle Docker image. ```yaml build: image: gradle:latest script: - gradle build # KSP runs automatically ``` -------------------------------- ### Single Module Architecture Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/REFERENCE.md Illustrates the directory structure for a single-module project using Kotlin Inject Anvil. ```kotlin src/commonMain/ ├── com/example/AppScope.kt ├── com/example/service/ServiceComponent.kt ├── com/example/database/DatabaseComponent.kt └── com/example/AppComponent.kt # @MergeComponent ``` -------------------------------- ### Document Processor Order Dependencies Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/CompositeSymbolProcessor.md If one processor depends on the output of another, document this dependency clearly. Consider naming conventions, like prefixing with an underscore, to enforce execution order. ```kotlin // MetadataProcessor must run before CodeGenerator // Alphabetical order ensures: CodeGenerator before MetadataProcessor // Consider naming: _MetadataProcessor to run first class _MetadataProcessor : SymbolProcessor // Runs first class MetadataConsumer : SymbolProcessor // Runs second ``` -------------------------------- ### Validate Replaced Classes Have Same Scope Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md Ensures that classes specified in the `replaces` parameter of an annotation share the same scope. This is a crucial validation step for correct dependency injection setup. ```kotlin fun checkReplacesHasSameScope(clazz: KSClassDeclaration, annotations: List) ``` ```kotlin checkReplacesHasSameScope(clazz, clazz.annotations.toList()) ``` -------------------------------- ### Get Merged Class Name Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md Generates a name for a merged interface by appending 'Merged' to the original class name. Used for creating composite interfaces in dependency injection. ```kotlin val KSClassDeclaration.mergedClassName: String ``` ```kotlin // For a class "AppComponent" val merged = clazz.mergedClassName // "AppComponentMerged" ``` -------------------------------- ### Manually Upload Release Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/RELEASING.md Manually publish a release to Maven Central using Gradle. This command's behavior (production vs. snapshot) depends on the version specified in gradle.properties. ```bash ./gradlew clean publish --no-build-cache ``` -------------------------------- ### Get Safe Class Name Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md Converts a package-separated class name into a PascalCase string suitable for safe use as an identifier. Transforms names like 'com.example.MyClass' to 'ComExampleMyClass'. ```kotlin val KSClassDeclaration.safeClassName: String ``` ```kotlin // For "software.amazon.lastmile.MyClass" val safe = clazz.safeClassName // "SoftwareAmazonLastmileMyClass" ``` -------------------------------- ### Manual vs Automatic Binding Generation Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContributesBinding.md Compares the manual approach of defining provider methods with the automatic generation provided by @ContributesBinding. ```kotlin // Without @ContributesBinding (manual approach): @Inject class RealAuthenticator : Authenticator @ContributesTo(AppScope::class) interface AuthComponent { @Provides fun provideAuthenticator(authenticator: RealAuthenticator): Authenticator = authenticator } // With @ContributesBinding (automatic): @Inject @ContributesBinding(AppScope::class) class RealAuthenticator : Authenticator // The provider method is generated automatically ``` -------------------------------- ### Get Inner Class Names Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md Concatenates the names of inner classes within a KSClassDeclaration, optionally using a separator. Useful for generating unique names for nested types. ```kotlin fun KSClassDeclaration.innerClassNames(separator: String = ""): String ``` ```kotlin val name = clazz.innerClassNames() // "Outer.Inner" val name = clazz.innerClassNames("$") // "Outer$Inner" ``` -------------------------------- ### Get Symbols Annotated with a Specific Class Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ContextAware.md Finds all symbols (classes, functions, properties) within a Resolver that are annotated with a given KClass. Useful for discovering annotated elements. ```kotlin fun Resolver.getSymbolsWithAnnotation(annotation: KClass<*>): Sequence ``` ```kotlin val allContributions = resolver.getSymbolsWithAnnotation(ContributesTo::class) ``` -------------------------------- ### Using AppScope as a Scope Marker Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/AppScope.md Demonstrates the correct usage of AppScope::class as a reference in annotations like @SingleIn, @ContributesTo, and @MergeComponent to define application-level scopes. ```kotlin @SingleIn(AppScope::class) // Singleton for entire app @ContributesTo(AppScope::class) // Contributes to app-level component @MergeComponent(AppScope::class) // Merges app-level contributions ``` -------------------------------- ### Database Connections with Scopes Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/ForScope.md Illustrates how to provide different database implementations based on scope. A shared database for the app scope and a user-specific database for the user scope are defined using @ForScope. ```kotlin object AppScope object UserScope interface Database @Inject @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) @ForScope(AppScope::class) class SharedDatabase : Database { override fun query(sql: String) = "shared: $sql" } @Inject @SingleIn(UserScope::class) @ContributesBinding(UserScope::class) @ForScope(UserScope::class) class UserDatabase : Database { override fun query(sql: String) = "user-specific: $sql" } @Inject class ConfigService( @ForScope(AppScope::class) val db: Database ) @Inject @SingleIn(UserScope::class) class UserProfileService( @ForScope(UserScope::class) val db: Database ) ``` -------------------------------- ### Configure Maven Repository for Snapshot Builds Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/configuration.md Add the Sonatype Maven snapshots repository to access bleeding-edge versions of Kotlin-Inject Anvil. ```gradle repositories { maven { url = 'https://central.sonatype.com/repository/maven-snapshots/' } } dependencies { kspCommonMainMetadata "software.amazon.lastmile.kotlin.inject.anvil:compiler:0.3.0-SNAPSHOT" } ``` -------------------------------- ### Enable Verbose Logging for KSP Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/configuration.md Enable verbose logging for KSP by setting the 'verbose' argument to 'true' in the ksp configuration. ```gradle ksp { arg("verbose", "true") } ``` -------------------------------- ### Scope Error: Couldn't find scope for [Class] Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/errors.md Classes that require a scope must have a scope annotation like @SingleIn. This example demonstrates adding the necessary scope annotation. ```kotlin // ❌ Wrong @Inject @ContributesBinding(AppScope::class) class MyService // ✅ Correct @Inject @SingleIn(AppScope::class) @ContributesBinding(AppScope::class) class MyService ``` -------------------------------- ### Defining Clear Scope Markers Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/SingleIn.md Illustrates the best practice of using object declarations to define clear, immutable markers for custom scopes like AppScope and UserScope. ```kotlin object AppScope object UserScope ``` -------------------------------- ### Configure Parallel Build and Workers Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/configuration.md Enable parallel builds and set the maximum number of worker cores for Gradle. ```properties org.gradle.parallel=true org.gradle.workers.max= ``` -------------------------------- ### Scope Correlation Example Source: https://github.com/amzn/kotlin-inject-anvil/blob/main/_autodocs/api-reference/SingleIn.md Illustrates the importance of correlating scope markers across @SingleIn, @MergeComponent, and @ContributesTo annotations. All annotations must use the same scope marker class (e.g., AppScope). ```kotlin object AppScope // Scope marker @Inject @SingleIn(AppScope::class) class Repository @ContributesTo(AppScope::class) interface ServiceComponent { } @MergeComponent(AppScope::class) interface AppComponent // All three use AppScope::class ```