0

I am trying to make a simple app that uses firestore, that just insert the token of devices and fetch the names corresponding to that token. When I tried to fetch "name" value , the flow in my helper class getting the correct value, but viewModel is not getting that value .

I think I got 2 observation from the log->

1. By seeing the log , first

 2024-10-05 09:32:29.658 29752-29808 FirestoreHelperForToken com.example.fcmpractice              D  Setting up addSnapshotListener 

first my class is created. then My viewModel tried to access the value , therefore this log is coming

 "2024-10-05 09:32:30.350 29752-29752 ChatViewModel           com.example.fcmpractice              D  List is null "

then fetchAllName of variable is accessing it

"2024-10-05 09:32:30.826 29752-29752 FirestoreHelperForToken com.example.fcmpractice              D  Snapshot size: 10 
2024-10-05 09:32:30.833 29752-29752 FirestoreHelperForToken com.example.fcmpractice              D  Names list: [mona, abhijit, mahesh, ritu, jodha, madhav, babu, raghav, mamu, nammy] "

maybe that's why my viewModel is not taking getting the value.

2. In my viewModel , this code piece

viewModelScope.launch {
    firestoreHelperForToken.fetchAllNames.collect { names ->
        _getAllNames.value = names
        Log.d("ChatViewModel", "Fetched names from Firestore: $names")
    }
}

log inside collect function is not printing. I don't know why. Please help me get your opinion and help.

These are my code files, I tried using the log in every step but cannot solve the problem.

  1. Helper class(important part)

    class FirestoreHelperForToken {
    
        val scope = CoroutineScope(Dispatchers.IO)
        private val TAG = "FirestoreHelperForToken"
        private val _db = Firebase.firestore
    
        val fetchAllNames: Flow<List<String>> = channelFlow {
            trySend(emptyList())
            Log.d(TAG, "Setting up addSnapshotListener")
    
            _db.collection("tokens")
                .addSnapshotListener { snapshot, e ->
                    if (e != null) {
                        Log.w(TAG, "Listen failed with error: $e")
                        return@addSnapshotListener
                    }
    
                    if (snapshot == null || snapshot.isEmpty) {
                        Log.d(TAG, "Snapshot is null or empty.")
                        trySend(emptyList()) // Emit empty list if no documents are found
                        return@addSnapshotListener
                    }
    
                    val namesList = mutableListOf<String>()
                    Log.d(TAG, "Snapshot size: ${snapshot.size()}")
    
                    for (document in snapshot.documents) {
                        val name = document.getString("name")
                        name?.let {
                            namesList.add(it)
                        }
                    }
                    try {
                        Log.d(TAG, "Names list: $namesList")
                        trySend(namesList) // Emit the list of names
                    } catch (sendError: Exception) {
                        Log.e(TAG, "Error while sending names list: ${sendError.message}")
                    }
                }
    
        }.flowOn(Dispatchers.IO)
    
    
    }
    
    data class TokenData(
        val name: String,
        val token: String
    )
    
  2. ViewModel(important part regarding the question)

    class ChatViewModel
        (
        private val chatRepository: ChatRepository,
        application: Application
    ) : AndroidViewModel(application) {
    
    
    
        val firestoreHelperForToken = FirestoreHelperForToken()
        private val _getAllNames = MutableLiveData<List<String>>()
        val getAllNames : LiveData<List<String>> get() = _getAllNames
        init {
    
            //subscribe to a topic to broadcast the message
            viewModelScope.launch {
    
                firestoreHelperForToken.fetchAllNames.collect { namesList ->
                    _getAllNames.value = namesList
                    // Log inside the collect block to ensure that the data is fetched
                    Log.d("ChatViewModel", "List from Firestore: $namesList")
    
                    // Small delay to ensure data sync if needed
                    delay(100)
    
                    // Check if list is not null or empty and log it
                    if (!namesList.isNullOrEmpty()) {
                        Log.d("ChatViewModel", "Valid list received: $namesList")
                    } else {
                        Log.d("ChatViewModel", "Empty list received.")
                    }
                }
    
                // Log outside of the collect block after it finishes fetching
                Log.d("ChatViewModel", "List outside the collect block: ${getAllNames.value}")
        }
    }
    
    
  3. AppNavigation

    @Composable
    fun AppNavigation(viewModel: ChatViewModel,context: Context){
        val navController = rememberNavController()
        val state = viewModel.state
        NavHost(navController = navController, startDestination = "JoinConversation")
        {
            composable(route = "JoinConversation"){
                EnteringTokenScreen(
                    onJoinConversationClicked = {
                        navController.navigate("ChatListScreen")
                    },
                    name = state.nameText,
                    onNameChange = viewModel::onNameChange,
                    onSaveTokenClicked = {
                        viewModel.insertToken(it,onSameName = {
                            Toast.makeText(context,"Name already exists",Toast.LENGTH_SHORT).show()
                        })
                    }
                )
    
            }
            composable(route = "ChatListScreen"){
                val nameList = viewModel.getAllNames.observeAsState(emptyList())
                ChatListScreen(nameList.value)
            }
        }
    }
    
  4. ChatListScreen

    @Composable
    fun ChatListScreen(nameList : List<String>){
    
        LazyColumn (
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp)
        ){
    
            items(nameList){
                name->
                Text(text = name)
    
            }
        }
    }
    

    Logs I am getting are as follows

    2024-10-05 10:24:09.915 30753-30779 FirestoreHelperForToken com.example.fcmpractice              D  Setting up addSnapshotListener
    2024-10-05 10:24:10.645 30753-30753 ChatViewModel           com.example.fcmpractice              D  List from Firestore: []
    2024-10-05 10:24:10.650 30753-30753 FirestoreHelperForToken com.example.fcmpractice              D  Snapshot size: 12
    2024-10-05 10:24:10.670 30753-30753 FirestoreHelperForToken com.example.fcmpractice              D  Names list: [mona, abhijit, mahesh, ritu, jodha, biru, madhav, babu, sujal, raghav, mamu, nammy]
    2024-10-05 10:24:10.751 30753-30753 ChatViewModel           com.example.fcmpractice              D  Empty list received.
    2024-10-05 10:24:10.758 30753-30753 ChatViewModel           com.example.fcmpractice              D  List outside the collect block: []
    
2
  • 1
    If you encounter problems, it's best to create a MCVE when posting a question. You posted almost 300 (three hundred) lines of code for this issue. That's way too much code for people to parse and try to debug online. Please edit your question and isolate the problem, in that way, you increase your chances of being helped. Please take a moment and read how to ask a question. Commented Oct 5, 2024 at 10:52
  • @AlexMamo Thanks for suggestion, I tried editing the question and reducing the code. Check it once . Please help me to find out answer. Commented Oct 5, 2024 at 13:40

1 Answer 1

0

To collect the Flow in viewModel, instead of using

 _getAllNames.value = namesList

You should use this

_getAllNames.postValue(namesList)

to asynchronously collect the value. And also modify Helper class to fetch names as->

fun getNamesFlow() : Flow<List<String>> = callbackFlow {
        val collectionRef = _db.collection("tokens")
        val listner = collectionRef.addSnapshotListener { snapshot, error ->
            if (error != null) {
                close(error)
                return@addSnapshotListener
            }
            if (snapshot != null && !snapshot.isEmpty){
                val nameList = snapshot.documents.map {
                    it.getString("name") ?: ""
                }
                trySend(nameList)
        }else{
                trySend(emptyList())
            }
        }
        awaitClose {
            listner.remove()
        }
    }

}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.