Build Source API Documentation License

Dagger Hilt Extensions

Maven Central Sonatype Nexus (Snapshots)

Build Configuration

Kotlin

dependencies {
    implementation("com.google.dagger:hilt-android:2.45")
    implementation("it.czerwinski.android.hilt:hilt-extensions:[VERSION]")

    // Using kapt:
    kapt("it.czerwinski.android.hilt:hilt-processor:[VERSION]")

    // Using KSP (recommended):
    ksp("it.czerwinski.android.hilt:hilt-processor-ksp:[VERSION]")
}

Groovy

dependencies {
    implementation 'com.google.dagger:hilt-android:2.45'
    implementation 'it.czerwinski.android.hilt:hilt-extensions:[VERSION]'

    // Using kapt:
    kapt 'it.czerwinski.android.hilt:hilt-processor:[VERSION]'

    // Using KSP (recommended):
    ksp 'it.czerwinski.android.hilt:hilt-processor-ksp:[VERSION]'
}

Property Delegation

With this library, it is possible to delegate properties to additional objects.

dagger.Lazy

val lazy: dagger.Lazy<String>

val property: String by lazy

javax.inject.Provider

val intProvider: Provider<Int>

val property: Int by intProvider

Generating Hilt Modules

@Bound and @BoundTo

Marks implementation bound to the given supertype in the given component.

@Bound annotation (added in v1.1.0) works exactly like @BoundTo annotation, but it implicitly uses the direct supertype of the annotated class. It may only annotate classes having exactly one direct supertype, excluding java.lang.Object.

For example:

interface Repository

@BoundTo(supertype = Repository::class)
class RepositoryA @Inject constructor() : Repository

@BoundTo(supertype = Repository::class, component = SingletonComponent::class)
@Singleton
@Named("online")
class RepositoryB @Inject constructor() : Repository

@Bound(component = SingletonComponent::class)
@Named("offline")
class RepositoryC @Inject constructor() : Repository

will generate module:

@Module
@InstallIn(SingletonComponent::class)
public interface Repository_SingletonComponent_BindingsModule {

    @Binds
    public fun bindRepositoryA(implementation: RepositoryA): Repository

    @Binds
    @Singleton
    @Named("online")
    public fun bindRepositoryB(implementation: RepositoryB): Repository

    @Binds
    @Named("offline")
    public fun bindRepositoryC(implementation: RepositoryC): Repository
}

Since release 1.1.0, component property is optional, and set to SingletonComponent by default.

@FactoryMethod

Marks factory method for the class returned by the annotated function.

For example, for a Room database:

@Database(
    entities = [
        User::class
    ],
    version = 1
)
abstract class AppDatabase : RoomDatabase() {

    @FactoryMethod(component = SingletonComponent::class)
    @Singleton
    abstract fun usersDao(): UsersDao
}

and a database factory:

interface DatabaseFactory {

    @FactoryMethod(component = SingletonComponent::class)
    @Singleton
    fun createDatabase(): AppDatabase
}

and a database factory provider:

object DatabaseFactoryProvider {

    @FactoryMethod(component = SingletonComponent::class)
    fun createDatabaseFactory(
        @ApplicationContext context: Context
    ): DatabaseFactory =
        if (BuildConfig.DEBUG) TestDatabaseFactory(context)
        else ProductionDatabaseFactory(context)
}

annotation processor will generate modules:

@Module
@InstallIn(SingletonComponent::class)
public object UsersDao_SingletonComponent_FactoryMethodsModule {
    @Provides
    @Singleton
    public fun appDatabase_usersDao(factory: AppDatabase): UsersDao = factory.usersDao()
}
@Module
@InstallIn(SingletonComponent::class)
public object AppDatabase_SingletonComponent_FactoryMethodsModule {
    @Provides
    @Singleton
    public fun databaseFactory_createDatabase(factory: DatabaseFactory): AppDatabase =
        factory.createDatabase()
}
@Module
@InstallIn(SingletonComponent::class)
public object DatabaseFactory_SingletonComponent_FactoryMethodsModule {
    @Provides
    public fun databaseFactoryProvider_createDatabaseFactory(
            @ApplicationContext context: Context
    ): DatabaseFactory = DatabaseFactoryProvider.INSTANCE.createDatabaseFactory(context)
}

Since release 1.1.0, component property is optional, and set to SingletonComponent by default.

@TestBound, @TestBoundTo and @TestFactoryMethod

Version 1.1.0 introduces additional test annotations that can be used to generate modules annotated with @TestInstallIn, instead of @InstallIn:

  • @TestBound (instead of @Bound)
  • @TestBoundTo (instead of @BoundTo)
  • @TestFactoryMethod (instead of @FactoryMethod)

Test module generated using @TestBound and/or @TestBoundTo will replace the module generated using @Bound and/or @BoundTo.

Test module generated using @TestFactoryMethod will replace the module generated with @FactoryMethod.

Hilt Testing Extensions

Maven Central Sonatype Nexus (Snapshots)

Build Configuration

Must be used as debugImplementation dependency to properly register EmptyFragmentActivity in manifest.

Kotlin

dependencies {
    implementation("com.google.dagger:hilt-android:2.45")

    androidTestImplementation("androidx.test:runner:1.5.2")
    debugImplementation("it.czerwinski.android.hilt:hilt-fragment-testing:[VERSION]")
}

Groovy

dependencies {
    implementation 'com.google.dagger:hilt-android:2.45'

    androidTestImplementation 'androidx.test:runner:1.5.2'
    debugImplementation 'it.czerwinski.android.hilt:hilt-fragment-testing:[VERSION]'
}

Testing Fragments With Hilt

HiltFragmentScenario

Works exactly like FragmentScenario, but supports Hilt dependency injection in fragments.

Just replace androidx.fragment:fragment-testing dependency with it.czerwinski.android.hilt:hilt-fragment-testing. All launchFragment and launchFragmentInContainer functions are available with exactly the same signatures, but return HiltFragmentScenario instead of FragmentScenario.

In addition, it is possible to run tests using a custom class extending FragmentActivity, so if you are using a base activity in your project, you can run tests of your fragment in it.

HiltFragmentScenario also implements onActivity, which allows for running ActivityActions, even when the fragment has already been removed.