Firebase queries not calling block when there are no results - ios

I'm using Firebase and I want to query to see if something exists. It gets called when a value is found, but the block does not get called when nothing is found. Is this expected behaviour?
ref.queryOrderedByKey().queryEqualToValue(channelName).observeSingleEventOfType(.ChildAdded, withBlock: { snapshot in
print("found channel: \(snapshot.key)")
}, withCancelBlock: { error in
print(error.description)
})
Have I done something wrong? Thanks

To check for no data (snapshot == NULL) it's done this way
let refToCheck = myRootRef.childByAppendingPath(channelNameString)
refToCheck.observeEventType(.Value, withBlock: { snapshot in
if snapshot.value is NSNull {
print("snapshot was NULL")
} else {
print(snapshot)
}
})
Queries are pretty heavy by comparison to the .observeEventType and since you already know the specific path you are checking for it will perform much better.
Edit: You can also use
if snapshot.exists() { ... }
Firebase queries are best used when you want to retrieve child nodes that contain specific values or a range of values.
Edit for Firebase 3.x and Swift 3.x
let refToCheck = myRootRef.child(channelNameString)
refToCheck.observe(.value, with: { snapshot in
if snapshot.exists() {
print("found the node")
} else {
print("node doesn't exist")
}
})
Note 1) the logic was changed a bit as we now leverage .exists to test for existence of a snapshot.
Note 2) this code leaves an observer active in Firebase so if the node is created at a later time, it will fire.
If you want to check for a node and don't want to leave an observer watching for that node do this:
let refToCheck = myRootRef.child(channelNameString)
refToCheck.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists() {
print("found the node")
} else {
print("node doesn't exist")
}
})

Related

Firebase -How to handle pagination if the key set to paginate from has been deleted

I use the pagination method at the bottom of this question which works fine. Once the startKey is initialized with a key from the db that's the point at which the next pagination will occur from and the next set of posts (children) will get appended to the datasource.
I realized that if that key got deleted by the initial user who posted it, then once I try to paginate from that key since it doesn't exist the children that would get appended if it was there wouldn't get appended because they wouldn't be accessible (they're accessible based on that key).
The only thing I could think of was to first check if the key exists() and if it doesn't just start everything over from the beginning:
if !snapshot.exists() {
self?.startKey = nil
self?.datasource.removeAll()
self?.collectionView.reloadData()
self?.handlePagination()
return
}
It works fine but it's not the most fluid user experience because I'd rather just pull the posts prior to the deleted key (I have no prior reference to them).
A possibility is to just keep an array of all the previous keys and just loop through them but there's always a minute chance that those keys can get deleted by the users who posted them also.
Any ideas of how to get around this?
var startKey: String?
func handlePagination() {
if startKey == nil {
Database...Ref.child("posts")
.queryOrderedByKey()
.queryLimited(toLast: 7)
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
guard let children = snapshot.children.allObjects.first as? DataSnapshot else { return}
for child in snapshot.children.allObjects as! [DataSnapshot] {
// append child to datasource
}
self?.startKey = children.key
})
} else {
Database...Ref.child("posts")
.queryOrderedByKey()
.queryEnding(atValue: startKey!)
.queryLimited(toLast: 8)
.observeSingleEvent(of: .value, with: { [weak self](snapshot) in
if !snapshot.exists() {
self?.startKey = nil
self?.datasource.removeAll()
self?.collectionView.reloadData()
self?.handlePagination()
return
}
guard let children = snapshot.children.allObjects.first as? DataSnapshot else { return}
for child in snapshot.children.allObjects as! [DataSnapshot] {
// insert child in datasource at startIndex
}
self?.startKey = children.key
})
}
}
there's always a minute chance that those keys can get deleted by the users who posted them also
You're saying that you could keep the keys of the current page, and just loop through until you find an item that was not deleted. In the case that all have been deleted, you'd reach the end of the list of keys, and should create a new query that doesn't have a startAt(). This will give you the first X items, which is the correct behavior in that case I think.
In general though: dealing with realtime and pagination is really hard, which is the main reason the paging adapters in FirebaseUI don't do realtime updates.

Firebase query not returning any data

My datamodel looks as follows :
allcomments
|__$comment_id_5
|__post_id: <post_id_5>
uid
|
|__activity
|__comments
|__$random_activity_id
|__post_id : <post_id_5> //ref to post_id_5 in allcomments
|__comment_id : <comment_id_5> // ref to comment_id_5 in allcomments
My Goal: To check if the user with uid has commented on the post or not. If that person has, then I he can proceed further else he'll be shown something else on the screen. On trying the following query, I am able to only get the callback when a snapshot exists and not otherwise.
FBDataservice.ds.child("allcomments").queryOrdered(byChild: "post_id").queryEqual(toValue: "post_id_5").observeSingleEvent(of: .ChildAdded) { (snapshot) in
if let data = snapshot.value as? DataDict {
let comment = Comment(comId: snapshot.key , comData: data)
self.checkUserHasResponded(completion: { (hasResponded) in
if !hasResponded {
// Never returns it there is nothng
print("You gotta respond first")
} else {
//this part does work
print("Welcome to seeing everything")
}
})
}
}
func checkUserHasResponded(completion: #escaping (Bool) -> ()) {
FBDataservice.ds.REF_USERS.child(uid).child("activity/comments").queryOrdered(byChild: "post_id").queryEqual(toValue: "post_id_5").observeSingleEvent(of: .value) { (snapshot) in
snapshot.exists() ? completion(true) : completion(false)
}
}
I even tried tweaking the architecture this way and query it differently, still nothing work and the program behaves in the same exact way as incase of above.
uid
|
|__activity
|__comments
|__post_id_5 : comment_id_5
and ran this query:
func checkUserHasResponded(completion: #escaping (Bool) -> ()) {
FBDataservice.ds.REF_USERS.child(uid).child("activity/comments").observeSingleEvent(of: .value) { (snapshot) in
snapshot.hasChild("post_id_5") ? completion(true) : completion(false)
}
}
I tried changing .childAdded to .value. It gives the same exact result. Tried changing .observeSingleEvent(of:) to .observe() as well. But nothing helps. I am not sure what exactly is wrong. Check plenty of answers here, none helped. What exactly am I over looking. Thanks for the help.
Use .value instead of .childAdded, that way it the closure is called whether or not the snapshot exists, Just a quick test shows it works.
func checkUserHasResponded() {
let uid = "uid_0"
let commentsRef = dbRef.child(uid).child("activity").child("comments")
commentsRef.queryOrdered(byChild: "post_id")
.queryEqual(toValue: "post_5")
.observeSingleEvent(of: .value) { snapshot in
if snapshot.exists() {
print("post exists")
} else {
print("post not found")
}
}
}
If your structure does not contain a post_id child value that exists then the output is
post not found
So this answer applies to the updated question. The code in the closure will not run if the node you're querying for does not exist because the query is using .childAdded
FBDataservice.ds.child("allcomments").queryOrdered(byChild: "post_id")
.queryEqual(toValue: "post_id_5")
.observeSingleEvent(of: .childAdded) { (snapshot) in
If that's changed to .value, it returns and the code in the closure runs if the node exists. Keeping in mind that you'll want to use
snapshot.exists()
with that as it will be nil if it doesn't.

print out firebase child values

I want to look through Firebase and check if a certain value for a child exists and if that value does not exist. I want to print out a statement
databaseRef.child("books").queryOrdered(byChild: "Category").queryEqual(toValue: categorySegue).observe(.childAdded, with: { (snapshot) in
if snapshot.exists() {
print("data found")
}else{
print("no data found")
}
})
When the child value exists, it prints out data found perfectly fine but when it does not exist. It does not print out no data found
That's because you're observing .childAdded, which only fires if there is a child matching your query.
If you want to also detect the situation when no child matches, you'll need to observe the .value event and loop over the results.
databaseRef.child("books").queryOrdered(byChild: "Category").queryEqual(toValue: categorySegue).observe(.childAdded, with: { (snapshot) in
if snapshot.exists() {
print("data found")
for child in snapshot.children {
print(child.key)
}
}else{
print("no data found")
}
})
Also see Searching through child values Firebase / Swift

Firebase get child name by value and remove it

My JSON looks like this:
users
username1: "WWOdh96Yr3Qs4N3GWDVq3OlQFfB2"
username2: "RJ6PztTLmsg222oUygWHtWpVHdg1"
I would like to get "username1" by their key which is (auth.uid)! and if its found i want to delete it, So after delete it will be looks like this:
users
username2: "RJ6PztTLmsg222oUygWHtWpVHdg1"
.
self.ref.child("users").queryOrderedByValue().queryEqual(toValue: user?.uid).observe(.value, with: { snapshot in
if snapshot.exists() {
print("\(snapshot.value)")
snapshot.ref.child(snapshot.value as! String).removeValue() // this deleting every thing which is WRONG!
} else{
print("Not found--")
}
self.ref.removeAllObservers()
})
}
This code deleting everything in users. I just want to delete specific user.
First of all, my approach is if I want to query 1 value at 1 moment, like you are doing, I do a observeSingleEvent (is this the best approach?). But to solve your problem, you forgot to add your "users" path. Below code is more safe to use, since you force unwrap (which is not recommend for this case):
let dataRef = self.ref.child("users")
dataRef.keepSynced(true)
if let usersUID = Auth.auth().currentUser?.uid{
dataRef.child("users").queryOrderedByValue().queryEqual(toValue: usersUID).observeSingleEvent(.value, with: { snapshot in
print(snapshot)
let values = snapshot.value as? NSDictionary
for (key, value) in values{
print(value) // does this print the uid?
print(key) // or this one?
self.ref.child("users/\(key)").removeValueWithCompletionBlock({ (err, ref) in
if err != nil {
print(err)
} else {
print(ref)
print("Removed")
}
})
}
})
}
Try to add this, right after the print(snapshot)

Firebase when query is empty

I perform a query using firebase to check if there are any objects that have a child with a certain value. The problem is if there are no such objects the code doesn't run. So in the code below if there are no objects that have a child with facebookID that is 17 the rest of the code will never run. Is there a way to determine when a query finds 0 results?
func previousUser(completion:(result:Bool)->Void){
var queryFound = false
print("check if user is previous")
print(UserData.globalfacebookID)
let ref = Firebase(url:"https://amber-torch-556.firebaseio.com/Users")
ref.queryOrderedByChild("facebookID").queryEqualToValue(17).observeSingleEventOfType(.ChildAdded, withBlock: { snapshot in
print(snapshot.childrenCount)
print("query has ran")
UserData.globalparseID = snapshot.key
queryFound = true
completion(result:queryFound)
})
Check your ref to make sure it's valid.
and try this
ref.queryOrderedByChild("facebookID").queryEqualToValue(17)
.observeEventType(.Value, withBlock: { snapshot in
if snapshot.value is NSNull {
print("dude, snapshot was null")
} else {
print(snapshot.key)
}
})
Change .ChildAdded to .Value and then check for NSNull
To check whether a query returns 0 results, you can check the snapshot as following:
if snapshot.exists() {
// write relevant logic here
}
else {
// handle the case when a query finds 0 results
}
In your case,
func previousUser(completion:(result:Bool)->Void){
var queryFound = false
print("check if user is previous")
print(UserData.globalfacebookID)
let ref = Firebase(url:"https://amber-torch-556.firebaseio.com/Users")
ref.queryOrderedByChild("facebookID").queryEqualToValue(17).observeSingleEventOfType(.ChildAdded, withBlock: { snapshot in
if snapshot.exists() {
print(snapshot.childrenCount)
print("query has ran")
UserData.globalparseID = snapshot.key
queryFound = true
completion(result:queryFound)
}
else {
// handle the case where query finds zero(0) results
}
})
Hope this helps !

Resources