Skip to main content
Version: Next

Extending Anvil

Anvil is a Kotlin compiler plugin which simplifies how we use Dagger. Anvil's documentation is definitely worth a read. Its benefits can be broken down into three categories:

  1. It can dramatically simplify Dagger development by automating most of the "plumbing".
  2. It can reduce build times by generating dagger factories, meaning that you can remove the dagger-compiler kapt dependency (and probably remove kapt entirely) from most of your project.
  3. It allows us to write our own code generators. This is what Tangle does. We're able to generate our own code, which is then integrated into the final graph by Anvil.

Generating code from generated code

Some of the code generated by Tangle requires additional processing.

For instance, given this human-written code:

class MyViewModel @VMInject constructor(
@TangleParam("name")
val name: String
) : ViewModel()

Tangle will generate this:

// generated by Tangle
public class MyViewModel_Factory @Inject constructor(
internal val savedStateHandleProvider: Provider<SavedStateHandle>
) {
public fun create(): MyViewModel {
val name = savedStateHandleProvider.get().get<String>("name")
checkNotNull(name) {
"Required parameter with name `name` and type `kotlin.String` is missing from SavedStateHandle."
}
return MyViewModel(name)
}
}

Notice that it uses @Inject constructor. This code was created after compilation started, but it needs to be analyzed as part of the overall Kotlin compilation task.

This is trivial if the project is using Dagger to generate dagger factories, because the kapt compilation stage hasn't started yet. When kapt starts, it will scan the files and see the generated MyViewModel_Factory.kt as though it's just another human-written file.

For Anvil, however, things are more difficult. The Kotlin compiler scans files once and passes that collection of files to the Anvil plugin. So in order for Anvil to generate code for Tangle, the two libraries need to be able to talk to one another. As Tangle creates new files, those files are passed along to Anvil for additional processing. For the MyViewModel_Factory.kt file above, Anvil will generate the following:

// generated by Anvil
public class MyViewModel_Factory_Factory(
private val param0: Provider<@JvmSuppressWildcards SavedStateHandle>
) : Factory<MyViewModel_Factory> {
public override fun `get`(): MyViewModel_Factory = newInstance(param0)

public companion object {
@JvmStatic
public fun create(param0: Provider<@JvmSuppressWildcards SavedStateHandle>):
MyViewModel_Factory_Factory = MyViewModel_Factory_Factory(param0)

@JvmStatic
public fun newInstance(param0: Provider<@JvmSuppressWildcards SavedStateHandle>):
MyViewModel_Factory = MyViewModel_Factory(param0)
}
}