I'm looking to write a plugin we can use throughou...
# plugin-development
l
I'm looking to write a plugin we can use throughout our app for modules for dependency injection. We are using
anvil
, but I don't think that should matter here. It looks like:
Copy code
/** Wraps all dependency injection code. */
class DependencyInjectionConventionPlugin : Plugin<Project> {
  override fun apply(target: Project) {
    with(target) {
      val extension = extensions.create<DependencyInjectionExtension>("di")

      configurePlugins()
      configureAnvil()
      configureDependencies(extension)
    }
  }

  private fun Project.configurePlugins() {
    with(pluginManager) {
      apply("com.squareup.anvil")
      apply("com.google.devtools.ksp")
    }
  }

  private fun Project.configureAnvil() {
    configure<AnvilExtension> {
      useKsp(contributesAndFactoryGeneration = true, componentMerging = true)
    }
  }

  private fun Project.configureDependencies(extension: DependencyInjectionExtension) {
    dependencies {
      add("implementation", project(":common:di:public"))
      if (extension.needsDaggerCompiler.getOrElse(false)) {
        println("We are true")
        add("ksp", libs.findLibrary("com-google-dagger-dagger-compiler"))
      } else {
        println("We are false")
      }
    }
  }
}

abstract class DependencyInjectionExtension {
  /** If your module contains any type of pure dagger modules, components, etc., set this to true. */
  abstract val needsDaggerCompiler: Property<Boolean>
}
I've added it to my modules and set
Copy code
di { needsDaggerCompiler = true }
On the modules that need it, but when I run the build, all modules no matter if they have been set to
true
are all
false
. I feel like I am missing something really obvious here, but no matter what I look up, it seems to show that what I wrote is correct, even though it is not, thanks!
Looks like I needed to put the
dependencies
block within an
afterEvaluate
block for this to work
p
I think you can also map the needsDagger Provider to a Dependency Provider or null and use dependencies.addLater
v
Definitely what Philip said. Using
afterEvaulate
to "fix" something is like using
Platform.runLater
or
SwingUtilities.runLater
to "fix" a GUI problem. You only do symptom treatment and move the problem to a later, harder to reproduce, harder to debug, harder to find, and harder to fix point in time instead of solving the actual problem. The main earnings you get for using
afterEvaluate
is timing problems, ordering problems, and race conditions. You should avoid using it at almost any cost. As you already recognized, your problem is that you read the property from the extension before the consumer had a chance to set it. By using
afterEvaluate
you gave it a chance to set it, but by now means you can be sure it will not change again after that. Whenever you call
get
or similar on a
Property
at configuration time, you can be pretty sure you are doing something wrong, as you introduce ordering problems and race conditions doing so. The sense of `Property`s is, that you do not evaluate them at configuration time, but only at execution time. At configuration time you only wire `Property`s and `Provider`s to other `Property`s or methods that know how to properly handle them like the
addLater
Philip mentioned. In other situations where you definitely need the value at configuration time, my usual recommendation is to replace the property in the extension by a function in the extension and doing the logic that needs the value in the body of that function.
l
Hmm I see, thanks for the background info! I tried to do what Philip suggested, but there is not
dependencies.addLater
function or parameter
p
There is
myConfiguration.dependencies.addLater(provider{})
and
DependencyHandler.add("myConfigName", provider {} )
👀 1