Reputation: 6785
I'm trying to understand this last step.
I need an atomic one shot operation, I need to go to Firebase and place my user device token.
For this I just make a call to a method in my viewModel which will trigger my repo, but now, I dont want to use LiveData in my repo, instead, I want to just return Resource from there, but since is an asynchronous method I cant just return.
I want only to use LiveData at my viewModel nad not in my repository, the repository should only deliver objects to my viewModel and my viewModel should deliver these to my view.
viewModel.userToken.observe(this, Observer {
when (it.status) {
Status.SUCCESS -> {
val user = FirebaseAuth.getInstance().currentUser
startActivity(Intent(this, SecondActivity::class.java))
Toast.makeText(this, "Welcome ${user!!.uid} !", Toast.LENGTH_SHORT).show()
finish()
}
Status.ERROR -> {
}
else -> {
Toast.makeText(this, "error ${it.message}", Toast.LENGTH_SHORT).show()
}
}
})
class LoginViewModel: ViewModel() {
private val useCase = PostUserToken(UserRepo())
var userToken = liveData(Dispatchers.IO){
emit(useCase.postUserToken())
}
}
Untill here, its working fine, now, from my postUserToken() method in my repo, I need to return a Resource<Boolean>
object to my viewmodel, how do I do this with coroutines ?
class PostUserToken(private val repo: UserRepo) {
suspend fun postUserToken(): Resource<Boolean> = repo.saveUserToken()
}
class UserRepo {
suspend fun saveUserToken(): Resource<Boolean> {
FirebaseInstanceId.getInstance().instanceId
.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.w("saveUserToken", "getInstanceId failed", task.exception)
return@OnCompleteListener
}
// Get new Instance ID token
val token = task.result?.token
})
//Here I need to return the Resource<Boolean> but wait untill it completes
}
}
These are my two helper classes I use
data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(msg: String, data: T?): Resource<T> {
return Resource(Status.ERROR, data, msg)
}
fun <T> loading(data: T?): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
enum class Status {
SUCCESS,
ERROR,
LOADING
}
I'm always stuck at the repository when need to deliver objects to the fellow classes, which is the best approach to deliver this flow ?
I know that if I need to keep listening I should use Flow with Coroutines, but now I only need a one time operation that should deliver this object accross all classes to my view
Thanks
Upvotes: 1
Views: 1941
Reputation: 606
You can call login
from Activity or Fragment without any problem, then you should update your MutableLiveData
to make your Activity aware of the changes.
What I've done to retrieve login from user repository and Firebase Auth is this:
ClassViewModel.kt
class LoginFirebaseViewModel(): ViewModel(){
private val _loginResult = MutableLiveData<LoginResult>()
val loginResult: LiveData<LoginResult> = _loginResult
fun login() {
viewModelScope.launch {
try {
repository.userLogin(email!!,password!!).let {
_loginResult.value = it
}
} catch (e: FirebaseAuthException) {
// Do something on firebase exception
}
}
}
}
UserRepository.kt
class UserRepository(private val firebaseAuth: FirebaseAuth) {
suspend fun userLogin(email: String, password: String) : LoginResult{
val firebaseUser = firebaseAuth.signInWithEmailAndPassword(email, password).await() // Do not forget .await()
return LoginResult(firebaseUser)
}
}
LoginResult
is a wrapper class of firebase auth response.
I hope this help you
Upvotes: 1
Reputation: 317332
It's perfectly valid for a repository object to return a LiveData. That's what I would do. I don't think you should should try to make it return anything synchronously, as that defeats the purpose of coroutines entirely.
If you want to use a Task with coroutines, look into using this library that converts play services Task objects into something that can be awaited in a suspend fun:
Upvotes: 4