I am trying to write a database query in Cloud Firestore in Kotlin.
The request itself is working but I am having some problems with the async workflow.
My goal is to make a database request that fetches a lot of data and puts this into an object.
Once this is complete for all objects, the normal workflow of the app should continue. (because I need to display some of this data in my UI).
My database structure:
I have a collection of users
-> Each user has a collection of their own recipes.
-> In each recipe there is a collection of Steps to complete and a collection of ingredients
My goal is to get the data and show it on my app screen (code below), but with my current code, the app starts the request and continues to show the app screen (which means there is no data loaded up to this point).
Is there a way to make my database request more efficient (as I have 3 requests per recipe) and also to make it work async? So that when the data gets loaded the UI will recognize a change and show this to the UI later on or the UI waits till all the requests are complete?
Sorry if this is a basic question about coroutines, but I am pretty new to this topic and could not find any info in the Firebase documentation.
My Code:
in My Main Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loadRecipes()//should load the information from the Database
initUiElements()//should show the Information in a Recycler View
}
-> load Recipies calls the following Class with the loadloadRecipes() function
Class: FirebaseRecipes:
fun loadRecipes(){
//First DB Request to get the Recipe Names and Durations
Log.d("FirebaseRecipes", "loadRecipes()")
db.collection("users").document(userID.toString()).collection("Recipes").get()
.addOnSuccessListener { result ->
for (document in result) {
val duration = document.data["duration"].toString()
val name = document.data["name"].toString()
var recipe = Recipe(name, duration)
RecipesList.add(recipe)
//Second DB Request to get the Ingredients
db.collection("users").document(userID.toString()).collection("Recipes").document(document.id).collection("ingredients").get()
.addOnSuccessListener { result ->
for (document in result) {
val amount = document.data["amount"].toString()
val name = document.data["name"].toString()
val barcode = document.data["unit"].toString()
val ingredient = Ingredient(name, amount, barcode)
recipe.ingredients.add(ingredient)
}
}
//Third DB Request to get the Steps
db.collection("users").document(userID.toString()).collection("Recipes").document(document.id).collection("steps").get()
.addOnSuccessListener { result ->
for (document in result) {
val step = document.data["text"].toString()
recipe.steps.add(step)
}
}
}
}
}
2
Answers
With this question, what you need to do is learn about
StateFlow
ShareFlow
LiveData
Flow
Suspend Function
To know how to save, listen, handle data asynchronous
If you didn’t learn about them before just learn
For example you have State Flow in ViewModel
Then listen data at UI like this
Data will never be destroyed if you do other things
There is no such limitation in Firestore. You can add as many documents as you want in a collection, even billions.
So if you think that the maximum number of recipes a user can have is 100, then you can add all of them in a single document, including the ingredients and the steps. In this way, you’ll have to pay for a single-read operation. However, there is a limitation when it comes to how much data you can store in a single document. You can only store data up to 1 MiB.
If you think that you can get over this limitation, which I doubt, then instead of creating one document, you might consider creating two, or even three. So there is no need in this case to create a separate document for each recipe, ingredient, and step. All the data cand be stored as object in an array.