'Simplifying multiple retrofit instances in Hilt Module
I'm developing an Android application in which my user needs to target 2 different endpoints based on some configuration on their profile. I've refactored the entire application trying to use Clean Architecture concept, and so I used Hilt for DI. At the time I didn't understand how to make the baseUrl dynamic in my retrofit instance inside the AppModule since I will know which endpoint has to be used only after the login (We have a unified login service with firebase and different backends for qa and prod), so I created 2 different instances in this way.
TLDR
I have 2 retrofit instance since I need to use different url, this info can be retrieved only after the unified Login.
How can I improve my AppModule so that I can have only one retrofit instance?
AppModule.kt
private const val BASE_PROD_URL = "some prod url"
private const val BASE_DEV_URL = "some other url"
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Production
@Singleton
@Provides
fun provideProductionMachineDetailApi(): MachineDetailApi {
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(logging)
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()
return Retrofit.Builder()
.baseUrl(BASE_PROD_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(MachineDetailApi::class.java)
}
@Development
@Singleton
@Provides
fun provideDevMachineDetailApi(): MachineDetailApi {
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(logging)
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()
return Retrofit.Builder()
.baseUrl(BASE_DEV_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(MachineDetailApi::class.java)
}
@Production
@Singleton
@Provides
fun provideProductionMachineDetailMainRepository(
@Production api: MachineDetailApi
): MachineDetailBaseRepository = MachineDetailRepositoryImplementation(api)
@Development
@Singleton
@Provides
fun provideDevMachineDetailMainRepository(
@Development api: MachineDetailApi
): MachineDetailBaseRepository = MachineDetailRepositoryImplementation(api)
@Singleton
@Provides
fun provideDispatchers(): DispatcherProvider = object : DispatcherProvider {
override val main: CoroutineDispatcher
get() = Dispatchers.Main
override val io: CoroutineDispatcher
get() = Dispatchers.IO
override val default: CoroutineDispatcher
get() = Dispatchers.Default
override val unconfined: CoroutineDispatcher
get() = Dispatchers.Unconfined
}
}
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class Production
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class Development
Then inside my viewmodel I inject both instances and based on a simple condition I call the API, for example:
SomeViewModel.kt
fun downloadMachineList() = viewModelScope.launch(dispatchers.io) {
val apiKey: String = getKey()
val repo = if (isProduction) productionRepository else devRepository
when (val response = repo.getMachineList(apiKey, requestBody)) {
is Resource.Error -> _plantListStateFlow.value =
PlantListEvent.Failure(response.message!!)
is Resource.Success -> {
_plantListStateFlow.value = PlantListEvent.Success
_plantList.postValue(response.data!!)
}
}
}
Is it totally a bad practice if done this way?
How could I handle the dynamic url so that I can have only one retrofit instance?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
