Swift : Firebase query never goes in call - ios

Im trying to access to my firebase db with Swift
import Foundation
import FirebaseDatabase
import FirebaseAuth
import Firebase
struct FirebaseManager {
private let reference = Database.database().reference()
func fetch (_ child: String, completion: #escaping (DataSnapshot) -> Void) {
print("before reference")
reference
.child(child)
.observeSingleEvent(of: .value) { (snapdata) in
print("inside reference")
guard let response = snapdata.value else { return print("error snapdata") }
print(response)
completion(snapdata)
}
print("after reference")
}
}
To check if code is running correctly, I put some prints and this is what I see in Xcode console:
before reference
after reference
There's neither "inside reference" nor response value nor "error snapdata"
So I've deduced it never goes inside it ! And I checked: my firebase db is not empty and corresponds to child parameter string.
I don't understand where is the problem.
Same problem here : iOS Swift - Firebase query never gets called
EDIT: I also tried this:
.observeSingleEvent(of: .value, with: { (snapshot) in
print("observe single event")
if snapshot.exists() {
if let value = snapshot.value as? String {
print("inside if let", value)
}
} else {
print("else")
// username doesn't exist
}
}, withCancel:{ error in
print("cancel", error)
})
And the ouput in console is the same. Nothing appears
And this is a screenshot in rules:

You might want to use observeSingleEvent(of:with:withCancel:) instead, to see if the query gets cancelled by the database. If it does, the user does not have permission to read the data, and you should read the documentation on security rules.

Related

Firebase observeSingleEvent completion handler

I have a FirebaseManager class and a get data function.
When calling this function, I need to get this data in the place from which it is called.
I am trying to do something like
import Firebase
class FirebaseManager {
var ref = Database.database().reference()
func getData (path: String) -> DataSnapshot {
self.ref.child(path).observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
print(snapshot.value!)
return snapshot // XCODE ERROR - Cannot convert return expression of type 'Void' to return type 'DataSnapshot'
} else {
print("No data by path \(path)")
}
}
}
}
As your title suggests, what you'll need a completion handler. You can pass that in as a parameter to your function. It might look something like this:
func getData(path: String, completion: #escaping (DataSnapshot) -> Void) {
self.ref.child(path).observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
completion(snapshot)
} else {
print("No data by path \(path)")
}
}
}
Then, you can call it like this:
getData(path: "myPath") { snapshot in
//do something with snapshot here
}
//no access to snapshot here
Note that you have access to snapshot inside the closure (the { }), so you can't access snapshot after the closure.
Personally, I'd probably refactor a bit so that you'd be returning the actual data than you want from inside the snapshot (like a String, a Dictionary, etc) rather than returning the DataSnapshot itself, but that's a matter of preference.

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.

Check if user exists with username using Swift and Firebase

I'm currently trying to code a function who pass the user Data when user exists. When the username is in the database, the code is okay, but if there is no username recorded in the database I don't know how to have a return function.
I'm beginner, this is what I did:
func observeUserByUsername(username: String, completion: #escaping (Userm?) -> Void) {
REF_USERS.queryOrdered(byChild: "username_lowercase").queryEqual(toValue: username).observeSingleEvent(of: .childAdded) { (snapshot) in
if let dict = snapshot.value as? [String: Any] {
let user = Userm.transformUser(dict: dict, key: snapshot.key)
completion(user)
} else {
print("no user")
completion(nil)
}
}
}
I would like to have something like this: if there is user with this username -> return nil (for the completion).
Do you know how I could do this?
So if I got it right, you want to just check if a user with the username exists. You can just enter the path to firebase and use the exists() method to check if this subnode exists. I have a similar method, you can maybe change it to fit into your project.
func checkUsernameAvailability(completion: #escaping (_ available:Bool)->()){
guard let lowercasedText = usernameTextField.text?.lowercased() else {completion(false); return}
let ref = Database.database().reference().child("users").child("username").child(lowercasedText)
ref.observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists(){
completion(false)
return
}else{
completion(true)
}
}
}
Be careful, Firebase is not case-sensitive (that's why I always check and also store the lowercased version). If your subnode e.g. is 'UserName' and you search for the name 'username' it will tell you that there is already one with this name.

Retrieving data from Firebase in Swift

I've been looking at the APIs from the Firebase website, however, when I print my snapshot, I get everything from my database.
I'm trying to only print the value I just set, which is my variable: test_person_1.
import UIKit
import Firebase
class Test: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let ref = FIRDatabase.database().reference()
ref.observeEventType(.ChildAdded, withBlock: { (snapshot) in
var test_person_1 = ["full_name": "Test 1", "date_of_birth": "June 1, 2000"]
var Test_person_2 = ["full_name": "Grace Hopper", "date_of_birth": "July 1, 2000"]
let usersRef = ref.child("New child")
usersRef.setValue([test_person_1])
print(snapshot) //
})
Json tree:
By the time you print the snapshot, the value hasn't been set on the server yet.
If you want to print when the data been sent to the server, add a so-called completion listener to your setValue() call:
usersRef.setValue([test_person_1]) { (error, ref) in
print(error, ref)
}
If there is no error, you know that the value on the server is what you just wrote. But if you want to read back the value from the server, you'll need to attach a listener for it:
usersRef.setValue([test_person_1]) { (error, ref) in
ref.observeSingleEventOfType(.Value) { (snapshot) in
print ("new value \(snapshot.value)")
}
}

Check if user exist with firebase 3.0 + swift

I have a app that after the user use Firebase auth it store the data on the Firebase database. Before storing the data, I want to check if the username the user give already exist in the database. So if it not exist I could give the user this unique username(like every user have a unique username). So I have a textField where the user enter his username, and then press Next. Then the app should check if the username exist or not, and tell the user if he need to change it.
So the code I used to check if the username exist:
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("Users").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if snapshot.hasChild(self.usernameTextField.text!){
print("user exist")
}else{
print("user doesn't exist")
}
})
So every time the next button is pressed, this code is called. The problem with this is that the result always remain the same as the first search (even after the textField value change).
For example, if I search Jose, and Jose exist in my database so is going to print "user exist". But when I change the textField to name that don't exist, it still show "user exist".
I figured out I need to change the .Value to FIRDataEventType.Value
if (usernameTextField.text?.isEmpty == false){
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("Users").observeSingleEventOfType(FIRDataEventType.Value, withBlock: { (snapshot) in
if snapshot.hasChild(self.usernameTextField.text!){
print("true rooms exist")
}else{
print("false room doesn't exist")
}
})
struct ModelUser {
var id: String
var name: String
init(data: DataSnapshot) {
// do init stuff
}
}
func isUserRegistered(with id: String, completion: #escaping (_ exists: Bool, _ user: ModelUser?) -> ()) {
DatabaseReference.users.child(id).observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
// user is already in our database
completion(true, ModelUser(data: snapshot))
} else {
// not in database
completion(false, nil)
}
}
}
This worked for me in a similar situation as yours. You can also go the Rx way like this.
enum CustomError: Error {
case userNotRegistered
var localizedDescription: String {
switch self {
case .userNotRegistered:
return "Dude is not registered..."
}
}
}
func isUserRegistered(with id: String) -> Observable<(ModelUser)> {
let reference = DatabaseReference.users.child(id)
return Observable<ModelUser>.create({ observer -> Disposable in
let listener = reference.observe(.value, with: { snapshot in
if snapshot.exists() {
observer.onNext(ModelUser(data: snapshot))
observer.onCompleted()
} else {
observer.onError(CustomError.userNotRegistered)
}
})
return Disposables.create {
reference.removeObserver(withHandle: listener)
}
})
}
The key in both cases is using the .exists() method of the snapshot.

Resources