Blog Infos
Author
Published
Topics
, , , ,
Published

Please fasten your seatbelts, the flight time today will be approximately a few minutes. Source: Unsplash

Have you ever found yourself traveling with friends on a low-cost carrier, choosing not to pay extra for seat selection, only to end up scattered all over the airplane? I have, and that’s why I created Flight Chat — a small app designed to help you stay in touch during flights. It’s available on the Play Store, and I’m hoping to launch it soon on the App Store for my iPhone friends…

But before supporting iOS, the app needs Kotlin Multiplatform (KMP) compatibility to remain “as native as possible”, which is especially important for BLE-related features. In my previous article about the recommended Android tech stack (as of 2022), I selected Hilt as my DI framework of choice. Over time, it proved solid — we rewrote a 10M+ downloads app without running into significant DI issues. However, the major drawback is that Hilt does not support KMP. And likely won’t for the foreseeable future.

Since Hilt isn’t a viable option for multi-platform development, I looked into other libraries written fully in Kotlin that offer KMP support. I decided to try Koin first to see how it fares. Why Koin? Let me quote myself from that previous article:

All the libraries in the previous sections are being created by the Square, Google or Kotlin team. So there is a big chance they will not be deprecated overnight, you may find them in multiple projects so they will be battle-proven in many (also large-scale) production apps, and when having an issue — there will be an answer on StackOverflow.

Koin, on the other hand, isn’t made by those same companies, but it’s backed by the Kotzilla team — which, on paper, seems more promising than single-contributor projects without such support. Besides, Koin has been around in the Android community for quite some time; I even used it in my first article back in 2019. It also leads in popularity on GitHub with 9.1k starts, compared to competing libraries with 3.2k and 1.3k stars respectively. While GitHub stars aren’t the only metric, they do provide a quick and easy way to compare.

With new library chosen, we’re now ready to take-off ✈️.

Step 1 — Choose Necessary Koin Dependencies

Koin has become quite modular in terms of dependencies selection — enough to feel overwhelming for some:

Since my app isn’t KMP-ready yet, I decided to use koin-androidkoin-corekoin-compose-viewmodel, and koin-startup. The most significant decision, however, was choosing between the default Koin DSL and Koin Annotations. As the name implies, Koin Annotations lets you declare your components and modules through annotations. For someone with a long history of using Dagger-like APIs, opting for Koin Annotations alongside these other dependencies was a no-brainer.

Step 2 — Migrate the Codebase Module by Module

It is usually best to migrate progressively in a larger team to keep the app functional throughout the transition. There are also a few helper methods available to bridge Hilt and Koin, making the process smoother. However, since I’m working on the project solo (and prefer not to learn additional APIs for a short-term solution), I opted for the “hard way”: migrating the entire codebase module by module, and only building the project at the very end.

I won’t repeat the details on how to map each Hilt annotation to its Koin Annotations counterpart — there’s already an official guide titled How To Migrate from Hilt to Koin — A Detailed Guide.

If you need any assistance during the flight, please do not hesitate to refer to the linked guide
Step 3 — Fix Migration Compile-Time Issues

Once the migration was complete, it was time to attempt the first build. As expected, it failed — the amount of code change was substantial, after all. Below are some issues I encountered while fixing my app; your specific challenges may vary:

  • A few classes needed to be public instead of internal.
  • If a dependency isn’t provided inside the Module (but is annotated and scoped at its declaration), you still need to create an (empty) Module with @ComponentScan annotation in the same package.
  • App’s broadcast receivers (like these used for local notifications) need to implement KoinComponent.
  • Certain automatic bindings, like custom Timber trees that depend on a build variant type, might not be what you’d expect.
  • Double-check you’ve re-added the correct Lazy imports if needed!

Additionally, if you define navigation in a multi-module project like I did in the previous article, specifically by placing navigation factories in each feature module that implement an interface located in a core sub-module, you may encounter dex merge conflicts. For example, :app:mergeLibDexDebug can fail due to binding name conflicts in feature modules.

The only workaround I’ve found is to avoid auto-generated bindings and define them manually using the Koin DSLs:

internal val navigationFactoryModule = module {
single { LoginNavigationFactory() } bind NavigationFactory::class
single { SearchNavigationFactory(get()) } bind NavigationFactory::class
single { SettingsNavigationFactory(get()) } bind NavigationFactory::class
single { ChatNavigationFactory(get()) } bind NavigationFactory::class
}
view raw AppModules.kt hosted with ❤ by GitHub

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Going offline first with Flutter: Best practices

Explore how to design robust Flutter applications with an offline-first approach. This session covers implementing a local database, and we’ll cover relational vs non-relational ones.
Watch Video

Going offline first with Flutter: Best practices

Gianfranco Papa & Elian Ortega
CTO ,Co-Founder & Software Engineer
Somnio Software

Going offline first with Flutter: Best practices

Gianfranco Papa & ...
CTO ,Co-Founder & So ...
Somnio Software

Going offline first with Flutter: Best practices

Gianfranco Papa ...
CTO ,Co-Founder & Softwar ...
Somnio Software

Jobs

No results found.

Step 4 — Launch the App and Observe a Run-Time Crash?

After some compile-time turbulence, and reading various online opinions claiming that “Koin will casually crash at run-time”, I assumed my migration landing will be rough. But when I hit “Run” in Android Studio, to my surprise, the app launched without a hitch. No crash at startup, no errors in Logcat. I tried some quick smoke tests, then did regression testing on my own — still nothing. The app worked exactly as it did before the migration.

Maybe I was just lucky, or maybe it was because my seat belt was fastened for the entire flight.

Summary

Today’s flight was quick and hassle-free — and I hope yours goes just as smoothly. Depending on the complexity of your codebase and the size of your team, you might choose a different migration strategy. But for my small, up-to-date app, the experience turned out to be surprisingly fine. If you’re also transitioning from a Hilt-based setup to Kotlin Multiplatform, I hope this article helps guide the way.

Wishing everyone a peaceful time with loved ones, and a happy year 2025!

. . .

This article is previously published on proandroiddev.com.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
This is the second article in an article series that will discuss the dependency…
READ MORE
blog
Using annotations in Kotlin has some nuances that are useful to know
READ MORE
blog
One of the latest trends in UI design is blurring the background content behind the foreground elements. This creates a sense of depth, transparency, and focus,…
READ MORE
blog
Now that Android Studio Iguana is out and stable, I wanted to write about…
READ MORE
Menu