Firebase not always getting all children from snapshot - swift

I'm developing an app on Firebase. When I manually reload the database by calling configureDatabase() for example in my viewDidLoad everything works fine. However, when the system refreshes itself some of the children are missing in the data snapshot. Perhaps I have to configure something to get all children?
Code
func configureDatabase() {
ref = FIRDatabase.database().reference()
// Listen for new messages in the Firebase database
_refHandle = self.ref.child(username).observeEventType(.ChildAdded, withBlock: { (snapshot) -> Void in
print(snapshot)
self.messages.append(snapshot)
self.tbl.reloadData()
})
}
When configureDatabase() is called
Snap (Group1) {
members = "[\"\Ud83d\Udc9c\": \"user1\", \"\Ud83c\Udf40\": \"user2\"]";
myScore = 0;
owner = user2; }
When it refreshes by itself
Snap (Group1) {
myScore = 0; }

Found the problem. I was using the .ChildAdded trigger but populating the child with children sequentially instead of all at once, so the method was called only the for the first addition, not the others.

Related

Request result structure firebasedatabase Swift

Looking to retrieve value of custom class from a snap in swift like i do in java , i use Firebasedecoder .
Works fine but i need the following structure
{
username = uiii;
email = test#rom.com
..}
If i make ordered requests like .queryOrdered(ByCHild:email).queryEqual("uiii"), i get the resquest with a previous node :
{
"hjhj"= {
username = uiii;
email = test#rom.com
..} }
Looking for a way to either remove the uneccessary values or to have the correct snap structure.
When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
To get to the individual node(s) in the result, you need to loop over snapshot.children, as shown in the Firebase documentation on listening for value events on a list of children.
Also see:
previous questions about looping over children
Get the data from all children in firebase using swift
Firebase queryOrderedByChild() method not giving sorted data for an alternative if you want to only receive a single child node and only once
In short, if you have extra data at the same level and that makes decodeFirebase crash, you still can use it:
let value = snapshot.value
let modifiedValue:NSMutableDictionary = (value as AnyObject).mutableCopy() as! MutableDictionary
You then can remove elements by key: modifiedValue.removeObject(forKey: test)
and then apply decode.
custom class USER with all values in the pictures
import Foundation
import SwiftUI
import Firebase
import CodableFirebase
//knowing the userid , clean beautiful result with Firebasedecoder
func cleanResultWithCodebableFirebase(){
ref.child("3oleg").observeSingleEvent(of: .value, with: { snapshot in
guard let value = snapshot.value else { return }
do {
let user = try FirebaseDecoder().decode(User.self, from: value)
print(user.getUser_id())
} catch let error {
print(error)
}
})
}
not knowing userID dirty result
func customwithdirtylists(){
let query = ref.queryOrdered(byChild: Strings.field_username).queryEqual(toValue: "uiiii")
query.observeSingleEvent(
of: .value, with: { (snapshot) -> Void in
for child in snapshot.children {
let childSnapshot = snapshot.childSnapshot(forPath: (child as AnyObject).key)
for grandchild in childSnapshot.children{
let grandchildSnapshot = childSnapshot.childSnapshot(forPath: (grandchild as AnyObject).key)
//possible from here to get the key and values of each element of the custom class
}
}
})
}
This is the code i use in both cases, direct request or when ordered . No list visible when direct with the help of firebase decode .Ugly way to rebuild custom class thru looping . I m sure there are more elegant ways to do it especially when all i need is just remove one value of the direct result to have a clean result

Firebase isn't completing before calling next method

I am making a call to a firebase database with the following:
let myRef1 = Database.database().reference(withPath: "/tests/\ .
(myTest)")
print(myTest)
myRef1.observe(.value, with: {
snapshot in
print(snapshot)
var newItems: [qItem] = []
for item in snapshot.children {
let mItem = qItem(snapshot: item as! DataSnapshot)
newItems.append(mItem)
print(newItems)
}
self.myArray = newItems.shuffled()
print(self.myArray)
})
loadNext()
...
However it never completes the completion handler before it moves on to the next method call, which is dependent on the results from this.
Tried making it a separate method, etc. but nothing seems to work.
You need to add the method loadNext() inside the observe to be able to call it after retrieving the data. When you retrieve data it is happening asynchronously which means the compiler wont wait until all the data is retrieved, it will call the method loadNext() first and then after finishing retrieving the data it will execute print(newItems), so you need to do the following:
myRef1.observe(.value, with: {
snapshot in
print(snapshot)
var newItems: [qItem] = []
for item in snapshot.children {
let mItem = qItem(snapshot: item as! DataSnapshot)
newItems.append(mItem)
print(newItems)
loadNext()
}
self.myArray = newItems.shuffled()
print(self.myArray)
})
Firebase data is handled asynchronously. Doug Stevenson wrote a great blog about why that is and what it means for developers. I also wrote a blog where I show how to use closures in your functions to handle asynchronous data. While you definitely can call loadNext() from inside the observe closure, you may find that you end up with a long sequence of closures inside the same function. I go into this a little more in the blog post. In your specific case, you could do something like this:
func getArray(completion: #escaping ([qtItem]) -> Void) {
let myRef1 = Database.database().reference(withPath: "/tests/\ .
(myTest)")
print(myTest)
myRef1.observe(.value, with: {
snapshot in
print(snapshot)
var newItems: [qItem] = []
for item in snapshot.children {
let mItem = qItem(snapshot: item as! DataSnapshot)
newItems.append(mItem)
print(newItems)
}
self.myArray = newItems.shuffled()
print(self.myArray)
completion(self.myArray)
})
}
Then when you call getArray(), you can handle data from within the closure:
getArray() { newItems in
// do something with newItems, like pass to `loadNext()` if needed
self.loadNext()
}

How do I retrieve only the autoids since I last called a function from firebase using swift.

My firebase database is structured as:
events
autoid
event name: ""
event date: ""
autoid
event name: ""
event date: ""
I currently have a function that returns all of the autoids from the events node then writes them to an array so I can use them in another snapshot.
The first time the function runs, it works as expected. But if I leave the view and come back it crashes. Which I think is because it's trying to append the array again, duplicating the values.
Here's my function
func getEvents() {
self.dispatchGroup.enter()
Database.database().reference().child("Events").observe(DataEventType.value, with: { (snapshot) in
if let dictionary = snapshot.children.allObjects as? [DataSnapshot] {
// self.dispatchGroup.enter()
for child in dictionary {
let eventid = child.key
self.eventsArray.append(eventid)
// print(eventid)
// print(self.eventsArray)
}
self.dispatchGroup.leave()
print(self.eventsArray)
}
})
}
Wondering how I can retrieve the existing autoids and any new ones that have been added when I return to the view. I tried .childadded but it returns event name, event date etc and I need the autoid.
I'm new to firebase and swift so any tips or recommendations are welcomed!
If you want to first handle the initial data and then get notified of only the new data, you're typically looking for the .childAdded event.
Database.database().reference().child("Events").observe(DataEventType.childAdded, with: { (snapshot) in
let eventid = snapshot.key
print(eventid)
self.eventsArray.append(eventid)
self.dispatchGroup.leave()
print(self.eventsArray)
}
When you first run this code, the .childAdded event fires for each existing child node. And after that, it fires whenever a new child is added. Similarly, you can listen for .childChanged and .childRemoved events to handle those.

Unable to get database items using Firebase observe listener for the first time

I have imported a simple json file and published on the realtime database called ("Quiz1"). I have then tried accessing it using the following code
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
print(ref.child("Quiz1").description())
ref.childByAutoId().observeSingleEvent(of:.value, with: {snapshot in
for child in snapshot.children {
print(child)
}
}, withCancel: {error in print("WHAT")})
From what I can see in the documentation, this should trigger the first time the app is launched. But the code just skips over this part, I don't get any error. I have also changed the read/write permissions to make sure there are no authentication steps required at this point.
In order to get the reference to a table in your Firebase database you need to call:
let ref = FIRDatabase.database().reference().child("your table name")
Then from here you can make a listener and get the values from the table
ref(of: .value, with: { snapshot in
First if you want to reference to the node "Quiz1", you need to access it using the child method something like this:
ref.child("Quiz1") and saving that reference in a variable (refQuiz), for then you can make queries (observeSingleEvent) with that reference (refQuiz).
let ref = FIRDatabase.database().reference()
let refQuiz = ref.child("Quiz1")
refQuiz.observeSingleEvent(of: .value) { (snapshot: FIRDataSnapshot) in
for child in snapshot.children {
print(child)
}
}
childByAutoId() creates new child under the ref and newly created node don't have any child nodes.
There's no child nodes to observe on your code.
If you are going to get nodes under "Quiz" please try below:
refHandle = ref.child("Quiz").observe(FIRDataEventType.value, with: { (snapshot) in
let quizDict = snapshot.value as? [String : AnyObject] ?? [:]
// process quizDict
})

Firebase query not being fired

I set breakpoints all throughout this query to Firebase, and all that's happening is the breakpoint for the following line gets hit (which is the very first line), and then no others.
_CHAT_REF.observeEventType(.Value, withBlock: { snapshot in
Any idea why this would be happening? Even if there is no data, the breakpoints inside the query block should be still be getting hit, but they aren't. What I've done: I've uninstalled and reinstalled Firebase at least 10 times, using both CocoaPods and not CocoaPods, following directions to the T. I'm not getting any kind of compile error, and I'm running FIRApp.configure() in my app delegate.
Full Code (breakpoints on each line, none called only _CHAT_REF.observe line):
private var _CHAT_REF = FIRDatabase.database().reference().child("chats")
_CHAT_REF.observeEventType(.Value, withBlock: { snapshot in
self.individualMessages = []
if snapshot.hasChildren() {
// Found chats
for snap in snapshot.children {
let theChat = Chat(snapshot: snap as! FIRDataSnapshot)
// The current user belongs to this chat, so add it to individual messages.
if theChat.sender_id == GlobalEnv.currentUser.id || theChat.receiver_id == GlobalEnv.currentUser.id {
self.individualMessages.append(theChat)
}
}
} else {
// No Children
print("No children found.")
}
self.tvContacts.reloadData()
})
DB Structure:
DB Structure on Firebase
I ran into a similar problem. It turned out that I wasn't able to read/write the database from behind my organization's proxy server. I built to a device using open wifi, and it worked.
Try this and let me know if it makes a difference. It's very simplified but the variable assignments are handled differently.
assume you are in a ViewController class...
class ViewController: UIViewController {
var ref: FIRDatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
}
func clickAction() { //just tied to an action in the UI
let chatsRef = ref.child("chats")
chatsRef.observeEventType(.Value, withBlock: { snapshot in
print("Hello, World")
})
}
}