Slide 1

Slide 1 text

Fabian Shallari - July 2024 The Lazy (Meta)programmer’s Guide to Hilt Extensions 1

Slide 2

Slide 2 text

Fabian Shallari - July 2024 About me • Senior Android Engineer @ • Dual roles: Feature + Platform teams • Interests: DX + Scalability • Previously: Mobile Platform Team @ 2

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

Fabian Shallari - July 2024 1. What is Metaprogramming? 4

Slide 5

Slide 5 text

Fabian Shallari - July 2024 Meta$anything() • Physics 5 study ( nature() ) Designed by Freepik

Slide 6

Slide 6 text

Fabian Shallari - July 2024 Meta$anything() 6 • Metaphysics study ( nature( nature() ) )

Slide 7

Slide 7 text

Fabian Shallari - July 2024 Meta$anything() • thinking() 7

Slide 8

Slide 8 text

Fabian Shallari - July 2024 Meta$anything() 8 • thinking( thinking() ) = Metathinking

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

Fabian Shallari - July 2024 DON’T PANIC !!! 10

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Fabian Shallari - July 2024 DON’T PANIC !!! 12

Slide 13

Slide 13 text

Fabian Shallari - July 2024 👩💻 📃 📱 👩💻 📃 📱 📃 Programming Metaprogramming 13

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

Fabian Shallari - July 2024 DON’T PANIC !!! 15

Slide 16

Slide 16 text

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)

Slide 17

Slide 17 text

Fabian Shallari - July 2024 2. Why being lazy is a good trait? 17

Slide 18

Slide 18 text

Fabian Shallari - July 2024 :the_guide_app 18

Slide 19

Slide 19 text

Fabian Shallari - July 2024 :the_guide_app :the_answer :union_of_philosophers :infinite_improbability_drive :heart_of_gold 19

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Fabian Shallari - July 2024 22 :the_answer:api public interface TheAnswerCalculator { suspend fun theAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything() : Int }

Slide 23

Slide 23 text

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 }

Slide 24

Slide 24 text

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 }

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

Fabian Shallari - July 2024 internal class TheUnionOfPhilosophersImpl @Inject constructor( private val philosophers: Set, ): 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 }

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Fabian Shallari - July 2024 internal class TheUnionOfPhilosophersImpl @Inject constructor( private val philosophers: Set, ): 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 }

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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 }

Slide 32

Slide 32 text

Fabian Shallari - July 2024 3. What does Hilt have to do with it? 32

Slide 33

Slide 33 text

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)

Slide 34

Slide 34 text

Fabian Shallari - July 2024 Extension Annotation Hook into Hilt 34 https://dagger.dev/hilt/creating-extensions

Slide 35

Slide 35 text

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)

Slide 36

Slide 36 text

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)

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Fabian Shallari - July 2024 @GeneratesRootInput public annotation class AutoBind 38 :autobind:annotations Extension Annotation Hook into Hilt

Slide 39

Slide 39 text

Fabian Shallari - July 2024 internal class TheUnionOfPhilosophersImpl @Inject constructor( private val philosophers: Set, ): 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 }

Slide 40

Slide 40 text

Fabian Shallari - July 2024 internal class TheUnionOfPhilosophersImpl @Inject constructor( private val philosophers: Set, ): 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

Slide 41

Slide 41 text

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, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 41 Type information incomplete

Slide 42

Slide 42 text

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, ): TheUnionOfPhilosophers { override suspend fun protest(): Unit { philosophers.forEach { philosopher - > philosopher.protest() } } } 42 Extension Annotation

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

Fabian Shallari - July 2024 @GeneratesRootInput public annotation class AutoBind( val components: Array> = [SingletonComponent : : class], ) 45 :autobind:annotations Extension Annotation Hook into Hilt

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

Fabian Shallari - July 2024 @AutoBind internal class TheUnionOfPhilosophersImpl @Inject constructor( private val philosophers: Set, ): 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 }

Slide 49

Slide 49 text

Fabian Shallari - July 2024 4. Building Hilt extensions with KSP 49

Slide 50

Slide 50 text

Fabian Shallari - July 2024 :autobind:processor/build.gradle.kts dependencies { implementation(project(":autobind:annotations")) api(libs.hilt.core) implementation(libs.kotlin.ksp) implementation(libs.kotlinpoet.ksp) } 50

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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”

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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 { }

Slide 61

Slide 61 text

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 { }

Slide 62

Slide 62 text

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 { }

Slide 63

Slide 63 text

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 { }

Slide 64

Slide 64 text

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 { }

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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 }

Slide 67

Slide 67 text

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 }

Slide 68

Slide 68 text

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 }

Slide 69

Slide 69 text

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 }

Slide 70

Slide 70 text

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 }

Slide 71

Slide 71 text

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 }

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

Fabian Shallari - July 2024 DEMO 73

Slide 74

Slide 74 text

Fabian Shallari - July 2024 So long and thanks for all the fi sh ! 74 Questions? Let’s connect on LinkedIn