Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Lazy (Meta)programmer's Guide to Hilt Exten...

Fabian Shallari
July 08, 2024
60

The Lazy (Meta)programmer's Guide to Hilt Extensions

Slides for the talk given at Droidcon Berlin 2024, July 4th.

Fabian Shallari

July 08, 2024
Tweet

Transcript

  1. Fabian Shallari - July 2024 About me • Senior Android

    Engineer @ • Dual roles: Feature + Platform teams • Interests: DX + Scalability • Previously: Mobile Platform Team @ 2
  2. Fabian Shallari - July 2024 A Guide to the Guide

    1. What is Metaprogramming? 2. Why being lazy is a good trait? 3. What does Hilt have to do with it? 4. Building Hilt extensions with KSP 3
  3. Fabian Shallari - July 2024 – Someone on the Internet.

    “Metaprogramming is a computer programming technique in which computer programs have the ability to treat other programs as their data. It means that a program can be designed to read, generate, analyze, or transform other programs, and even modify itself, while running..” 9
  4. Fabian Shallari - July 2024 – Someone in this room

    “Metaprogramming is just a fancy term which means writing programs that can read, generate and modify other programs or themselves” 11
  5. Fabian Shallari - July 2024 👩💻 📃 📱 👩💻 📃

    📱 📃 Programming Metaprogramming 13
  6. Fabian Shallari - July 2024 Metaprogramming in Kotlin 1. Re

    f lection implementation(libs.kotlin.re f lect) // org.jetbrains.kotlin:kotlin-re f lect:x.y.z 14 2. Code Generation ksp(libs.hilt.compiler) // com.google.dagger:hilt-compiler:x.y.z read, generate, modify read, generate
  7. Fabian Shallari - July 2024 Metaprogramming in Kotlin 1. Re

    f lection implementation(libs.kotlin.re f lect) 16 2. Code Generation ksp(libs.hilt.compiler)
  8. Fabian Shallari - July 2024 :the_guide_app :the_answer :the_answer:api :the_answer:impl :union_of_philosophers

    :union_of_philosophers:api :union_of_philosophers:impl :infinite_improbability_drive :infinite_improbability_drive:api :infinite_improbability_drive:impl :heart_of_gold :heart_of_gold:api :heart_of_gold:impl 20
  9. Fabian Shallari - July 2024 21 the_answer api impl the_guide_app

    union_of_philosophers api impl heart_of_gold api impl in fi nite_improbability _drive api impl
  10. Fabian Shallari - July 2024 22 :the_answer:api public interface TheAnswerCalculator

    { suspend fun theAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything() : Int }
  11. Fabian Shallari - July 2024 internal class DeepThoughtComputer @Inject constructor(

    theUnionOfPhilosophers: TheUnionOfPhilosophers, ... / / other dependencies ): TheAnswerCalculator { override suspend fun theAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything(): Int { theUnionOfPhilosophers.protest() thinkDeeply(72,5.toMillionYears()) return 42 } 23 :the_answer:impl @Module @InstallIn(SingletonComponent :: class) internal abstract class DeepThoughtComputerModule { @Binds abstract fun bindTheAnswerCalculator(deepThought: DeepThoughtComputer): TheAnswerCalculator }
  12. Fabian Shallari - July 2024 internal class DeepThoughtComputer @Inject constructor(

    theUnionOfPhilosophers: TheUnionOfPhilosophers, ... / / other dependencies ): TheAnswerCalculator { override suspend fun theAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything(): Int { theUnionOfPhilosophers.protest() thinkDeeply(72,5.toMillionYears()) return 42 } 24 :the_answer:impl @Module @InstallIn(SingletonComponent :: class) internal abstract class DeepThoughtComputerModule { @Binds abstract fun bindTheAnswerCalculator(deepThought: DeepThoughtComputer): TheAnswerCalculator }
  13. Fabian Shallari - July 2024 25 :union_of_philosophers:api public interface TheUnionOfPhilosophers

    { suspend fun protest() : Unit } the_answer api impl union_of_philosophers api impl
  14. Fabian Shallari - July 2024 internal class TheUnionOfPhilosophersImpl @Inject constructor(

    private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 26 :union_of_philosophers:impl @Module @InstallIn(SingletonComponent :: class) internal abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers }
  15. Fabian Shallari - July 2024 internal class TheUnionOfPhilosophersImpl @Inject constructor(

    private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 27 :union_of_philosophers:impl
  16. Fabian Shallari - July 2024 :the_guide_app :the_answer :the_answer:api :the_answer:impl :union_of_philosophers

    :union_of_philosophers:api :union_of_philosophers:impl :infinite_improbability_drive :infinite_improbability_drive:api :infinite_improbability_drive:impl :heart_of_gold :heart_of_gold:api :heart_of_gold:impl 28 Example app: 4 modules x 1 binding = 4 extra classes :real_app :module_0 :the_answer:api :the_answer:impl :module_1 :union_of_philosophers:api :union_of_philosophers:impl ... ... ... :module_100 :heart_of_gold:api :heart_of_gold:impl Real app: 100 modules x 10 bindings = 1000 extra classes
  17. Fabian Shallari - July 2024 internal class TheUnionOfPhilosophersImpl @Inject constructor(

    private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 29 :union_of_philosophers:impl @Module @InstallIn(SingletonComponent :: class) internal abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers }
  18. Fabian Shallari - July 2024 @AutoBind internal class TheUnionOfPhilosophersImpl @Inject

    constructor( private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 30 :union_of_philosophers:impl 2. Code Generation ksp(libs.autobind.processor)
  19. Fabian Shallari - July 2024 @AutoBind 31 2. Code Generation

    ksp(libs.autobind.processor) @Module @InstallIn(SingletonComponent :: class) internal abstract class DeepThoughtComputerModule { @Binds abstract fun bindTheAnswerCalculator(deepThought: DeepThoughtComputer): TheAnswerCalculator }
  20. Fabian Shallari - July 2024 Kotlin Compiler Process Lexer Parser

    KSP Plugins … … Source Code Byte Code 33 ? annotation class @AutoBind ksp(libs.autobind.processor) Hilt ksp(libs.hilt.compiler)
  21. Fabian Shallari - July 2024 Extension Annotation Hook into Hilt

    34 https://dagger.dev/hilt/creating-extensions
  22. Fabian Shallari - July 2024 Kotlin Compiler Process Lexer Parser

    KSP Plugins … … Source Code Byte Code 35 annotation class @AutoBind ksp(libs.autobind.processor) Hilt ksp(libs.hilt.compiler)
  23. Fabian Shallari - July 2024 Kotlin Compiler Process Lexer Parser

    KSP Plugins … … Source Code Byte Code 36 @GeneratesRootInput annotation class @AutoBind ksp(libs.autobind.processor) Hilt ksp(libs.hilt.compiler)
  24. Fabian Shallari - July 2024 @AutoBind internal class TheUnionOfPhilosophersImpl @Inject

    constructor( private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 37 :union_of_philosophers:impl Extension Annotation
  25. Fabian Shallari - July 2024 @GeneratesRootInput public annotation class AutoBind

    38 :autobind:annotations Extension Annotation Hook into Hilt
  26. Fabian Shallari - July 2024 internal class TheUnionOfPhilosophersImpl @Inject constructor(

    private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 39 :union_of_philosophers:impl @Module @InstallIn(SingletonComponent :: class) internal abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers }
  27. Fabian Shallari - July 2024 internal class TheUnionOfPhilosophersImpl @Inject constructor(

    private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 40 :union_of_philosophers:impl @Module @InstallIn(SingletonComponent :: class) internal abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers } Module we want to generate
  28. Fabian Shallari - July 2024 :union_of_philosophers:impl @Module @InstallIn(SingletonComponent :: class)

    internal abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers } internal class TheUnionOfPhilosophersImpl @Inject constructor( private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 41 Type information incomplete
  29. Fabian Shallari - July 2024 :union_of_philosophers:impl @Module @InstallIn(SingletonComponent :: class)

    internal abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers } @AutoBind(components = [SingletonComponent :: class]) internal class TheUnionOfPhilosophersImpl @Inject constructor( private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 42 Extension Annotation
  30. Fabian Shallari - July 2024 @GeneratesRootInput public annotation class AutoBind

    43 :autobind:annotations Extension Annotation Hook into Hilt
  31. Fabian Shallari - July 2024 @GeneratesRootInput public annotation class AutoBind(

    val components: Array<KClass <* >> ) 44 :autobind:annotations Extension Annotation Hook into Hilt
  32. Fabian Shallari - July 2024 @GeneratesRootInput public annotation class AutoBind(

    val components: Array<KClass <* >> = [SingletonComponent : : class], ) 45 :autobind:annotations Extension Annotation Hook into Hilt
  33. Fabian Shallari - July 2024 @AutoBind(components = [SingletonComponent :: class])

    internal class TheUnionOfPhilosophersImpl @Inject constructor( private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 46 :union_of_philosophers:impl
  34. Fabian Shallari - July 2024 @AutoBind internal class TheUnionOfPhilosophersImpl @Inject

    constructor( private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 47 :union_of_philosophers:impl
  35. Fabian Shallari - July 2024 @AutoBind internal class TheUnionOfPhilosophersImpl @Inject

    constructor( private val philosophers: Set<Philosopher>, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 48 :union_of_philosophers:impl /* GENERATED BY @AutoBind */ @Module @InstallIn(SingletonComponent :: class) internal abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers }
  36. Fabian Shallari - July 2024 :autobind:processor class AutoBindProcessorProvider : SymbolProcessorProvider

    { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = AutoBindProcessor( logger = environment.logger, bindModuleCodeGenerator = BindModuleCodeGenerator(environment.codeGenerator), ) } 51
  37. Fabian Shallari - July 2024 :autobind:processor class AutoBindProcessorProvider : SymbolProcessorProvider

    { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = AutoBindProcessor( logger = environment.logger, bindModuleCodeGenerator = BindModuleCodeGenerator(environment.codeGenerator), ) } 52
  38. Fabian Shallari - July 2024 internal class AutoBindProcessor( private val

    logger: KSPLogger, private val bindModuleCodeGenerator: BindModuleCodeGenerator, ) : SymbolProcessor { override fun process(resolver: Resolver): List<KSAnnotated> { resolver .getSymbolsWithAnnotation(requireNotNull(AutoBind :: class.qualifiedName)) .filterIsInstance<KSClassDeclaration>() .forEach { bindModuleCodeGenerator.generateModule(it, resolver) } return emptyList() } } 53 :autobind:processor
  39. Fabian Shallari - July 2024 internal class AutoBindProcessor( private val

    logger: KSPLogger, private val bindModuleCodeGenerator: BindModuleCodeGenerator, ) : SymbolProcessor { override fun process(resolver: Resolver): List<KSAnnotated> { resolver .getSymbolsWithAnnotation(requireNotNull(AutoBind :: class.qualifiedName)) .filterIsInstance<KSClassDeclaration>() .forEach { bindModuleCodeGenerator.generateModule(it, resolver) } return emptyList() } } 54 :autobind:processor
  40. Fabian Shallari - July 2024 internal class AutoBindProcessor( private val

    logger: KSPLogger, private val bindModuleCodeGenerator: BindModuleCodeGenerator, ) : SymbolProcessor { override fun process(resolver: Resolver): List<KSAnnotated> { resolver .getSymbolsWithAnnotation(requireNotNull(AutoBind :: class.qualifiedName)) .filterIsInstance<KSClassDeclaration>() .forEach { bindModuleCodeGenerator.generateModule(it, resolver) } return emptyList() } } 55 :autobind:processor
  41. Fabian Shallari - July 2024 internal class AutoBindProcessor( private val

    logger: KSPLogger, private val bindModuleCodeGenerator: BindModuleCodeGenerator, ) : SymbolProcessor { override fun process(resolver: Resolver): List<KSAnnotated> { resolver .getSymbolsWithAnnotation(requireNotNull(AutoBind :: class.qualifiedName)) .filterIsInstance<KSClassDeclaration>() .forEach { bindModuleCodeGenerator.generateModule(it) } return emptyList() } } 56 :autobind:processor
  42. Fabian Shallari - July 2024 57 /* GENERATED BY @AutoBind

    */ /* UnionOfPhilosophersModule.kt */ @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers } 1. single file named “${implDeclaration.syperType()}Module” 2. single abstract class named “${implDeclaration.syperType()}Module” annotated with @Module and @InstallIn(autoBindAnnotation.components) 3. single abstract function named “bind${implDeclaration.syperType()}Module” annotated with @Binds one parameter of type implementation returns implDeclaration.syperType() :union_of_philosophers:impl
  43. Fabian Shallari - July 2024 internal class BindModuleCodeGenerator(private val codeGenerator:

    CodeGenerator) { fun generateBindModule(implDeclaration: KSClassDeclaration) { FileSpec.builder(implDeclaration.packageName.asString(), daggerModuleNameFor(implDeclaration)) .addType(generateClassSpec(implDeclaration)) .build() .writeTo( codeGenerator = codeGenerator, aggregating = false, originatingKSFiles = listOf(requireNotNull(implDeclaration.containingFile)) ) } private fun daggerModuleNameFor(classDeclaration: KSClassDeclaration): String { return "${classDeclaration.superTypes.first().resolve().toClassName().simpleName}Module" } } 58 :autobind:processor 1. single file named “${implementation.syperType()}Module”
  44. Fabian Shallari - July 2024 internal class BindModuleCodeGenerator(private val codeGenerator:

    CodeGenerator) { fun generateBindModule(implDeclaration: KSClassDeclaration) { FileSpec.builder(implDeclaration.packageName.asString(), daggerModuleNameFor(implDeclaration)) .addType(generateClassSpec(implDeclaration)) .build() .writeTo( codeGenerator = codeGenerator, aggregating = false, originatingKSFiles = listOf(requireNotNull(implDeclaration.containingFile)) ) } private fun daggerModuleNameFor(classDeclaration: KSClassDeclaration): String { return “${classDeclaration.superTypes.first().resolve().toClassName().simpleName}Module” } } 59 :autobind:processor
  45. Fabian Shallari - July 2024 private fun generateClassSpec(implDeclaration: KSClassDeclaration): TypeSpec

    { return TypeSpec.classBuilder(daggerModuleNameFor(implDeclaration)) .addModifiers(KModifier.ABSTRACT) .addAnnotation(AnnotationSpec.builder(Module :: class).build()) .addAnnotation( AnnotationSpec .builder(InstallIn :: class) .addMember(implDeclaration.valueOfAutoBindComponentArg()) .build() ) .addFunction(generateFunctionSpec(implDeclaration)) .build() } } 60 2. single abstract class named “${implDeclaration.syperType()}Module” annotated with @Module and @InstallIn(autoBindAnnotation.components) @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { }
  46. Fabian Shallari - July 2024 private fun generateClassSpec(implDeclaration: KSClassDeclaration): TypeSpec

    { return TypeSpec.classBuilder(daggerModuleNameFor(implDeclaration)) .addModifiers(KModifier.ABSTRACT) .addAnnotation(AnnotationSpec.builder(Module :: class).build()) .addAnnotation( AnnotationSpec .builder(InstallIn :: class) .addMember(implDeclaration.valueOfAutoBindComponentArg()) .build() ) .addFunction(generateFunctionSpec(implDeclaration)) .build() } } 61 2. single abstract class named “${implDeclaration.syperType()}Module” annotated with @Module and @InstallIn(autoBindAnnotation.components) @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { }
  47. Fabian Shallari - July 2024 private fun generateClassSpec(implDeclaration: KSClassDeclaration): TypeSpec

    { return TypeSpec.classBuilder(daggerModuleNameFor(implDeclaration)) .addModifiers(KModifier.ABSTRACT) .addAnnotation(AnnotationSpec.builder(Module :: class).build()) .addAnnotation( AnnotationSpec .builder(InstallIn :: class) .addMember(implDeclaration.valueOfAutoBindComponentArg()) .build() ) .addFunction(generateFunctionSpec(implDeclaration)) .build() } } 62 2. single abstract class named “${implDeclaration.syperType()}Module” annotated with @Module and @InstallIn(autoBindAnnotation.components) @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { }
  48. Fabian Shallari - July 2024 private fun generateClassSpec(implDeclaration: KSClassDeclaration): TypeSpec

    { return TypeSpec.classBuilder(daggerModuleNameFor(implDeclaration)) .addModifiers(KModifier.ABSTRACT) .addAnnotation(AnnotationSpec.builder(Module :: class).build()) .addAnnotation( AnnotationSpec .builder(InstallIn :: class) .addMember(implDeclaration.valueOfAutoBindComponentArg()) .build() ) .addFunction(generateFunctionSpec(implDeclaration)) .build() } } 63 2. single abstract class named “${implDeclaration.syperType()}Module” annotated with @Module and @InstallIn(autoBindAnnotation.components) @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { }
  49. Fabian Shallari - July 2024 private fun generateClassSpec(implDeclaration: KSClassDeclaration): TypeSpec

    { return TypeSpec.classBuilder(daggerModuleNameFor(implDeclaration)) .addModifiers(KModifier.ABSTRACT) .addAnnotation(AnnotationSpec.builder(Module :: class).build()) .addAnnotation( AnnotationSpec .builder(InstallIn :: class) .addMember(implDeclaration.valueOfAutoBindComponentArg()) .build() ) .addFunction(generateFunctionSpec(implDeclaration)) .build() } } 64 2. single abstract class named “${implDeclaration.syperType()}Module” annotated with @Module and @InstallIn(autoBindAnnotation.components) @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { }
  50. Fabian Shallari - July 2024 private fun generateClassSpec(implDeclaration: KSClassDeclaration): TypeSpec

    { return TypeSpec.classBuilder(daggerModuleNameFor(implDeclaration)) .addModifiers(KModifier.ABSTRACT) .addAnnotation(AnnotationSpec.builder(Module :: class).build()) .addAnnotation( AnnotationSpec .builder(InstallIn :: class) .addMember(implDeclaration.valueOfAutoBindComponentArg()) .build() ) .addFunction(generateFunctionSpec(implDeclaration)) .build() } } 65
  51. Fabian Shallari - July 2024 private fun generateFunctionSpec(implDeclaration: KSClassDeclaration): FunSpec

    { return FunSpec .builder("bind${implDeclaration.superTypes.first().resolve().toClassName().simpleName}") .addModifiers(KModifier.ABSTRACT) .addAnnotation(Binds :: class) .addParameter( name = "impl", type = implDeclaration.toClassName() ) .returns(implDeclaration.superTypes.first().toTypeName()) .build() } 66 3. single abstract function named “bind${implDeclaration.syperType()}Module” annotated with @Bind one parameter of type implementation returns implDeclaration.syperType() @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers }
  52. Fabian Shallari - July 2024 private fun generateFunctionSpec(implDeclaration: KSClassDeclaration): FunSpec

    { return FunSpec .builder("bind${implDeclaration.superTypes.first().resolve().toClassName().simpleName}") .addModifiers(KModifier.ABSTRACT) .addAnnotation(Binds :: class) .addParameter( name = "impl", type = implDeclaration.toClassName() ) .returns(implDeclaration.superTypes.first().toTypeName()) .build() } 67 3. single abstract function named “bind${implDeclaration.syperType()}Module” annotated with @Bind one parameter of type implementation returns implDeclaration.syperType() @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers }
  53. Fabian Shallari - July 2024 private fun generateFunctionSpec(implDeclaration: KSClassDeclaration): FunSpec

    { return FunSpec .builder("bind${implDeclaration.superTypes.first().resolve().toClassName().simpleName}") .addModifiers(KModifier.ABSTRACT) .addAnnotation(Binds :: class) .addParameter( name = "impl", type = implDeclaration.toClassName() ) .returns(implDeclaration.superTypes.first().toTypeName()) .build() } 68 3. single abstract function named “bind${implDeclaration.syperType()}Module” annotated with @Bind one parameter of type implementation returns implDeclaration.syperType() @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers }
  54. Fabian Shallari - July 2024 private fun generateFunctionSpec(implDeclaration: KSClassDeclaration): FunSpec

    { return FunSpec .builder("bind${implDeclaration.superTypes.first().resolve().toClassName().simpleName}") .addModifiers(KModifier.ABSTRACT) .addAnnotation(Binds :: class) .addParameter( name = "impl", type = implDeclaration.toClassName() ) .returns(implDeclaration.superTypes.first().toTypeName()) .build() } 69 3. single abstract function named “bind${implDeclaration.syperType()}Module” annotated with @Bind one parameter of type implementation returns implDeclaration.syperType() @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers }
  55. Fabian Shallari - July 2024 private fun generateFunctionSpec(implDeclaration: KSClassDeclaration): FunSpec

    { return FunSpec .builder("bind${implDeclaration.superTypes.first().resolve().toClassName().simpleName}") .addModifiers(KModifier.ABSTRACT) .addAnnotation(Binds :: class) .addParameter( name = "impl", type = implDeclaration.toClassName() ) .returns(implDeclaration.superTypes.first().toTypeName()) .build() } 70 3. single abstract function named “bind${implDeclaration.syperType()}Module” annotated with @Bind one parameter of type implementation returns implDeclaration.syperType() @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers }
  56. Fabian Shallari - July 2024 private fun generateFunctionSpec(implDeclaration: KSClassDeclaration): FunSpec

    { return FunSpec .builder("bind${implDeclaration.superTypes.first().resolve().toClassName().simpleName}") .addModifiers(KModifier.ABSTRACT) .addAnnotation(Binds :: class) .addParameter( name = "impl", type = implDeclaration.toClassName() ) .returns(implDeclaration.superTypes.first().toTypeName()) .build() } 71 3. single abstract function named “bind${implDeclaration.syperType()}Module” annotated with @Bind one parameter of type implementation returns implDeclaration.syperType() @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers }
  57. Fabian Shallari - July 2024 72 /* GENERATED BY @AutoBind

    */ /* UnionOfPhilosophersModule.kt */ @Module @InstallIn(SingletonComponent :: class) abstract class UnionOfPhilosophersModule { @Binds abstract fun bindTheUnionOfPhilosphers(impl: TheUnionOfPhilosophersImpl): TheUnionOfPhilosophers } 1. single file named “${implDeclaration.syperType()}Module” 2. single abstract class named “${implDeclaration.syperType()}Module” annotated with @Module and @InstallIn(autoBindAnnotation.components) 3. single abstract function named “bind${implDeclaration.syperType()}Module” annotated with @Binds one parameter of type implementation returns implDeclaration.syperType() :union_of_philosophers:impl
  58. Fabian Shallari - July 2024 So long and thanks for

    all the fi sh ! 74 Questions? Let’s connect on LinkedIn