Retrieve Firebase Data As A Custom Object? - android

I'm trying to make handling database data easier in an app that I am creating.
This is the object being used to upload the data to Firebase. This is also the object I want to convert the data back into to be used by the app's interface:
class WYRQuestionDBModel(var side1Question: String,
var side2Question : String,
var side1Count : Int,
var side2Count : Int)
So far I am getting this result:
{side2Question=empty, side2Count=1, side1Count=1, side1Question=empty}
How can I convert this back to the object above in Kotlin?
Here is the function that I ma trying to achieve this in:
Firebase.database.reference.child(Constants.wyrDBQuestionLocation)
.child(randQuestionId.toString())
.get()
.addOnFailureListener { task ->
Log.i("Get WYR Question Status", "Failed: " + task.message.toString())
//All values remain the same
}.addOnSuccessListener {
Log.i("Get WYR Question Status", "Success")
Log.i("Request Data Result", it.value.toString())
dataRequestResult = it.value.toString()
//Convert the data to an object here
Log.i("Database Result", dataRequestResult.toString())
}

Did you see the Firebase documentation on reading and writing data? The Post class in there should be a pretty direct template for your WYRQuestionDBModel class.
If you follow the pattern outline in those docs, reading a `` can be done with:
val question = it.value.getValue<WYRQuestionDBModel >()
Note: be sure to include the -ktx extensions version of the SDK.

Related

Get an Object from Firebase Firestore in Kotlin

I alredy searched here in the forum but i didn't find nothing like that. I want to get an Object from Firebase Firestore, but I can't manipulate the object that I am receiving. How can I separate the imageLink from the imageTitle?
The database structure is:
The code I am using is:
firebaseFirestore.collection("image").get()
.addOnSuccessListener { documentSnapshot ->
val imageData = documentSnapshot.documents[0].data?.getValue("imageData")
}
But, when I do that, I receive something like that:
How can I get the imageLink from the imageTitle separately?
You can try casting imageData as a Map as shown below:
db.collection("images").limit(1).get().addOnSuccessListener { querySnapshot ->
val imageData =
querySnapshot.documents[0].data!!.getValue("imageData")?.let { it as Map<*, *> }
val imageLink = imageData?.get("imageLink")
val imageTitle = imageData?.get("imageTitle")
Log.d("MainActivity", "imageLink: $imageLink")
Log.d("MainActivity", "imageTitle: $imageTitle")
}
Checkout the documentation for let for more information.
You are calling get() on a CollectionReference that will fetch all documents from the collection (if you add any). If you only want to fetch 1st document from the collection, you can add limit(1) that'll save any additional charges.

how to save an object in object in real-time database, Firebase (kotlin)

I would like to save data into the real-time database of Firebase, but I run into a problem. I've only been working with Kotlin for 1 month, and unfortunately I don't know how to save data to the real-time database in this form, as shown in the picture.
My current code is this:
data class userData(
val username: String,
val uid: String,
)
database = FirebaseDatabase.getInstance().getReference("Userdata")
val user = uid?.let {
userData("Test1", it)
}
if (uid != null) {
database.child(uid).setValue(user).addOnSuccessListener {
Toast.makeText(this, "Success saved", Toast.LENGTH_LONG).show()
}.addOnFailureListener {
Toast.makeText(this, "Failed", Toast.LENGTH_LONG).show()
}
}
My database. The same way, I would like to be able to save it with Kotlin
https://i.stack.imgur.com/fbAsC.png
The problem is, I don't know how to save the data object to the database, is there an easy way?
I would suggest you to improve your data class for userData model like below because there are some cases, you will have error about it in future.
data class userData(
val username: String? = null,
val uid: String? = null,
)
To answer your question, you need to create a new data class for the data model like below.
data class UserDataExtra(
val banned: Boolean? = null
)
Next you just need to implement inside the addOnSuccessListener like below.
if (uid != null) {
database.child(uid).setValue(user).addOnSuccessListener {
//Here you will update new model for userDataExtra
val userExtra = UserDataExtra (false)
database.child(uid).child("UserDataExtra").setValue(userExtra )..addOnSuccessListener {
Toast.makeText(this, "Success saved with data extra!", Toast.LENGTH_LONG).show()
}
}.addOnFailureListener {
Toast.makeText(this, "Failed", Toast.LENGTH_LONG).show()
}
}
Get instance of your database:
val database = Firebase.database.reference
The call to write to your database:
database.child("UserData").child(uid).setValue(user)
To Read from your database once:
database.child("UserData").child(uid).get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
}
This article has the solution to what you're looking for.
https://firebase.google.com/docs/database/android/read-and-write?hl=en&authuser=0#read_data
It is also possible to listen for changes in your database and update accordingly mentioned in the article above.
I think what you forgot is to tell the table name inside the database.
You should try using database.child(tableName).child(uid).setValue(user)
and then the addOnSuccessListener

How to covert the $document.data details to useful format so that I could use it in listviews

I would like to convert $document.data details to useful format so that I could use it for further applications. This is data from firestore documents.
private val mFireStore = FirebaseFirestore.getInstance()
mFireStore.collection("Users").whereEqualTo("lastName","H").whereEqualTo("firstName", "Uc").get()
.addOnSuccessListener{ documents ->
for(document in documents){
Log.d("TAG","${document.id}=>${document.data}")
Toast.makeText(applicationContext, "${document.id} => ${document.data}",
Toast.LENGTH_LONG).show()
}
}
.addOnFailureListener{exception ->
Log.w("TAG","Error getting documents:",exception)
Toast.makeText(applicationContext, "Failed",
Toast.LENGTH_LONG).show()
}
This is my code. Now when I run the code get this in the logcat
OL0rD4UfgHSh2K8UoTMnX6Xea6P2=>{lastName=H, image=, firstName=Uc, B=L, gender=, organization=, profileCompleted=0, mobile=0, blood group=o+, id=OL0rD4UfgHSh2K8UoTMnX6Xea6P2, email=jojoy09#gmail.com}
Now I want to convert this result to a useful format so that I could use it later. I wpuld like to convert the data so that I could load it in listview.
In the following for-loop:
for(document in documents) { ... }
The "document" object is of type DocumentSnapshot. When you call getData() on such an object, the type of object that is returned is a Map<String, Object>.
In Kotlin, this object is represented by Map<String, Any>. In order to get the data, you can simply iterate over the Map and get the data accordingly, using the following lines of code:
val map = document.data
for ((key, value) in map) {
Log.d("TAG", "$key = $value")
}
Or even simpler, using:
map.forEach { (key, value) -> Log.d("TAG", "$key = $value") }
However, if you only need, the value of a particular property, for example, the value of the email address, you can simply get it by using DocumentSnapshot#getString(String field) method:
val email = document.getString("email")
Log.d("TAG", email)
The result in the logcat will be:
jojoy09#gmail.com
.................
As I see in your screenshot, almost all of the properties are of type String. However, you can find different flavors for each type of field, like getLong(String field), getDouble(String field), getDate(String field), getTimestamp(String field), and so on.
Furthermore, if you need to get the entire document, and you want to convert it into an object of a specific class, as also #mehulbisht mentioned in his answer, you should use DocumentSnapshot#toObject(Class valueType). So assuming that you have a data class that looks like this:
data class User(
var email: String? = null,
var firstName: String? = null,
var lastName: String? = null,
//Other necessary fields
)
To convert the "document" object into an object of the "User" class, please use the following line of code:
val user = document.toObject(User::class.java)
Log.d("TAG", user.email)
The result in the logcat will be the same as above.
If you want to display a list of "User" objects in a ListView, then please check my answer from the following post:
What miss, with connect Firestore and ListView for random results in sample?
It's really simple to convert the Java code into Kotlin.
The Model that you used to set this data will be used here. You can convert the documents to your Model class using the .toObjects() method on it. Just use:
val myObjs = documents.toObjects(Model::class.java)
EDIT
For displaying this as a Log in Logcat use:
Log.d("myObjs ","""
$myObjs
""".trimIndent())
Do tell if this doesn't work for you :)

Need help Kotlin Coroutines, Architecture Component and Retrofit

I'm trying to wrap my head around the mentioned components and I can't get it right. I want to do something very simple: Fetch data from the network and present it to the user. Currently am not yet caching it as am still learning new Coroutine features in Architecture components. Every time app loads I get an empty model posted, which seems weird.
My API is get hit fine and response is 200 which is OK.
Below is what I have attempted:
POJO
data class Profile(#SerializedName("fullname") val fullName : String.....)
Repository
class UserRepo(val context: Context, val api: Api) {
suspend fun getProfile(): Profile
{
val accessToken = ....
return api.getUserProfile(accessToken)
}
}
API
interface GatewayApi {
#GET("users/profile")
suspend fun getUserProfile(#Query("access-token") accessToken: String?): Profile
}
ViewModel
class UserViewModel(application: Application) : AndroidViewModel(application) {
private val usersRepo = UserRepo(application.applicationContext, Apifactory.Api)
val userProfileData = liveData{
emit(usersRepo.getProfile())
}
fun getProfile() = viewModelScope.launch {
usersRepo.getProfile()
}
}
Finally my fragment's relevant code
val viewModel = ViewModelProviders.of(activity!!).get(UserViewModel::class.java)
viewModel.userProfileData.observe(this, Observer<UserProfile> {
//it is having nulls
})
//trigger change
viewModel.getProfile()
So I added HTTP requests and responses (thanks to #CommonsWare for pointing that out) and it happened I had used a different model than I was supposed to use. The correct model that mapped the JSON response was ProfileResponse and as you can see in my posted code, I used Profile instead. So all fields were empty as Gson could not correctly serialize JSON into Profile object.
All the credit goes to #CommonsWare for pointing that out in comment.

Android MVVM: how can I update data in a Repository from another Repository, when same data is part of response from different server endpoints?

I'm trying to move Android project to MVVM. Two different repositories get same data response, when calling different server endpoints. What is the right way to keep both up-to-date?
There are different server endpoints returning "UserData": "/get_user_data" and "/get_user_statistics". I need to update data in "UserRepository", when making request in "UserStatisticsRepository" (which returns UserData as well).
Should I inject both Repositories in a ViewModel and then set the "UserData" via ViewModel to "UserRepository"? It just feels to me not really right way to set data to a Repository from a ViewModel...
Let's say, I have:
data class UserData(
#SerializedName("id") val id: Int,
#SerializedName("name") val name: String
)
and
data class UserStatisticData(
#SerializedName("id") val id: Int,
#SerializedName("active_users_count") val activeUsersCount: Int,
#SerializedName("users") val users: List<UserData>
)
and
class UserStatisticRepository(...) {
...
suspend fun makeUserStatisticDataRequest(){
withContext(Dispatchers.IO) {
val networkResponse = myAppAPI?.getUserStatisticData(params)
try {
if (networkResponse!!.isSuccessful) {
_userStatisticData.value = networkResponse.body()?.userStatisticData
// TODO: Should I set UserData (networkResponse.body()?.userData) to UserRepository here???
// How can I access UserRepository?
} else {
throw UserStatisticDataResponseError(Exception("Response body error..."))
}
} catch (e: Exception) {
throw UserStatisticDataResponseError(Exception("Network error..."))
}
}
}
...
}
I am not sure how your projwct is set up but If I was in this kind of situation I would :
Either have a single state that is mutated by both of the repository, make it reactive. Both of the repostitory can take that state as argument when being constructed and they can also mutate the state.
A reactive database that is mutated by both the calls. i.e. whenever any new UserData entity is fetched, its written to databse. When we need UserData we listen it directly from Reactive Database like "Room" instead of those API endpoints directly.
When to do what?
I would choose the first option if I did not have to persist the fetched data & I am cool with losing all the data once app is closed where as If I wanted the data from those endpoints to survive App restart the I would go for the second options.
For the first option the code may look like this :
data class UserDataSource(val userDataList : List<UserData>)
class UserDataRepo(val subject : PublishSubject<UserDataSource>){
UserdataService.getUserData(userID).subscribe{
// Do calculations to find the old list of user and edit or update the new user
subject.onNext(UserDataSource(newUserList))
}
}
class UserStatRepo(subject : PublishSubject<UserDataSource>){
UserdataService.getUserStat().subscribe{
// Do calculations to find the old list of user and edit or update the new user
subject.onNext(UserDataSource(newUserList))
}}
In Ui subscribe to the userDataSource subject..
class UserDataViewModel{
val userDataSource = PublishSubject.create<userDataSource>()
val userDataRepository = UserDataRepo (userDataSource)
val userDataRepository = UserStatRepo (userDataSource)
}

Categories

Resources