Should the fragment or the ViewModel decide when t...
# android-architecture
f
Should the fragment or the ViewModel decide when to show this snackbar? My LoginFragment pops itself from the backstack when the user is logged in:
Copy code
mainViewModel.sessionManager.firebaseUser.observe(viewLifecycleOwner) {
            if (it != null) {
                previousSavedStateHandle.set(LOGIN_SUCCESSFUL, true)
                binding.editTextEmail.clearFocus()
                binding.editTextPassword.clearFocus()
                findNavController().popBackStack()
            }
        }
The login method in the ViewModel looks like this:
Copy code
fun login(email: String, password: String) {
        if (email.isEmpty() || password.isEmpty()) {
            viewModelScope.launch {
                snackBarMessage.send("Please fill out all fields")
            }
            return
        }

        viewModelScope.launch {
            _loginLoading.value = true
            when (val result = authRepository.login(email, password)) {
                is AsyncResult.Success -> {
                    val firebaseUser = sessionManager.firebaseUser.value!!
                    snackBarMessage.send("Logged in as ${firebaseUser.displayName}")
                }
                is AsyncResult.Error -> snackBarMessage.send(
                    result.throwable.localizedMessage ?: "Something went wrong"
                )
            }
            _loginLoading.value = false
        }
    }
My question is about that confirmation snackbar (
"Logged in as ${firebaseUser.displayName}"
). Does the code that triggers the snackbar belong into the when statement inside the ViewModel (where it is right now), or can/should I put it into the
if (it != null)
block inside the fragment? I'm asking this because the latter option would get rid of the problem that the snackbar doesn't arrive early enough before the fragment is popped from the backstack. But I am not sure if showing a snackbar is considered "business logic" here and should be in the ViewModel. (The snackbar event is send via a Kotlin channel here that the fragment reacts to)
j
ViewModel
emit states, view observes and paint them
f
Thank you. Does that mean it would be wrong to put
Snackbar.make
into the if
(it != null)
block?
j
I should move that logic to the VM, in other words, just emit if
it
is not null
f
I'm specifically interested in the snackbar here
j
When you pass the email and password to the VM, if they are null or empty, I should emit an error state which the view should be observing and then it shows the snackbar
f
right, but as explained in the opening post I'm specifically wondering about the place of the confirmation snackbar (
"Logged in as ${firebaseUser.displayName}"
). Would it be wrong to put that directly into the observe method?
in other words, should the ViewModel make the decision when to show that snackbar?
j
snackbarMessage is a livedata?
f
it's a channel that I then collect as a Flow
👍 1
so pretty similar to LiveData
I'm still interested in an answer
f
Can you observe that firebase user sessionManager from the VM instead of from the fragment? If you can, you could move that logic to the VM, and ask the fragment to pop the snackbar
if != null
. If you can’t, then you might send
it
to the VM, and have the VM ask the fragment to pop the snackbar if it’s not null.
f
yes I think I can get rid of that
!= null
check
but I also want to understand why
is this considered "business logic"?
f
I think so. “Once the user has logged in, update the UI to reflect that.” I think one of the best benefits of using VMs is that you can easily test them. Therefore, if you want to write a test to verify that the UI is updated after the user has logged in, you can do it. If you were observing the session from the fragment, testing it may be more difficult.
In that case, I think that you could have a “User Logged In” state, which the view observes and updates the UI accordingly.
f
I get it, thank you very much!
😁 1
but instead of observing the sessionManager in the ViewModel, could I not just put an
isLoggedIn
LiveData into the sessionManager directly? This way I don't have to create a LiveData for each fragment
f
Hmm. Personally, I wouldn’t connect a fragment with the sessionManager if I can help it. I like my views to depend only on viewModels, so the VM would ask or observe the sessionManager to know if the user is logged in, and update the view accordingly. This way, I know that the views are only used for “Android UI” stuff, and everything else connects with the VM. The testing becomes easier, and if anything happens with the sessionManager, it only affects the viewModel.
I wouldn’t overthink it much though, if having the connection in the fragment makes your life easier, go with it. It’s not that bad, and there’s probably better options than mine 😅
f
I forgot to say thank you again but this was really helpful in hindsight