Replacing Koin with Dagger?

from the CommonsWare Community archives

At January 19, 2020, 4:23pm, sagarsuri56 asked:

Since Google officially recommends Dagger as the DI framework. Will you add a new chapter or replace Koin with Dagger in Elements of Android? @mmurphy

At January 19, 2020, 4:36pm, mmurphy replied:

IMHO, the vast majority of apps do not need the complexity of Dagger. I do not plan to cover Dagger; other authors can do that.

At January 22, 2020, 1:03am, mmurphy replied:

Circling back to this… I am still getting caught up on Android Developer Summit videos :frowning:.

The presentation on “Opinionated DI” pretty much reinforces my opinion that the vast majority of apps do not need Dagger. Their chart with equal size boxes for “small”, “medium”, and “large” projects suggests that there are equal numbers of those projects. I believe that there are few “medium” and very few “large” projects, compared to the number of “small” projects. Plus, Dagger ties you to the JVM, whereas Koin is Kotlin/Multiplatform-friendly.

However, if Google comes up with some sort of Jetpack wrapper around Dagger, where the complexity of Dagger is greatly reduced, then I would consider covering that. I’d probably stick with Koin for Exploring Android, but I would expand the Elements of Android Jetpack chapter on dependency inversion to show both Koin and whatever the Jetpack/Dagger thing winds up being.

At February 11, 2020, 4:01am, sagarsuri56 replied:

Yeah you are right about Koin is multiplatform-friendly. That is an advantage over Dagger. I am just thinking, is it a good trade to ignore compile time safety what dagger provides over Koin?

At February 11, 2020, 12:01pm, mmurphy replied:

I am not certain what aspect of type safety that you get with Dagger that you do not get with Koin.

For example, suppose we have this Koin module:

  private val koinModule = module {
    single { ToDoDatabase.newInstance(androidContext()) }
    single {
      val db: ToDoDatabase = get()

      ToDoRepository(db.todoStore(), get())
    single {
      Handlebars().apply {
        registerHelper("dateFormat", Helper<Calendar> { value, _ ->
              androidContext(), value.timeInMillis,
              DateUtils.MINUTE_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, 0
    single { RosterReport(androidContext(), get()) }
    single { OkHttpClient.Builder().build() }
    single { ToDoRemoteDataSource(get()) }
    single { PrefsRepository(androidContext()) }
    viewModel { RosterMotor(get(), get(), get(), androidContext()) }
    viewModel { (modelId: String) -> SingleModelMotor(get(), modelId) }

I cannot provide the wrong types as the parameters to the constructors and factory functions that I am calling, as Kotlin’s type safety prevents that. So, even though I am using get() all over the place, so long as there is only one match for the type that I am trying to get(), Koin should do the right thing. If there are collisions — multiple injectable objects with the same type — you need to use additional annotations… just as you need to with Dagger.

One advantage of Dagger over Koin is that Dagger code-generates all of the plumbing to connect injection sites with their sources of injected objects. With Koin, that is handled at runtime, and it will be slower as a result. And there may well be some fairly complex scenarios where Dagger does handle type safety better as a result, but I cannot name any off the top of my head.

At March 10, 2020, 3:07am, sagarsuri56 replied:

Yeah completely make sense. Practically even I have not seen anyone facing a blocker using Koin but with Dagger, yes(complexity). Going forward with KMP there is only Koin or Kodein and no Dagger.