I want to pass header authorization token in HTTP requests in retrofit. The token is saved in DataStore. My problem is how to retrieve the token from DataStore and pass it to the intercepter header. I've spent hours thinking of a workaround and searching through the internet but I came with nothing. I'm also new to Kotlin. Here is the code snippet:
interface RoomAPIService {
@GET("rooms")
fun getAllRooms(@Header("Authorization") authHeader: String): Call<List<Room>>
var context: Context
companion object {
var retrofitService: RoomAPIService? = null
var token: String = ""
fun getInstance() : RoomAPIService {
GlobalScope.launch(Dispatchers.IO)
{
//How to pass context to DataRepository.getInstance(context)
token = DataStoreRepository.getInstance().getToken().toString()
}
val httpClient = OkHttpClient.Builder()
httpClient.addInterceptor { chain ->
val request = chain.request().newBuilder().addHeader("Authorization","Bearer " theTokenRetrievedFromDataStore).build()
chain.proceed(request)
}
.
.
}
.
.
}
Here is DataStoreRepository.kt
:
class DataStoreRepository(context: Context) {
private val dataStore: DataStore<Preferences> = context.createDataStore(
name = "token_store"
)
companion object {
private val TOKEN = preferencesKey<String>("TOKEN")
private var instance: DataStoreRepository? = null
fun getInstance(context: Context): DataStoreRepository {
return instance ?: synchronized(this) {
instance ?: DataStoreRepository(context).also { instance = it }
}
}
}
suspend fun savetoDataStore(token: String) {
dataStore.edit {
it[TOKEN] = token
}
}
suspend fun getToken(): String? {
val preferences: Preferences = dataStore.data.first()
Log.d("datastore", "token retrieved: ${preferences[TOKEN]} ")
return preferences[TOKEN]
}
}
And here is MainActivity.kt:
class MainActivity : AppCompatActivity() {
private lateinit var logoutBtn: Button
private lateinit var bottomNavigation: BottomNavigationView
private lateinit var binding: ActivityMainBinding
lateinit var viewModel: RoomViewModel
private val retrofitService = RoomAPIService.getInstance(this)
val adapter = RoomsAdapter()
private var token: String = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
logoutBtn = binding.logoutBtn
bottomNavigation = binding.bottomNavigation
viewModel = ViewModelProvider(this, RoomViewModeFactory(RoomRepository(retrofitService))).get(RoomViewModel::class.java)
binding.recyclerview.adapter = adapter
var token = intent.getStringExtra("token")
Log.d("tokenCheck","checkToken: $token")
if (token != null) {
viewModel.getAllRooms(token)
}
..
Any help will be greatly appreciated!
CodePudding user response:
You can change your getInstance() signature to contain Context object.
fun getInstance(context:Context) : RoomAPIService {
GlobalScope.launch(Dispatchers.IO)
{
token = DataRepository.getInstance(context).getToken().toString()
}
}
CodePudding user response:
This is not a good practise to use "context" in a non-ui module in your application. Networking modules ( or Data layer you may say) should not know about android libraries and components. My suggestion is to use a value in an "variable" which can be changed in runtime and can be seen in the Network module then use it in the OkHttp interceptor. Finally, you just need to initialize the variable at the beginning of your application.