17

I am using Koin as a DI for my app. I created a module:

object NetworkModule {
    fun get() = module {
        single {
            val authenticationInterceptor = Interceptor { chain ->
                // Request customization goes here
            }

            OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .addInterceptor(authenticationInterceptor) //Not all clients might have this interceptor
                .build()
        }

        single {
            Retrofit.Builder()
                .baseUrl("example.com")
                .client(get(/* I would like to send some paramter here */))
                .addConverterFactory(GsonConverterFactory.create(get()))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build()
                .create(Api::class.java)
        }
    } 
}

How can I create different HttpClient or Retrofit instances which have different parameters set or has different instantiation? For instance, in some cases, I might need OkHttpClient with AutheniticationInterceptor and in some other cases my client might not need to use it.

Can I pass some parameters when calling get() so that I can get different instances? Any suggestions would be apprieciated.

Azizjon Kholmatov
  • 1,136
  • 1
  • 13
  • 26

2 Answers2

45

You can use named properties - e.g.

single<OkHttpClient>(named("auth")){
// here you pass the version with authinterceptor
}
single<OkHttpClient>(named("noAuth")){
// here you pass the version without authinterceptor
}

Then in your get() method you pass the name, e.g.

.client(get(named("auth")))
r2rek
  • 2,083
  • 13
  • 16
  • ^ This is how I do it in the current project I'm working on. – Martin Marconcini Jun 17 '19 at 11:27
  • @r2rek Thanks for your response. Can you give more clear explanation on how I can access this module now? Like `var myVariable = NetworkModule.get()` ? – Azizjon Kholmatov Jun 17 '19 at 13:22
  • Basically you'd also have two instances of you Api interface e.g. ` single("authApi") { Retrofit.Builder() .baseUrl(Const.BASE_API_URL) .client(get("auth")) .addConverterFactory(GsonConverterFactory.create(get())) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() .create(Api::class.java) ` – r2rek Jun 17 '19 at 13:27
  • I understood this point. I think I was not clear with my question. What I actually asked you was "How can I use those 2 Retrofit instances now"? Like this `val api: Api = get("auth")` ? – Azizjon Kholmatov Jun 17 '19 at 14:46
  • it depends where you'll be using that. If you'll be using this in a constructor from module, then yes, `get("auth")` will be the way to go. If, however you will be injecting it directly, then `inject("auth")`. – r2rek Jun 17 '19 at 14:48
9

You can do like below (Use koin latest version for named property).Also why I use single and factory because

single— declare a singleton definition of given type. Koin keeps only one instance of this definition

factory — declare a factory definition of given type. Koin gives a new instance each time

const val WITH_AUTH: String = "WITH_AUTH"
const val WITH_OUT_AUTH: String = "WITH_OUT_AUTH"

val remoteModule = module {
factory(named("HEADERS")) {
        val map = it.get<MutableMap<String, String>>(0)
        Interceptor { chain ->
            val original = chain.request()
            val request = original.newBuilder()
            map.forEach { entry ->
                request.addHeader(entry.key, entry.value)
            }
            chain.proceed(request.build())
        }
    }

factory(named("auth")) {
        OkHttpClient.Builder().apply {
            map["AUTHORIZATION"] = "token"

            readTimeout(1, TimeUnit.MINUTES)
            connectTimeout(2, TimeUnit.MINUTES)
            writeTimeout(1, TimeUnit.MINUTES)
            addInterceptor(get(named("HEADERS"), parameters = {
               parametersOf(map)
            }))
        }.build()
    }

factory(named("auth")) {
        Retrofit.Builder()
                .baseUrl("base_url")
                .client(get(named("auth")))
                //.addCallAdapterFactory()
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(ApiService::class.java)
    }

single(named("noAuth")) {
        val map = mutableMapOf(ACCEPT to CONTENT_TYPE)
        OkHttpClient.Builder().apply {
            readTimeout(1, TimeUnit.MINUTES)
            connectTimeout(2, TimeUnit.MINUTES)
            writeTimeout(1, TimeUnit.MINUTES)
            addInterceptor(get(named("HEADERS"), parameters = {
                parametersOf(map)
            }))
          
        }.build()
    }

single(named("noAuth")) {
        Retrofit.Builder()
                .baseUrl("base_url")
                .client(get(named("noAuth")))
                //.addCallAdapterFactory()
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(ApiService::class.java)
    }
}

Now in your activity or viewModel

protected val apiServiceWithoutHeader: ApiService by inject(named(WITH_OUT_HEADER))
protected val apiServiceWithHeader: ApiService by inject(named(WITH_HEADER))

with above object call appropriate API

Community
  • 1
  • 1
Shweta Chauhan
  • 6,739
  • 6
  • 37
  • 57
  • I want to ask what is next after this? how to switch between "noAuth" and "auth" in the same activity? – Quan Nguyen Dec 25 '19 at 09:59
  • @QuanNguyen : I have updated my answer let me know if you found any issue – Shweta Chauhan Dec 26 '19 at 08:55
  • a, thank you for updated answer. I should explain more. I'm using MVVM architecture, have like Datasource, repository, view model and stuff. In koin module beside 2 retrofit instance I also have single { providePostService(get(named(getProperty(RETROFIT_CHOOSE_NAMESPACE)) ))} I want in the same activity (not on onCreate), after logging in, I want to switch retrofit to RETROFIT_LOGGED_NAMESPACE I call
 getKoin().setProperty(RetrofitObject.RETROFIT_CHOOSE_NAMESPACE, RetrofitObject.RETROFIT_LOGGED_NAMESPACE) it doesnt do anything – Quan Nguyen Dec 26 '19 at 09:22