Now, I'm so confused about firebase with observe using childAdded data event type. The reason why I use childAdded to observe my firebase because I want to make my list page dynamic whether firebase has new data insert.
And my question is how to know observe is stop calling when reach the queryLimit? Because I have a indicator and I want to turn it off when reach the queryLimit.
My firebase structure below:
root {
talkID1(id by auto create) {
attribute1 ...
attribute2 ...
attribute3 ...
time
}
talkID2(id by auto create){
...
time
}
... // many talk ID which auto create by firebase
}
As what I know, if using childAdd to observe, data will one child by child to passing data in call back. So If I have N datas in firebase and I think it will calls N<=5 times, right?
My completion handler below:
func receiveIfExist(completion: @escaping (_ data: (My data type) -> Void) {
let requestWithQuery = Database.database.reference().child("root").queryOrdered(byChild: "time")
requestWithQuery.queryLimited(toLast: 5).observe(.childAdded, with: { (snapshot) in
guard let value = snapshot.value as? [String: Any] else { return }
self.getSingleTalkInfo(key: snapshot.key, value: value, completion: { (data) in
completion(data)
})
})
}
I'm calling receiveIfExist this function in viewDidLoad().
override func viewDidLoad() {
super.viewDidLoad()
self.myIndicator.startAnimating() // start running indicator
self.receiveIfExist { (data) in
self.handleTalk(with: data) // do something with request data
self.myIndicator.stopAnimating() // WEIRD!!!! Please look comments below
/*
I think it can not be added here because this completion will call N<=5 times just what I said before.
I think it should detect what my queryLimit data is and check the request data is this queryLimit data or not.
If yes then stop indicator animating, if not then keep waiting the queryLimit reach.
*/
}
}
How can I detect the observe is reach queryLimit?
If I can detect then I can turn off my indicator when it reach.
Thank you!
queryLimited(toLast: 5)
means (in much simpler words) please get the last 5 values (order is decided by the previous part of your query)
1. Now, since you are sorting the data by times , the values with the last 5 times will be retrieved, therefore your observer will be triggered 5 times
2. Note that if you have less than 5 records say 2 records, then it will be triggered only twice because maximum limit is 5, not minimum limit
3. Another point is that say if a new child is added and when you sort the items again according to the time and the new child is one of the last 5 items then this observer will be triggered again.
so to get the query limit you can make some changes in your code like this:
func receiveIfExist(completion: @escaping (data: YourType, limitCount: Int) -> Void) {
let requestWithQuery = Database.database.reference().child("root").queryOrdered(byChild: "time")
requestWithQuery.queryLimited(toLast: 5).observe(.childAdded, with: { (snapshot) in
guard let value = snapshot.value as? [String: Any] else { return }
self.getSingleTalkInfo(key: snapshot.key, value: value, completion: { (data) in
self.index = self.index + 1
completion(data, self.index)
})
})
}
Then using the above function as follows:
var index = 0
override func viewDidLoad() {
super.viewDidLoad()
self.myIndicator.startAnimating() // start running indicator
self.receiveIfExist { (data, limitCount) in
self.handleTalk(with: data) // do something with request data
if limitCount == 5 {
self.myIndicator.stopAnimating()
}
}
}
UPDATED:
Since very good point raised by Kevin, that above solution will fail if we have say only two records and index will never be equal to 5 and myIndicator
will not stop animating,
One solution that comes to my mind is this:
First we get the children count using observeSingleEvent
:
func getChildrenCount(completion: @escaping (_ childrenCount: Int) -> Void){
Database.database.reference().child("root").observeSingleEvent(of:.value with: { (snapshot) in
completion(snapshot.children.count)
}
}
then we apply the query to get last 5 items:
func receiveIfExist(completion: @escaping (data: YourType, limitCount: Int) -> Void) {
let requestWithQuery = Database.database.reference().child("root").queryOrdered(byChild: "time")
requestWithQuery.queryLimited(toLast: queryLimit).observe(.childAdded, with: { (snapshot) in
guard let value = snapshot.value as? [String: Any] else { return }
self.getSingleTalkInfo(key: snapshot.key, value: value, completion: { (data) in
self.index = self.index + 1
completion(data, self.index)
})
})
}
then use this count in your code as follows:
var index = 0
var childrenCount = 0
var queryLimit = 5
override func viewDidLoad() {
super.viewDidLoad()
self.myIndicator.startAnimating() // start running indicator
self.getChildrenCount {(snapChildrenCount) in
self.childrenCount = snapChildrenCount
self.receiveIfExist { (data, limitCount) in
self.handleTalk(with: data) // do something with request data
if (self.childrenCount < self.queryLimit && limitCount == self.childrenCount) || limitCount == self.queryLimit {
DispatchQueue.main.async {
self.myIndicator.stopAnimating()
}
}
}
}
}