2

I have an app that saves user data in SharedPref every time user login, but after user login with another account after the previous one logged out koin still getting the previous user data.

i already checked my createSession/save user data function and its working fine.

here is how i put Auth header inside of interceptor by accessing SharedPreferences with Koin :

// inside interface
companion object {
    fun create(sessionManager: SessionManager): AuthorizedInterface {
        val at = "Bearer ${sessionManager.userDetail[ACCESS_TOKEN]!!}"

        val interceptor: HttpLoggingInterceptor = HttpLoggingInterceptor().apply {
            this.level = HttpLoggingInterceptor.Level.BODY
        }

        val builder: OkHttpClient.Builder = OkHttpClient.Builder()
        builder.connectTimeout(2, TimeUnit.MINUTES)
            .readTimeout(2, TimeUnit.MINUTES)
            .writeTimeout(2, TimeUnit.MINUTES)
            .addInterceptor { chain ->
                val request =
                    chain.request().newBuilder().addHeader("Authorization", at)
                        .build()
                return@addInterceptor chain.proceed(request)
            }

        val client: OkHttpClient = builder.apply {
            this.addInterceptor(interceptor)
        }.build()

        val retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build()

        return retrofit.create(AuthorizedInterface::class.java)
    }
}

i even tried to pass context

fun create(ctx: Context): AuthorizedInterface {
        val at = "Bearer ${SessionManager(ctx).userDetail[ACCESS_TOKEN]!!}"

but still getting the same result. Here's my SessionManager:

class SessionManager (context: Context) { private val sharedPreferences: SharedPreferences private val editor: SharedPreferences.Editor var PRIVATE_MODE = 0

fun createSession(
    email: String?,
    fullname: String?,
    username: String?,
    userId: String?,
    accessToken: String?
) {
    editor.putBoolean(LOGIN, true)
    editor.putString(EMAIL, email)
    editor.putString(FULLNAME, fullname)
    editor.putString(USERNAME, username)
    editor.putString(USERID, userId)
    editor.putString(ACCESS_TOKEN, accessToken)
    editor.apply()
}

val userDetail: HashMap<String, String?>
    get() {
        val user =
            HashMap<String, String?>()
        user[EMAIL] = sharedPreferences.getString(
            EMAIL,
            null
        )
        user[FULLNAME] = sharedPreferences.getString(
            FULLNAME,
            null
        )
        user[USERNAME] = sharedPreferences.getString(
            USERNAME,
            null
        )
        user[USERID] = sharedPreferences.getString(
            USERID,
            null
        )
        user[ACCESS_TOKEN] = sharedPreferences.getString(
            ACCESS_TOKEN,
            null
        )
        return user
    }

fun logout() {
    editor.remove(EMAIL)
    editor.remove(FULLNAME)
    editor.remove(USERNAME)
    editor.remove(USERID)
    editor.remove(ACCESS_TOKEN)
    editor.commit()
}

    init {
        sharedPreferences = context.getSharedPreferences(
            PREF_NAME,
            PRIVATE_MODE
        )
        editor = sharedPreferences.edit()
    }
}

And here's my module :

val module = module {
//    single { AuthorizedInterface.create(context = get()) }
single { AuthorizedInterface.create(sessionManager = get()) }
single { SessionManager(context = get()) }

}

What did i do wrong? Why can't i get the latest SharedPref?

2 Answers2

3

use factory instead of single for defining SessionManager in your module.

factory { SessionManager(context = get()) }

By default in Koin, we have 3 kind of scopes:

  • single definition : create an object that persists with the entire container lifetime (can't be dropped).
  • factory definition : create a new object each time. Short life. No persistence in the container (can't be shared).
  • scoped definition : create an object that is persistent tied to the associated scope lifetime.

As a result, when you define SessionManager as single, it is created just once and is not updated whenever the value of SharedPreferences changes.

checkout this :Refresh auth token in Ktor while using koin

2

Yes, it's a regular use-case to have to disconnect all components from session injected from Http/Retrofit session.

  • injecting as factory, you can have side effects while recreating instances
  • using scope is more complex

The simplest way to do that is to use the Koin.unloadModules() function from Koin, to unload modules and drop singleton instances. Then reload it, before connecting.