Set UI elements after read firebase kotlin - firebase

I am trying to read a document from a database in firebase and show the info at the UI. Any idea why is it not working?
Viewmodel:
var leid = MediatorLiveData<Gas>()
init {
initializeDocument()
}
fun initializeDocument(){
mFirestore?.collection("expenditures")?.document("ASfavaftaseacdadf")?.get()?.addOnSuccessListener { document ->
if (document != null) {
Log.d("TAG", "DocumentSnapshot data: ${document.data}")
var gastl = document.toObject<Gas>()!!
leid.value=gastl
} else {
Log.d("TAG", "No such document")
}
}
?.addOnFailureListener { exception ->
Log.d("TAG", "get failed with ", exception)
}
}
After that, I use databinding for the UI. When I try to execute the code, I dont get any log or anything; It is like the function never execute.
Thanks in advance.

Related

Kotlin firebase executed with addOnSuccessListener even if fails

I have this piece of code and it is executed with .continueWith and addOnSuccessListener even if failed.I try with continueWithTask but i dont understand very well Tasks API. Please help me to understand how to do this.
db.collection(customGameName).document().get().continueWith {
if (!gameslist.contains(customGameName)) {
gameslist.add(customGameName)
SavedPreference.setGamesList(this, gameslist)
adapter.notifyDataSetChanged()
}else{
Toast.makeText(this,"$customGameName is in the list already!",Toast.LENGTH_SHORT).show()
}
}.addOnFailureListener {
Toast.makeText(this,"$customGameName not exist!",Toast.LENGTH_SHORT).show()
gameslist.remove(customGameName)
SavedPreference.setGamesList(this, gameslist)
adapter.notifyDataSetChanged()
}
If you have code that you only want to run when the task is successful, add a success listener to the task. That should look something like this:
db.collection(customGameName).document().get()
.addOnSuccessListener { document ->
if (document != null) {
Log.d(TAG, "DocumentSnapshot data: ${document.data}")
if (!gameslist.contains(customGameName)) {
gameslist.add(customGameName)
SavedPreference.setGamesList(this, gameslist)
adapter.notifyDataSetChanged()
}else{
Toast.makeText(this,"$customGameName is in the list already!",Toast.LENGTH_SHORT).show()
}
} else {
Log.d(TAG, "No such document")
}
}.addOnFailureListener {
Toast.makeText(this,"$customGameName not exist!",Toast.LENGTH_SHORT).show()
gameslist.remove(customGameName)
SavedPreference.setGamesList(this, gameslist)
adapter.notifyDataSetChanged()
}
Note that this code is pretty much straight from the Firebase documentation on getting a document in Kotlin, so I recommend spending some more time there.

Swift with nested ForEach or For in

Fatal error: each layout item may only occur once: file SwiftUI, line 0
2021-04-20 19:17:54.685999-0500 PetGram[17828:1053156] Fatal error: each layout item may only occur once: file SwiftUI, line 0
I can't figure it out. It only crashes on a second user follow. Once you relaunch you can follow and unfollow and it works fine. If you unfollow until you are below 2 and follow another it crashes again. Any help would be great.
func loadFilteredPosts() {
guard let uid = Auth.auth().currentUser?.uid else { return }
FB.USER_COLLECTION.whereField(FB.USER_FOLLOWERS, arrayContains: uid)
.addSnapshotListener { (snapshot, error) in
if let error = error {
print("DEBUG: Error getting followers from user docs \(error.localizedDescription)")
} else {
if let snapshot = snapshot {
self.filteredUsers = snapshot.documents.compactMap { document in
try? document.data(as: User.self)
}
}
self.posts.forEach { post in
self.filteredUsers.forEach { user in
if user.uid == post.ownerUid {
self.filteredPosts.append(post)
}
}
}
}
}
}

SwiftUI + Firebase - Listener not listening for changes?

I've set up a listener, but it doesn't seem to be changing according to changes in the data. The flow is the following:
If userCustomHabit is empty, user sees a button
When clicked, user can enter text in a TextField from a sheet to add to userCustomHabit (an array of strings)
Now that userCustomHabit is not empty, they should see something else
However, the problem I'm seeing is that userCustomHabits isn't updating in the view itself even though it is updating in the Firestore database.
Anyone know why this is? Included code below:
View
#ObservedObject var viewModel = RoutinesViewModel()
Group {
if self.viewModel.userCustomHabits.isEmpty {
Button(action: {
self.showCreateSheet.toggle()
}) {
Text("Create your own habits")
.font(Font.custom("Roboto-Regular", size: 20))
.frame(width: geometry.size.width * 88/100, height: 200)
.foregroundColor(.black)
.background(Color.init(UIColor.systemGray5))
.cornerRadius(40)
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(style: StrokeStyle(lineWidth: 2, dash: [20]))
.foregroundColor(Color.init(UIColor.systemGray3))
)
}
}
else {
// Something else
}
}
.onAppear(perform: self.viewModel.newHabitsListener)
Sheet
VStack {
TextField("Enter text", text: $enteredText)
Button("Add Habit") {
self.viewModel.createNewHabits(newHabit: self.enteredText)
}
}
View Model
#Published var userCustomHabits = [String]()
func newHabitsListener() {
db.collection("users").document(currUser?.uid ?? "").addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
DispatchQueue.main.async {
self.userCustomHabits = data["userCustomHabits"] as! [String]
}
}
}
func createNewHabits(newHabit: String) {
db.collection("users").document(currUser?.uid ?? "").updateData(["userCustomHabits": FieldValue.arrayUnion([newHabit])])
}
So I played around with your code a bit (since the code sample is incomplete, I had to make a few assumptions), and it seems like you might never have created the document you're writing to in the first place.
updateData only updates existing documents (see the documentation). To create a new document, use setData (see the documentation)
When changing your code form updateData to setData, the listener kicked in as expected.
However, it might be better to add a sub-collection customHabits to each user document. This way, adding new habits is as simple as adding a new document, which also makes querying a lot easier.

Why Firebase Task in RxJava Completable emitter doesn't execute?

I'm developing a Firebase Android application which connect to a Firestore. The nomenclature is that the collection is "Assets". The example code had simple actions like addAsset and deleteAsset, those work fine. This is the data repository layer which actually converse with Firebase, the view model layer is above this.
class FirestoreAssetRepository(secondaryDB: FirebaseFirestore) : IAssetRepository {
companion object {
private const val TAG = "FirestoreAssetRepo"
private const val ASSET_COLLECTION = "Assets"
}
private var remoteDB: FirebaseFirestore
private var changeObservable: Observable<List<DocumentSnapshot>>
init {
remoteDB = secondaryDB
}
override fun addAsset(asset: Asset): Completable {
return Completable.create { emitter ->
remoteDB.collection(ASSET_COLLECTION)
.add(mapToAssetData(asset))
.addOnSuccessListener {
if (!emitter.isDisposed) {
emitter.onComplete()
}
}
.addOnFailureListener {
if (!emitter.isDisposed) {
emitter.onError(it)
}
}
}
}
override fun deleteAsset(assetId: String): Completable {
return Completable.create { emitter ->
remoteDB.collection(ASSET_COLLECTION)
.document(assetId)
.delete()
.addOnSuccessListener {
if (!emitter.isDisposed) {
emitter.onComplete()
}
}
.addOnFailureListener {
if (!emitter.isDisposed) {
emitter.onError(it)
}
}
}
}
I'm adding an action to the repository which would modify a specific document.
override fun lockUnlockAsset(assetId: String): Completable {
Log.d(TAG, "lockUnlockAsset")
return Completable.create { emitter ->
remoteDB.collection(ASSET_COLLECTION)
.document(assetId)
.get()
.addOnSuccessListener {
Log.d(TAG, "Unlocking")
val remoteAsset = mapDocumentToRemoteAsset(it)
it.reference.update(getUnlockLocation())
if (!emitter.isDisposed) {
emitter.onComplete()
}
}
.addOnFailureListener {
Log.d(TAG, "Could not find asset to unlock")
if (!emitter.isDisposed) {
emitter.onError(it)
}
}
}
}
The execution reaches Log.d(TAG, "lockUnlockAsset") but never gets to Log.d(TAG, "Unlocking"). If I place a break point at that second logging command it is the usual red dot in the beginning, but when the call comes into the function the icon changes to a grey "don't enter" icon and when I hover over it Android Studio tells me that "No executable found at ...". So something is definitely wrong there.
I'm new to Kotlin and RxJava2. How can I get this to work?
Update: to answer Pavel's question: these functions are called from the ViewModel layer:
fun deleteAsset(assetId: String) {
repository.deleteAsset(assetId)
.subscribeOn(Schedulers.io())
.subscribe(
{},
{
it.printStackTrace()
})
.addTo(disposable)
}
fun addAsset(assetTitle: String) {
repository.addAsset(Asset("${System.currentTimeMillis()}", assetTitle))
.subscribeOn(Schedulers.io())
.subscribe(
{},
{
it.printStackTrace()
})
.addTo(disposable)
}
fun lockUnlockAsset(assetId: String) {
repository.lockUnlockAsset(assetId)
}
I was experimenting with combinations of .subscribeOn(Schedulers.io()).observe at the repository level. Maybe it's the .addTo(disposable) which got it working, I'm not sure what I was missing. Now it's working, I wait for Pavel for his answer.
I experimented with combinations of .subscribeOn(...) and observeOn(..) + .observe(...) at the data repository level, but I should have just followed the pattern in the view model (view model calls the functions of the data repository): it's a chained subscribeOn + subscribe + addTo(disposable):
fun lockUnlockAsset(assetId: String) {
repository.lockUnlockAsset(assetId)
.subscribeOn(Schedulers.io())
.subscribe(
{},
{
it.printStackTrace()
})
.addTo(disposable)
}
Thanks for Pavel for pointing this out.

Can't catch meteor error thrown in Method from Event

I have a method and it throws an error so I can catch it in my event and display it to the user, like this:
Meteor.methods({
addPlayer: function(nickname) {
if (nickname == "") {
throw new Meteor.Error('empty-nickname', 'You must choose a nickname');
} else {
Player.insert({
nickname: nickname,
});
}
},
})
and in my event
'submit form': function (e) {
e.preventDefault();
var nickname = $('input').val();
Meteor.call('addPlayer', nickname, function(error, result) {
if (error) {
console.log(typeof error);
console.log(error);
}
});
}
However, meteor still throws an Exception while simulating the effect of invoking 'addPlayer', and the error variable is not an error object, but a string with the same message as the console log, so I get two errors in my console instead of an error object.
Wrapping method.call in a try/catch does not work.
What am I missing here?
-- Edit
Here is an print screen of the result:
Image link for full resolution: http://i.stack.imgur.com/zABar.png
Throw the error only on the server. Wrap it inside if(!this.isSimulation) {}
Meteor.methods({
addPlayer: function(nickname) {
if (nickname == "") {
if(!this.isSimulation) {
throw new Meteor.Error('empty-nickname', 'You must choose a nickname');
}
} else {
Player.insert({
nickname: nickname,
});
}
},
})

Resources