I'm injecting retrofit instances using HILT, now my problem is that I want to add an authorization token to the request with is stored in Data Store Preferences. I'm new to coroutines and hilt so I'm confused about how to do the same. Previously I used to get the token using shared preferences which was pretty straightforward.
AppModule.kt
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun provideRetrofit(@ApplicationContext context: Context, sessionManager: SessionManager): Retrofit {
//sessionManager.getToken() cannot call this as it requires provideRetrofit() to be a suspend function
val builder = OkHttpClient.Builder()
//This is to check the logs of api request
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
builder.writeTimeout(60, TimeUnit.SECONDS)
builder.readTimeout(60, TimeUnit.SECONDS)
builder.connectTimeout(60, TimeUnit.SECONDS)
//create network interceptor
//create network interceptor
Log.d("TAG", "provideRetrofit: $token")
builder.addNetworkInterceptor(Interceptor { chain: Interceptor.Chain ->
val request = chain.request()
chain.proceed(request).newBuilder()
.header("Cache-Control", "public")
.removeHeader("Pragma")
.build()
})
//create offline interceptor
builder.addInterceptor(Interceptor { chain: Interceptor.Chain ->
var request = chain.request()
if (!Tools.checkInternet(context)) {
val maxStale = 60 * 60 * 24 * 30 // Offline cache available for 30 days
request = request.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=$maxStale")
.removeHeader("Pragma")
.build()
}
chain.proceed(request)
})
builder.addInterceptor(logging)
//setup cache
//setup cache
val httpCacheDirectory: File = File(context.getCacheDir(), "responses")
val cacheSize = 10 * 1024 * 1024 // 10 MB
val cache = Cache(httpCacheDirectory, cacheSize.toLong())
builder.cache(cache)
return Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(builder.build())
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
fun provideApi(retrofit: Retrofit): ApiInterface = retrofit.create(ApiInterface::class.java)
@Provides
@Singleton
fun provideSessionManager(@ApplicationContext context: Context) = SessionManager(context)
}
SessionManager.kt
class SessionManager @Inject constructor(@ApplicationContext private val context: Context) {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = Constants.PREFERENCES)
private val tokenPreferences = stringPreferencesKey(Constants.AUTH_TOKEN)
suspend fun saveToken(token : String){
context.dataStore.edit {
it[tokenPreferences] = token
}
}
fun getToken() = context.dataStore.data.map {
it[tokenPreferences]
}
}
I want to get the value of the token using getToken() function but this function returns flow of data which is suspend function which cannot be called from a function. Any kind of help will be appreciated.
You can use runBlocking
to run a suspend function from a normal function. OkHttp interceptors run on a thread pool and not the main thread, so it should be safe to obtain the current token there.
builder.addNetworkInterceptor(Interceptor { chain: Interceptor.Chain ->
val token = runBlocking {
sessionManager.getToken().first()
}
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer $token")
.build()
chain.proceed(request)
})