0

I am creating an app that right now is just supposed to be able to use Firebase authentication with mvvm as a standard to get some kind of seperation of concerns. So i have a database class that uses Firebase and gets an injection from Dagger hilt to be able to use Authentication from firebase. Now i am trying to check if the user is logged in. So i type the auth.getCurrentUser(). This does only give me the possibility to check if a user exist and with firebase when you check this and you have deleted the user while you are testing it does not update the value so it's still logged in for another hour when you have deleted the user. If you check around the internet about this you get the answer to use the authstatelistener. My question is though how to use this together with mvvm? is there a way to do this when i have my clases seperated by a viewmodel a repository and a database.

My classes look like this right now.

Database: //This has a comment written in it that has some use for the problem

import com.google.android.gms.tasks.Task
import com.google.firebase.auth.AuthResult
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.lang.Exception
import javax.inject.Inject
import javax.security.auth.callback.Callback


class FirDatabase @Inject constructor(var auth : FirebaseAuth) { 
    suspend fun register(user: User) : Task<AuthResult>{
        return auth.createUserWithEmailAndPassword(user.email, user.password)
    }
    suspend fun checkIfUserExist() : Boolean? {
        //i would like to be able to check it right here somehow
        println("Currentuser " + auth.currentUser?.uid)
        return auth.currentUser != null
    }
}  

Repository:

import androidx.lifecycle.MutableLiveData
import com.google.android.gms.tasks.Task
import com.google.android.gms.tasks.Tasks.await
import com.google.firebase.auth.AuthResult
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class AuthRepository @Inject constructor (private val database: FirDatabase) : IAuthRepository {    
    override suspend fun register(user: User) : Resource<AuthResult> {
            return try{
              val response = database.register(user)
              val result = response.result
              if(response.isSuccessful && result != null){
                  Resource.Success(result)
              }else{
                  Resource.Error(response.exception?.message.toString())
              }
           }catch (e: Exception){
                Resource.Error(e.message ?: "An Error occurred")
           }
    }
    override suspend fun CheckIfloggedIn() : Resource<Boolean>{
           return try{
               val user = database.checkIfUserExist()
               if(user != false){
                    Resource.IsLoggedIn("User is already logged in" )
               }else{
                   Resource.IsNotLoggedIn("User is not logged in")
               }
           }catch(e: Exception){
               Resource.Error(e.message ?: "An Error occurred")
           }
    }  
}

ViewModel:

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class AuthViewModel @Inject constructor(private val repository: AuthRepository, private val dispatchers: DispatcherProvider) : ViewModel(){
    sealed class AuthEvent{
        class Success(val result: String): AuthEvent()
        class Failure(val errorText: String): AuthEvent()
        object Loading : AuthEvent()
        object LoggedIn : AuthEvent()
        object NotRegistered : AuthEvent()
        object NotLoggedIn : AuthEvent()
        object Empty : AuthEvent()
    }

    private val _registering = MutableStateFlow<AuthEvent>(AuthEvent.Empty)
    val registering : StateFlow<AuthEvent> = _registering

    private val _checkUserIsLoggedIn = MutableStateFlow<AuthEvent>(AuthEvent.Empty)
    val checkUserIsLoggedIn : StateFlow<AuthEvent> = _checkUserIsLoggedIn

        fun register(user: User){
            viewModelScope.launch(dispatchers.io) {
                _registering.value = AuthEvent.Loading
                when(val authResponse = repository.register(user)){
                    is Resource.Error -> _registering.value = AuthEvent.Failure(authResponse.message!!)
                    is Resource.Success -> {
                        val response = authResponse.data!!.user
                    _registering.value = AuthEvent.Success("Success")

                }
            }

        }
    }

    fun CheckIfUserIsLoggedIn()
    {
        viewModelScope.launch(dispatchers.io) {
                when(val isUserLoggedIn = repository.CheckIfloggedIn()){
                    is Resource.IsLoggedIn -> _checkUserIsLoggedIn.value = AuthEvent.LoggedIn
                    is Resource.IsNotLoggedIn -> _checkUserIsLoggedIn.value = AuthEvent.NotLoggedIn
                    is Resource.Error -> _checkUserIsLoggedIn.value = AuthEvent.Failure(isUserLoggedIn.message!!)
                }


        }
    }
}

I have followed tutorials from this dude https://www.youtube.com/watch?v=ct5etYgB5pQ and i have already seen alot of the documentation on this page like this for example Firebase: how to check if user is logged in? and here How does the firebase AuthStateListener work?. So with further investigations into the answer you gave me i have come up with this solution... but it is not really working? why is this?

Repository function:

override fun CheckIfloggedIn()  = callbackFlow<Resource<Boolean>>{

    val isUserLoggedIn = flow<Resource<Boolean>>{
        database.checkIfUserExist().collect { isLoggedIn ->
            if(isLoggedIn){
                Resource.Success(isLoggedIn)
            }else{
                Resource.Error("User is not logged in")
            }
        }
    }
}

Database function:

fun checkIfUserExist() = callbackFlow {
    val authStatelistener = FirebaseAuth.AuthStateListener {auth ->
        trySend(auth.currentUser == null)
    }
    auth.addAuthStateListener(authStatelistener)
    awaitClose {
        auth.removeAuthStateListener(authStatelistener)

    }
}
jens
  • 207
  • 2
  • 9
  • I think this article, [How to create an Android app using multiple Firebase products in Kotlin?](https://medium.com/firebase-tips-tricks/how-to-create-an-android-app-using-multiple-firebase-products-in-kotlin-16aade81ffec), along with this [repo](https://github.com/alexmamo/FireApp/blob/master/app/src/main/java/ro/alexmamo/firebase/main/MainRepository.kt), might help. As you can see, you can simply observe the auth state. – Alex Mamo Dec 28 '21 at 08:56
  • If you're interested in using Jetpack Compose, then this article, [How to handle Firebase Authentication in clean architecture using Jetpack Compose?](https://medium.com/firebase-tips-tricks/how-to-handle-firebase-authentication-in-clean-architecture-using-jetpack-compose-e9929c0e31f8) might also help. – Alex Mamo Dec 28 '21 at 08:58
  • so there is no way to do it the way i want to which is with the firDatabase. i dont see a way to get it into the scope of the main thread again? so i can use it in the fragment – jens Dec 28 '21 at 11:31
  • You can use that either with a fragment or with an activity. – Alex Mamo Dec 28 '21 at 11:32

0 Answers0