Introduction
I develop a small multi-platform(android, desktop) project for finding cars at an auction using Koin as dependency injection. Koin imposes a small inconvenience in the form of describing all dependencies in one place, because of that during development exceptions often appear (either I forget to declare a class or to add a new parameter).
koin-annotations 1.0.0-beta-1
One day a miracle happened, koin-annotations beta-1 🥳 was released. You no longer need to describe koin modules, you just write an annotation above the class and life is good. I didn’t hesitate to start integrating them into my project by following the documentation.
The first problem was adding the path to the generated modules in srcDirs. Since the project is multiplatform, KSP generates 2 directories: android and desktop. By the way, Android Studio expects to see the common directory as well, so all imports are red. Anyway, the project is being built because during compilation compiler uses platform directories.
Screenshot of broken imports
I clumsily solved this problem, by simply adding the path to the desktop directory as to the common, and the studio began to see all imports. 🎉
sourceSets { | |
val commonMain by getting { | |
kotlin.srcDir("build/generated/ksp/desktop/desktopMain/kotlin") | |
dependencies { | |
implementation(libs.koin.core) | |
implementation(libs.koin.ksp.annotations) | |
} | |
} | |
} |
Screenshot of working imports
Unfortunately, this caused an even bigger issue. Now, when assembling an android target, the build fails with a duplication exception, since I’ve added the path to the desktop directory as a common one, the android compiler sees all the resources, not just its own.
Screenshot of duplication exception
I had to remove the code above and continue my work with red imports. ☹️
Additionally, I’ve created an issue with the described problem.
So, the project is synching, everything is in order, and you can remove the Koin modules, replacing them with annotations.
I decided to start the migration from a small module declared in the common main folder.
internal val commonModule = module { | |
factory<ScreenProvider> { AppScreenProvider() } | |
single { | |
Json { | |
ignoreUnknownKeys = true | |
} | |
} | |
} |
@Module | |
@ComponentScan("com.cprt.advancedauction.common") | |
class CommonModule { | |
@Single | |
fun json() = Json { | |
ignoreUnknownKeys = true | |
} | |
} | |
@Factory | |
internal class AppScreenProvider : ScreenProvider |
The application was assembled, installed on the device, and… crashed on startup. As it turned out, my interface ScreenProvider and its implementation AppScreenProvider are declared in different Gradle modules. Simply, Koin annotations don’t have the ability to search for dependencies in different Gradle modules.
I found this problem in the issues for this library, it is impossible to bypass it now, so my code gets into a git stash, and the implementation is postponed indefinitely. ☹️
Photo by Moritz Kindler on Unsplash
Job Offers
koin-annotations 1.0.0-beta-2
A few weeks later Koin-annotations beta-2 🥳 was released with a fix for my blocker. I return to the task with the integration of annotations and remake all the modules. The project is assembling and everything works, but… 2 problems were found:
1. In the project, I use sealed interfaces, but the library doesn’t handle them correctly, KSP can’t access the nested interfaces. So I have to change their spelling:
//From | |
sealed interface A { | |
interface B : A | |
} | |
class C : A.B | |
//To | |
sealed interface A | |
interface B : A | |
class C : B |
The new approach allows me to access interface B directly instead of doing this via interface A (not critical, but I don’t like how this approach looks). I’ve created an issue with this problem.
2. Koin annotations allow you to include one module into another using includes property in Module annotation:
@Module(includes = [SecondModule::class]) | |
@ComponentScan("com.firstModule") | |
class FirstModule | |
@Module | |
@ComponentScan("com.secondModule") | |
class SecondModule |
However, in my case, Koin modules are placed in different Gradle modules and KSP cannot find an import for the included module. I’ve also created an issue with this problem.
Conclusion
I can honestly say that if you start a project from scratch, and you need neither to use sealed interfaces nor include modules into each other, then you can try using koin annotations, the library saves a lot of developer’s time. 😉
Waiting for koin-annotations 1.0.0-beta-3
However, I send my code to the stash again just because I don’t want to adjust the previously written code to the bugs of the library, so I’m still waiting for beta-3 to finally merge the library into the dev branch. 😏
Main project: https://github.com/DoTheMonkeyBusiness/CopartAdvancedAuction
Sample project where you can observe the problems: https://github.com/DoTheMonkeyBusiness/koinAnnotationIssue
Photo by Markus Spiske on Unsplash
This article was originally published on proandroiddev.com on May 19, 2022