Skip to content

A library that writes an hilt module for a requested interface implementation.

License

Notifications You must be signed in to change notification settings

alecarnevale/claymore

Repository files navigation

🗡️ CLAYMORE

A library that writes an hilt module for a requested interface implementation.

🎬 Scenario

Prerequisite: project using Hilt for dependencies injection.

In a typical use case, you have:

interface MyInterface

---

class MyImplementation @Inject constructor() : MyInterface

and you need to write a simple module only to bind the right implementation:

@Module
@InstallIn(SingletonComponent::class)
interface MyModule {
  @Binds
  fun binding(impl: MyImplementation): MyInterface
}

🧙 Annotation

With claymore you can avoid to manually write that Module, by using @AutoBinds annotation:

@AutoBinds
class MyImplementation: MyInterface

clamyore will automatically generate the necessary module for you.

Component

You can optionally request claymore to install the binding in a specific hilt component, using the component parameter.

@AutoBinds(component = ActivityComponent::class)
class MyImplementation: MyInterface

If not set, the SingletonComponent will be used by default.

Annotations support

You can also request claymore to attach any annotation to the binding method, using the annotations parameter.

@AutoBinds(annotations = [IntoSet::class])
class MyImplementation: MyInterface

// generates a module with a binding function like

@Binds
@IntoSet
fun binding(impl: MyImplementation): MyInterface

AutoUninstall annotation

When in tests you need to uninstall module generated by AutoBinds annotation and so replace real implementations with fakes, you can avoid to reference module generated by claymore using the AutoUninstall annotation instead.

@AutoUninstall(
  implementations = [
    MyImplementation::class,
    MyOtherImplementation::class,
  ]
)
@InstallIn(SingletonComponent::class)
@Module
object TestModule {
  @Provides
  fun fakeImplementation(): MyInterface = FakeImplementation()
  
  @Provides
  fun otherFakeImplementation(): MyOtherInterface = OtherFakeImplementation()  
}

claymore will generate a module that replaces generated MyImplementationModule and MyOtherImplementationModule for you. Again, you can set the component where replace the modules, otherwise SingletonComponent is used by default.

AutoProvides annotation

AutoProvides is an experimental annotation that wants to erase all the boilerplate code needed to inject parameter into an Android ViewModel through SavedStateHandle.

Given the following scenario:

  • FirstActivity wants to start SecondActivity and pass Foo and Bar as input;
  • SecondActivity uses SecondViewModel;
  • SecondViewModel expect Foo and Bar to be injected as properties.

So we can use AutoProvides annotation on top of an interface that define an invoke operator function that take all inputs as parameter and return an Intent. Such interface works as a contract between the two activities and the view model, defining what are expected as inputs.

@AutoProvides(activityClass = SecondActivity::class)
interface SecondActivityIntent {
  // this can be read as a contract on how to start the SecondActivity
  
  // qualifiers are needed to avoid clash between same input type
  @Qualifier
  annotation class FirstArg

  @Qualifier
  annotation class SecondArg

  operator fun invoke(
    @FirstArg firstArg: String,
    @SecondArg secondArg: String,
  ): Intent
}

@AndroidEntryPoint
class SecondActivity: Activity() {
  val viewModel: SecondViewModel by viewModels()
  ...
}

@HiltViewModel
class SecondViewModel @Inject constructor(
  @SecondActivityIntent.FirstArg firstArg: String,
  @SecondActivityIntent.SecondArg secondArg: String,
): ViewModel() {
  ...
}

@AndroiEntryPoint
class FirstActivity: Activity() {
  
  // the injection is provided by claymore itself
  @Inject lateinit var secondActivityIntent: SecondActivityIntent
  
  ...

  fun startSecondActivity() {
    startActivity(
      secondActivityIntent(
        firstArg = "Foo",
        secondArg = "Bar",
      )
    )
  }
}

🎮 Demo

Take a look at the :demo module for a sample usage.

In this simple project we have:

  • api module where a single service (interface) lives;
  • impl module where the service implementation is defined, and annotated with AutoBinds;
  • app module where DI starts and service is requested.

Moreover:

  • the annotations module define other services to show the usage of AutoBinds.annotations parameter;
  • inside the annotations module the AutoUninstall annotation is used in the test source set to replace the implementations with fakes without referencing the generated modules;
  • inside multiround module you can find a sample usage of AutoProvides annotation.

🛠️ Installation

Download

Claymore is available in Maven Central Repository:

Gradle

repositories {
  mavenCentral()
}

dependencies {
  compileOnly 'io.github.alecarnevale:claymore-annotations:x.y.z'
}

KSP integration

In order to completely enable claymore integration you need to apply the ksp plugin

plugins {
  id 'com.google.devtools.ksp'
}

dependencies {
  ksp 'io.github.alecarnevale:claymore-processors:x.y.z'
}

🙏 Thanks to

About

A library that writes an hilt module for a requested interface implementation.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages