Swift Firebase snapshot.exists() keeps Value True - ios

snapshot.exists() is retaining its value of true when someValue == nil.
I dont know how to reset the snapshot.exists() value after the initial query. How do i set the database call to only be triggered when i call upon it? like removing the observer the proper way?
let ref = Database.database().reference()
ref.child("matches").child("available").queryOrdered(byChild: "opponent_1").queryEqual(toValue: potential_oponnent).observeSingleEvent(of: .value, with: { (snapshot) -> Void in
if (snapshot.exists()) && someValue != nil {
}else {
ref.removeAllObservers()
}
Do i need to use ref.removeObserver(withHandle: ) or ref.removeAllObservers()?

Related

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.

What to do when snapshot.exists() returns false?

I have a ref that exists and I use observeEventType to query the data. But the ref might not have data in it due to the user deleting it. I test it using snapshot.exists(). In the situation below snapshot.exists() will return false/no. Since it's false I want to do something else but the code never runs
How do I so something else when snapshot.exists() returns false/no?
//there is no data at levelTwo so there's nothing to observe
let levelTwoRef = dbRef.child("players").child("uid").child("levelTwo")
levelTwoRef.observeEventType(.ChildAdded, withBlock: {
(snapshot) in
if snapshot.exists(){
if let dict = snapshot.value as? [String:AnyObject]{
let power = dict["power"] as? String
let score = dict["score"] as? String
}
//this will never run because the data has been deleted
} else{
do something else as an alternative //why isn't this running??
}
})
Firebase has a .hasChild function that you can run on a child to see if it exists:
func hasChild(_ childPathString: String) -> Bool
It takes a child as a String argument and returns True or False depending on wether it exists or not.
The way to check if a child exists is to first set a path to the child before the child your looking for. In the situation from the question the child to look for is "levelTwo" and the child before that is uid:
//there is no data at levelTwo so there's nothing to observe
let levelTwoRef = dbRef.child("players").child("uid").child("levelTwo")
Assuming you know the uid ref definitely exists set a constant for the uid ref instead of the levelTwo ref
// uid path definitely exists
let uidRef = dbRef.child("players").child("uid")
Run .value on the uid ref and inside the callback check to see if the levelTwo ref exists:
uidRef?.observeSingleEvent(of: .value, with: {
(snapshot) in
if snapshot.hasChild("levelTwo"){
// true -levelTwo ref Does exist so do something
}else{
// false -levelTwo ref DOESN'T exist so do something else
}
}
You are running that inside observeEventType with the type .ChildAdded, which would return the snapshot of each new path that is created. If you just need to retrieve the value once you should use observeSingleEventOfType (link), with .Value as the event type.

How to cancel a firebase observe single event database call

In my iOS app, a user is able to add friends by searching for there unique username.
The user types the username in a textField and I have a textFieldDidChange notification which is fired every time the text changes.
Within that method I then call the Firebase method below to check if the username exists.
func searchFor(_ username: String) {
guard let uid = FIRAuth.auth()?.currentUser?.uid else {
return
}
let lowercaseUsername = username.lowercased()
let ref = FIRDatabase.database().reference()
ref.child(FirebaseDatabaseBranchNames.usernames.rawValue).child(lowercaseUsername).observeSingleEvent(of: .value, with: { [unowned self](snapshot) in
if snapshot.exists() {
if let usernameUid = snapshot.value as? String {
self.isUserAlreadyAFriend(ref, uid: uid, usernameUid: usernameUid)
}
} else {
// username doesn't exist
}
}, withCancel: nil)
}
How can I cancel this method, before performing it again?
When you attach a listener/observer, Firebase returns a handle for that observer. You can subsequently remove the listener/observer by calling ref.removeObserverWithHandle().
So assuming you want at most one observer, you can keep the reference and observer handle in a member field of you class and then use this code in the searchFor method:
if (self.searchHandle != nil) {
self.searchRef.removeObserverWithHandle(searchHandle)
}
self.searchRef = ref.child(FirebaseDatabaseBranchNames.usernames.rawValue).child(lowercaseUsername)
self.searchHandle = self.searchRef.observeSingleEvent(of: .value, with: { [unowned self](snapshot) in
if snapshot.exists() {
if let usernameUid = snapshot.value as? String {
self.isUserAlreadyAFriend(ref, uid: uid, usernameUid: usernameUid)
}
} else {
// username doesn't exist
}
}, withCancel: nil)
Be aware that you won't be saving data transfer with this though, as the most likely result is that the database client simply drops the data that it gets back from the server.
Better you add the "removeAllObservers()" after the observe single event block. It is working for me.
let ref = Database.database().reference().ref.child(XXXX).child(YYYYY)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
}else{
}
}) { (error) in
print(error.localizedDescription)
}
ref.removeAllObservers()

Remove observer after value change in Firebase

I have global observer in ViewController and need some different observers inside it for specific values like one below. Is it possible to remove observer after value change once?
var ref = Firebase(url: "https://<FIREBASE-APP>.firebaseio.com/")
let handle = ref.observeEventType(.Value, withBlock: { snapshot in
//Here VALUE Changes to NEW_VALUE
if snapshot.value as! String == NEW_VALUE {
//IS IT POSSIBLE TO REMOVE HANDLE HERE????
...something here
}
})
//NOT HERE
...ref.removeObserverWithHandle(handle)
This is one of the cases where you need to take an extra step in Swift, since it doesn't realize that you can safely access handle inside the block.
One way of working around this is:
let ref = Firebase(url: "https://yours.firebaseio.com/")
var handle: UInt = 0
handle = ref.observeEventType(.Value, withBlock: { snapshot in
print(snapshot)
if snapshot.exists() && snapshot.value as! String == "42" {
print("The value is now 42")
ref.removeObserverWithHandle(handle)
}
})
By explicitly initializing the handle variable, we remove the error from the Swift compiler. But given that the handle will have been set before our block is invoked, we can safely call ref.removeObserverWithHandle(handle) inside the block.

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