3

Suppose I have a module in which one binding depends on another:

class MyModule : Module(){
  init {
    bind(SettingsStorage::class.java).to(PreferencesBasedSettingsStorage::class.java)
    // how to use createOkHttpClient here? 
    // how to get instance of SettingsStorage to pass to it?
    bind(OkHttpClient::class.java).to?(???)
  }

  private fun createOkHttpClient(settingsStorage: SettingsStorage): OkHttpClient {
    return OkHttpClient.Builder()
      .addNetworkInterceptor(MyInterceptor(settingsStorage))
      .build()
  } 
}

Here I can create OkHttpClient only having an instance of another binding, namely SettingsStorage. But how to do that?

Currently I see no way to get instance of SettingsStorage binding inside a module to pass it to createOkHttpClient()

In Dagger I would've simply created two provider methods with appropriate arguments like

fun provideSessionStorage(/*...*/): SessionStorage { /* ... */ }

fun provideOkHttpclient(sessionStorage: SessionStorage): OkHttpClient {
   return OkHttpClient.Builder()
    .addNetworkInterceptor(MyInterceptor(settingsStorage))
    .build()
}

And it would figure all out by itself and passed appropriate instance of sessionStorage to a second provider function.

How to achieve the same inside a Toothpick module?

dimsuz
  • 8,969
  • 8
  • 54
  • 88

1 Answers1

2

It's simple with TP:

class MyModule : Module(){
  init {
    bind(SettingsStorage::class.java).to(PreferencesBasedSettingsStorage::class.java)
    // how to use createOkHttpClient here? 
    // how to get instance of SettingsStorage to pass to it?
    bind(OkHttpClient::class.java).toProvider(OkHttpClientProvider::class)
  }
}

and then you define a provider (sorry I don't use Kotlin):

class OkHttpClientProvider implements Provider<OkHttpClient> {
  @Inject SettingsStorage settingsStorage;
  public OkHttpClient get() {
     return OkHttpClient.Builder()
      .addNetworkInterceptor(MyInterceptor(settingsStorage))
      .build()
  }
}

Your provider will use the first binding to provide the OkHttp client.

Snicolas
  • 37,840
  • 15
  • 114
  • 173
  • 1
    I figured that there must be a way to do this using a custom Provider class (after I posted the question :)). So putting @Inject like this will make Toothpick automatically perform field injection in this case? No need to manually call `Toothpick.inject()`? I guess another option would be to use constructor injection. – dimsuz Oct 31 '17 at 09:29
  • Providers are injected automatically in TP. – Snicolas Nov 02 '17 at 20:31
  • "Simple with TP"???? Pardon me, but this is the definition of "boilerplate" -- if you have to write a concrete Provider class with @Inject fields, while in Dagger, you just write a short method with essential implementation. – user4698855 Feb 10 '19 at 02:46
  • @user4698855 yes, it's simpler. You should give it a try. There are very very few cases where you need to write a provider in TP, even a binding is kinda rare. Whereas with dagger you do it for all types you want to inject and for all places you want to inject to... There is far less boilerplate in TP projects. And if you add the ability to change the implementation for mocks when testing, you will see that TP is for real simpler than dagger to use, and by far. – Snicolas Feb 12 '19 at 06:55
  • @Snicolas I gave it a try, and every time I needed to provide an instance of an object that has to be built (which in Dagger is just 1 method) - TP requires a Provider<> class, which then generates two more classes (an adapter and member injector). So, if you use 3rd party libraries, then TP is a non-starter. For simple projects where you only use your own code which you can annotate - then yes, TP would be simpler. – user4698855 Feb 16 '19 at 01:24