Can't Access DataStore Android

79 views Asked by At

I build an Android app and currently working on the user authentication. When the app was installed and launched for the first time, I want to check if there is a user token available on the datastore Preferences. I check the token using Coroutine. But the result was my app being force closed during the process.

Here is a simple workflow how token will be used: Token Workflow

Here is my DataStore Preference class

val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user")

class UserPreferences private constructor(private val dataStore: DataStore<Preferences>){

    private val USER_TOKEN = stringPreferencesKey("user_token")

    fun getToken(): Flow<String?> {
        return dataStore.data.map { preferences ->
            preferences[USER_TOKEN]
        }
    }

   suspend fun deleteToken() {
       ...
    }

    suspend fun saveToken(token: String) {
       ...
    }

    ...

    companion object {
        @Volatile
        private var INSTANCE: UserPreferences? = null

        fun getInstance(dataStore: DataStore<Preferences>): UserPreferences {
            return INSTANCE ?: synchronized(this) {
                val instance = UserPreferences(dataStore)
                INSTANCE = instance
                instance
            }
        }
    }
}

I try to use it with manual injection object

object Injection {
    fun provideUserRepository(context: Context): UserRepository {
        val preferences = UserPreferences.getInstance(context.dataStore)
        val token = runBlocking { preferences.getToken().first() }
        val apiService = runBlocking { ApiConfig.getApiService(token!!) }
        return UserRepository(preferences, apiService)
    }
}

And use that dependency on the viewModelFactory like so

class ViewModelFactory private constructor(private val context: Context) : ViewModelProvider.NewInstanceFactory() {

    private val mGetTokenUseCase = GetTokenUseCase.getInstance(Injection.provideUserRepository(context))

    ...

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(SplashViewModel::class.java)) {
            return SplashViewModel(mGetTokenUseCase) as T
        }

        throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
    }
}

Here is my SplashViewModel

class SplashViewModel(private val getTokenUseCase: GetTokenUseCase): ViewModel() {
    private val _token = MutableLiveData<String?>()
    val token = _token

    init {
        viewModelScope.launch {
            getTokenUseCase.invoke()
                .collect{ value ->
                    _token.value = value
                }
        }
    }
}

Here is my SplashActivity

@SuppressLint("CustomSplashScreen")
class SplashActivity : AppCompatActivity() {
    private val splashViewModel: SplashViewModel by viewModels { ViewModelFactory.getInstance(application) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)

        splashViewModel.token.observe(this) {
            if(it != null) {
                val splashIntent = Intent(
                    this@SplashActivity,
                    MainActivity::class.java
                )
                startActivity(splashIntent)
            } else {
                val splashIntent = Intent(
                    this@SplashActivity,
                    OnboardingActivity::class.java
                )
                startActivity(splashIntent)
            }
            finish()
        }
    }
}

Here is the error, and the app was force closed

Error Logcat

0

There are 0 answers