How do I retrieve only the autoids since I last called a function from firebase using swift. - 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.

Related

Firebase removeObserver is not working in Swift 5

I am using Firebase's Realtime database in my app. I am fetching data from the database and do some change and after that I am removing the observer which is not working fine.
I have some data in Realtime Database like this:
I am using firebase's observe(.value) function to get this value and after that I am updating an entry and then I am removing the observer. This is my code:
func updatePoints() {
let firebaseId = UserDefaults.standard.value(forKey: "firebaseId") as? String ?? ""
let reference = self.database.child("Points").child(firebaseId)
var handler : UInt = 0
handler = reference.observe(.value, with: { snapshot in
guard let userPoints = snapshot.value as? [String : Any] else {
print("no points data found")
return
}
let pointsLeft = userPoints["points_left"] as? Int ?? 0
reference.child("points_left").setValue(pointsLeft - 1)
reference.removeObserver(withHandle: handler)
})
}
The problem now is, this observer runs twice. For example, if "points_left" : 10, then after this function the points left will have 8 value but it should have 9 instead. It is running twice and I am not understanding why is it doing so as I am using removeObserver. Can someone help me with this?
The reason to the above unexpected behaviour is the setValue function you called to update the points is triggering another .value event in the database. Then it triggers the observer again. Therefore, by the time you remove the observer, it has already triggered twice. This leads to decrease of points by 2 instead of 1.
So if u interchange the last two lines, by the time you call the setValue function observer is removed. So it will not get triggered for the second time.

Swift 5, how to execute code after fetching all childadded from Firebase

How can I execute some code after fetching all childadded from Firebase in Swift 5?
I've tried using DispatchGroup and observe .value, but none of them worked efficiently.
let dispatchGroup = DispachGroup()
ref.child("path").observe(.childAdded, with: { (snapshot) in
self.dispatchGroup.enter()
//store snapshot data into an object
self.dispatchGroup.leave()
})
dispatchGroup.notify(queue: .main) {
//code to execute after all children are fetched
}
In this case, the code will be executed before fetching the data.
How can I execute code when only the callback block reaches the last child?
One option is to leverage that Firebase .value functions are called after .childAdded functions.
What this means is that .childAdded will iterate over all childNodes and then after the last childNode is read, any .value functions will be called.
Suppose we want to iterate over all users in a users node, print their name and after the last user name is printed, output a message that all users were read in.
Starting with a simple structure
users
uid_0
name: "Jim"
uid_1
name: "Spock"
uid_2
name: "Bones"
and then the code that reads the users in, one at a time, prints their name and then outputs to console when all names have been read
var initialRead = true
func readTheUsers() {
let usersRef = self.ref.child("users")
usersRef.observe(.childAdded, with: { snapshot in
let userName = snapshot.childSnapshot(forPath: "name").value as? String ?? "no name"
print(userName)
if self.initialRead == false {
print("a new user was added")
}
})
usersRef.observeSingleEvent(of: .value, with: { snapshot in
print("--inital load has completed and the last user was read--")
self.initialRead = false
})
}
and the output
Jim
Spock
Bones
--inital load has completed and the last user was read--
Note this will leave an observer on the users node so if a new user is added it will print their name as well.
Note Note: self.ref points to my root firebase reference.
When you're listening to .childAdded, there is no way when your code is getting called for the last child. So if you must treat the last child different, listen for .value and loop over the children as shown here.

Firebase Observe called after following command

I am trying to load a username form a Firebase which is than supposed to be set in an Object. But the Firebase Observe Command is getting called after the name already gets set. What is the problem and how can I fix it?
let ref = Database.database().reference().child("Users").child(currentMessage.senderId).child("name")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
// This is supposed to be called first
self.username = snapshot.value as! String
print(self.username)
})
// This somehow gets called first
let nameModel = NameModel(name: self.username, uid: *some UID*)
decoratedItems.append(DecoratedChatItem(chatItem: nameModel, decorationAttributes: nil))
Firebase loads data from its database asynchronously. This means that the code executes in a different order from what you may expect. The easiest way to see this is with some log statements:
let ref = Database.database().reference().child("Users").child(currentMessage.senderId).child("name")
print("Before attaching observer")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
print("In completion handler")
})
print("After attaching observer")
Unlike what you may expect, this code prints:
Before attaching observer
After attaching observer
In completion handler
This happens because loading data from Firebase (or any other web service) may take some time. If the code would wait, it would be keeping your user from interacting with your application. So instead, Firebase loads the data in the background, and lets your code continue. Then when the data is available, it calls your completion handler.
The easiest way to get used to this paradigm is to reframe your problems. Instead of saying "first load the data, then add it to the list", frame your problem as "start loading the data. when the data is loaded, add it to the list".
In code this means that you move any code that needs the data into the completion handler:
let ref = Database.database().reference().child("Users").child(currentMessage.senderId).child("name")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
self.username = snapshot.value as! String
let nameModel = NameModel(name: self.username, uid: *some UID*)
decoratedItems.append(DecoratedChatItem(chatItem: nameModel, decorationAttributes: nil))
})
For some more questions on this topic, see:
Swift: Wait for Firebase to load before return a function
Can Swift return value from an async Void-returning block?
Array items are deleted after exiting 'while' loop?
Asynchronous functions and Firebase with Swift 3

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
})

How can my code know when Firebase has finished retrieving data?

I just want to ask about firebase retrieve data. How can i handle firebase retrieve data finished? I don't see any completion handler.
I want to call some function after this firebase data retrieve finished. How can i handle???
DataService.ds.POST_REF.queryOrderedByChild("created_at").observeEventType(.ChildAdded, withBlock: { snapshot in
if let postDict = snapshot.value as? Dictionary<String, AnyObject> {
let postKey = snapshot.key
let post = Post(postKey: postKey, dictionary: postDict)
self.posts.append(post)
}
})
In Firebase, there isn't really a concept of 'finished' (when listening to 'child added'). It is just a stream of data (imagine someone adds a new record before the initial data is 'finished'). You can use the 'value' event to get an entire object, but that won't give you new records as they're added like 'child added' does.
If, you really need to use child added and get notified when it's probably finished, you can set a timer. I don't know swift, but here's the logic.
Set up your 'child added' event.
Set a timer to call some finishedLoading() function in 500ms.
Each time the 'child added' event is triggered, destroy the timer set in step two and create another one (that is, extend it another 500ms).
When new data stops coming in, the timer will stop being extended and finsihedLoading() will be called 500ms later.
500ms is just a made up number, use whatever suits.
Do one request for SingleEventOfType(.Value). This will give you all info initially in one shot, allowing you to then do whatever function you want to complete once you have that data.
You can create a separate query for childAdded and then do anything there you want to do when a new post has been added
Write your entire block of code in a function which has a completion handler like so:
func aMethod(completion: (Bool) -> ()){
DataService.ds.POST_REF.queryOrderedByChild("created_at").observeEventType(.ChildAdded, withBlock: { snapshot in
if let postDict = snapshot.value as? Dictionary<String, AnyObject> {
let postKey = snapshot.key
let post = Post(postKey: postKey, dictionary: postDict)
self.posts.append(post)
}
completion(true)
})
}
Then call it somewhere like so:
aMethod { success in
guard success == true else {
//Do something if some error occured while retreiving data from firebase
return
}
//Do something if everything went well.
.
.
.