Dagger KSP

Warning: Dagger’s KSP support is currently in alpha.

Requirements

  • Dagger 2.48 (or above)
  • Kotlin 1.9.0 (or above)
  • KSP 1.9.0-1.0.12 (or above)

Setup

Note: This guide focuses on Gradle. If you are using a different build system, please consult the documentation for that build system to enable KSP.

A general guide for migrating Gradle processors from KAPT to KSP can be found at https://developer.android.com/build/migrate-to-ksp.

The main steps are:

  1. Apply the Kotlin JVM plugin (or Kotlin Android plugin)
  2. Apply the KSP plugin.
  3. Change the compiler dependency configurations from kapt to ksp.
plugins {
  // STEP 1: Apply the Kotlin JVM (or Kotlin Android plugin)
  id "org.jetbrains.kotlin.jvm" version "1.9.0"
  // STEP 2: Apply the KSP plugin
  id "com.google.devtools.ksp" version "1.9.0-1.0.12"
}

// STEP 3: Change compiler dependencies from 'kapt' to 'ksp' configuration.
dependencies {
  ksp "com.google.dagger:dagger-compiler:2.48" // Dagger compiler
  ksp "com.google.dagger:hilt-compiler:2.48"   // Hilt compiler
}

New Dagger SPI plugin (with KSP support)

Dagger’s KSP processor requires all registered Dagger SPI plugins to be converted to the new Dagger SPI plugin API. The new API provides wrappers around the Javac and KSP model types that allows users to write plugins that support both Javac and KSP. The full list of wrappers are shown below.

Dagger Wrapper Javac KSP
DaggerAnnotation AnnotationMirror KSAnnotation
DaggerType TypeMirror KSType
DaggerElement Element KSDeclaration
DaggerTypeElement TypeElement KSClassDeclaration
DaggerExecutableElement ExecutableElement KSFunctionDeclaration

Users can get access to the underlying Javac or KSP type of a wrapper by calling its #javac() or #ksp() methods, respectively. In addition, all wrappers have a #backend() method that can be used to switch logic based on the current backend. For example:

/** Returns the qualified name of the given {@link DaggerTypeElement}. */
String getQualifiedName(DaggerTypeElement typeElement) {
  switch (typeElement.backend()) {
    case JAVAC: return typeElement.javac().getQualifiedName();
    case KSP: return typeElement.ksp().getQualifiedName().asString();
    default: throw AssertionError("Unexpected backend: " + typeElement.backend());
  }
}

Note that calling #javac() when processing with KSP (or calling #ksp() when processing with Javac) will throw an exception.

Interaction with Javac/KAPT processors

KSP processors are not able to resolve types generated by other Javac/KAPT processors. Thus, if your project contains other processors that generate classes that need to interact with Dagger or Hilt, then those processors also need to be migrated to KSP.

For example, if you’re using the androidx.hilt:hilt-compiler, it should also be migrated to use the ksp configuration. KSP support for androidx.hilt:hilt-compiler is available in version 1.1.x.

Note that even if another processor is not directly related to Dagger/Hilt, it will still need to be migrated to KSP if its generated type needs to be inspected by the Dagger/Hilt processor. For example, consider the usage of an AutoFactory generated class within an @Inject-annotated constructor:

@AutoFactory
class SomeClass(@Provided val dep: Dep, arg: String)

class Foo @Inject constructor(val factory: SomeClassFactory)

If com.google.auto.factory:auto-factory:1.0.1 is in the kapt configuration, then the generated class SomeClassFactory will not be visible to the Dagger processor while it inspects the parameters of the @Inject constructor. For such cases, an error similar to the following will be raised:

... was unable to process ‘Foo’ because 'SomeClassFactory' could not be resolved.

Even though usages of @AutoFactory can be replaced with Dagger’s @AssistedInject APIs, there might be other processors that might prevent your project from migrating to KSP’s version of Dagger.