'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