Home > Back-end >  How to pass context inside Retrofit interface
How to pass context inside Retrofit interface

Time:11-06

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.

  • Related