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()
}
}
}