In my app I'm trying to get user information from Firebase database, but it always returns null. Here is my code for reading current user information from database
func workWithDataBase(){
let email = "test#gmail.com"
let password = "test23"
FIRAuth.auth()?.signIn(withEmail: email, password: password, completion: { (user, error) in
if error != nil {
print(error)
return
} else {
print("SIGNED IN\n")
let uid = FIRAuth.auth()?.currentUser?.uid
FIRDatabase.database().reference().child("users").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot)
}) {
(error) in
print(error.localizedDescription)
}
}
})
}
and here is the output and the screenshot of Firebase database
My question is if the fields for email and name are not empty why "observeSingleEvent" method returns null? How can I fix this?
Try This:-
FIRDatabase.database().reference().child("user").child(FIRAuth.auth()!.currentUser!.uid).observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot)
}) {(error) in
print(error.localizedDescription)
}
Related
I am working on the user profile page of my app, where the user can make changes to the user info, like change their username, name... Each user has their own unique username, I have done that with the following code:
// Function to check if the username is taken
static func checkUsernameUnique(newUserName: String, completion: #escaping(Bool) -> Void) {
let ref = Database.database().reference()
ref.child("Users").queryOrdered(byChild: "Username").queryEqual(toValue: newUserName).observeSingleEvent(of: .value, with: {(snapshot: DataSnapshot) in
if snapshot.exists() {
completion(true)
} else {
completion(false)
}
})
}
This checks if there is a user with the same username. Calling this function:
checkUsernameUnique(newUserName: username) { isExist in
if isExist {
print("Username is taken")
} else {
print("Username is not taken")
}
}
The problem with this is that this checks if there is a user using the same username with all the other users in the database, including the current user. How do I exclude the current user?
Code that I tried with the help of one of the answers
static func checkUsernameUnique(newUserName: String, completion: #escaping(Bool) -> Void) {
let ref = Database.database().reference()
if let userID = Auth.auth().currentUser?.uid {
print(userID)
ref.child("Users").queryOrdered(byChild: "Username").queryEqual(toValue: newUserName).observeSingleEvent(of: .value, with: {(snapshot: DataSnapshot) in
if !snapshot.exists() {
completion(false)
} else {
//Here we will check the given user's UID
snapshot.ref.child("UserID").observeSingleEvent(of: .value, with: { (uidSnapshot) in
//This forced cast should never fail
let uid = uidSnapshot.value as! String
print(uid)
//Now we use FirebaseAuth to cross reference the current user's UID with whatever the "Username" node's sibling node "UserID" is
if Auth.auth().currentUser!.uid == uid {
//The returned user is the same as the current user
completion(false)
} else {
//The returned user is not the same as the current user
completion(true)
}
})
}
})
} else {
ref.child("Users").queryOrdered(byChild: "Username").queryEqual(toValue: newUserName).observeSingleEvent(of: .value, with: {(snapshot: DataSnapshot) in
if snapshot.exists() {
completion(true)
} else {
completion(false)
}
})
}
}
Try this. All we have to do is get the userID of whatever is returned from the query, and check if it equals the current user's uid.
// Function to check if the username is taken
static func checkUsernameUnique(newUserName: String, completion: #escaping(Bool) -> Void) {
let ref = Database.database().reference()
ref.child("Users").queryOrdered(byChild: "Username").queryEqual(toValue: newUserName).observeSingleEvent(of: .value, with: {(snapshot: DataSnapshot) in
guard let json = snapshot.value as? [String:[String:Any]] else {
completion(false)
return
}
//Assuming there will never be more than one database entry with this username, this first element (result) can be unwrapped explicitly without crashing
let result = json.first!.value
let userID = result["UserID"] as! String
if Auth.auth().currentUser!.uid == uid {
completion(false)
} else {
completion(true)
}
})
}
There is no way to conditionally exclude data from a query. You will have to check in your client code if specific data is present in the result, and exclude it from there. Just checking snapshot.exists will not be sufficient in your case - instead, look inside the snapshot to see if it contains the UID of the current user.
I am trying to check my Firebase database to see if there is already a username taken within the database. Unfortunately, when I use a username which is already saved in there, it doesn't let me know. Code below.
#objc func pushedToRegister(_ sender: SignInSignUpButtons) {
checkUserNameAlreadyExist()
Auth.auth().createUser(withEmail: email.text!, password: password.text!, completion: { (user, error) in
if error != nil {
self.errorMessage = (error?.localizedDescription)!
print(error!)
self.showErrorView(forReason: 0)
return
}
else {
print("Registration was successful!")
//Here we should go back to the home screen where the message at the top should say welcome back USER!
if let userID = user?.user.uid {
//Create User Profile
let databaseRef = Database.database().reference()
let usersRef = databaseRef.child("Users").child(userID)
let usernameValue = ["username":self.username.text]
usersRef.updateChildValues(usernameValue, withCompletionBlock: { (err, ref) in
if err != nil {
print(err!.localizedDescription)
self.showErrorView(forReason: 2)
return
}
//Profile created and updated!
self.navigationController?.popViewController(animated: true)
})
}
}
})
}
func checkUserNameAlreadyExist() {
let ref = Database.database().reference()
var usernameTaken = false
ref.child("Users").queryOrdered(byChild: "username").queryEqual(toValue: username.text!).observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists(){
usernameTaken = true
print("username taken")
}else{
usernameTaken = false
print("username available")
}
}) { error in
print(error.localizedDescription)
}
if usernameTaken == false{
//do stuff with unique username
}
}
Unfortunately, every time I type in the same username, it lets me create a new profile everytime. It does not alert me to the fact that the username is already taken.
Firebase functions are asynchronous, so they do not block the rest of the code from running. If you want your code to wait for a function to complete, one option is use closures. You can check out my blog post on closures to see some examples.
Also, the code shown doesn't actually do anything to prevent the rest of the function from running. You need to write some sort of condition to handle that. For example, you could use a boolean in your completion handler, like this:
func checkUserNameAlreadyExist(completion: #escaping (Bool) -> Void) {
let ref = Database.database().reference()
ref.child("Users").queryOrdered(byChild: "username").queryEqual(toValue: username.text!).observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists() {
usernameTaken = true
print("username taken")
completion(true)
} else {
usernameTaken = false
print("username available")
completion(false)
}
}) { error in
print(error.localizedDescription)
completion(true)
}
}
Then in pushToRegister, you check if the boolean is true before proceeding.
#objc func pushedToRegister(_ sender: SignInSignUpButtons) {
checkUserNameAlreadyExist() { isTaken in
if (isTaken == true) {
// show some message to the user
return
}
Auth.auth().createUser(withEmail: email.text!, password: password.text!, completion: { (user, error) in
if let error = error {
self.errorMessage = error.localizedDescription
print(error)
self.showErrorView(forReason: 0)
return
}
print("Registration was successful!")
//Here we should go back to the home screen where the message at the top should say welcome back USER!
if let userID = user?.user.uid {
//Create User Profile
let databaseRef = Database.database().reference()
let usersRef = databaseRef.child("Users").child(userID)
let usernameValue = ["username":self.username.text]
usersRef.updateChildValues(usernameValue, withCompletionBlock: { (err, ref) in
if let err = err {
print(err.localizedDescription)
self.showErrorView(forReason: 2)
return
}
//Profile created and updated!
self.navigationController?.popViewController(animated: true)
})
}
})
}
}
I can not solve the problem: In Firestore there is a collection of user, in which there are several documents with fields (email: "String", name: "String", password: "String"). In the textField, the email and password are entered, it is necessary to write a function that selects a specific user from Firestore from the entered data. The function entranceLogin ():
func entranceLogin() {
guard let email = emailTextField.text, let password = passwordTextField.text else{
print("Form is not valid")
return
}
let values1 = [email]
let values2 = [password]
var db = Firestore.firestore()
db.collection("users").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
db = document.value(forKey: "email = '\(values1)' && password = '\(values2)'") as! Firestore
print("\(db) => \(document.data())")
}
// self.dismiss(animated: true, completion: nil)
}
}
}
It is very unsafe to store a password in the database the way you are doing it. Firebase already provides you with a login function
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
//.....
}
Then in your database instead of saving the password, just save the User UID for each of the users. Then you can fetch the user by using:
guard let uid = Auth.auth().currentUser.uid else { return }
Firestore.firestore().collection("users").whereField("userID", isEqualTo: uid).getDocuments() { snapshot, error in
// Do user fetching here
}
How do I check if a key exists in Firebase? I have seen this link here, but it's for Firebase 3, and it doesn't work for my situation. So for my case, I want to check to see if a username exists, and if it does, then don't register a user, but if it doesn't then register. I have something along the lines of this:
let usersDB = Database.database().reference().child("Users")
var taken = false
usersDB.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.hasChild(username) {
taken = true
self.errorLabel.text = "Username already taken."
}
})
if !taken {
// Email registration
Auth.auth().createUser(withEmail: email, password: password, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
self.errorLabel.text = error!.localizedDescription
} else {
// Allows for username log in
usersDB.child(username).setValue(["email" : user?.email])
self.performSegue(withIdentifier: "goToGroups", sender: self)
}
})
}
The observeSingleEvent is what the previous similar post's solution was, but it only runs after I add a child on this line usersDB.child(username).setValue(["email" : user?.email]), it never runs before. Is there any other way to do this?
This looks like an issue with handling asynchronous calls. if !taken is very likely going to be checked before data is returned from observeSingleEvent because it's asynchronous, so the rest of the code will continue to run. One option is to move if !taken into the closure, like this:
let usersDB = Database.database().reference().child("Users/\(username)")
var taken = false
usersDB.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
taken = true
self.errorLabel.text = "Username already taken."
}
if !taken {
// Email registration
Auth.auth().createUser(withEmail: email, password: password, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
self.errorLabel.text = error!.localizedDescription
} else {
// Allows for username log in
usersDB.child(username).setValue(["email" : user?.email])
self.performSegue(withIdentifier: "goToGroups", sender: self)
}
})
}
})
When i select register.. the data is sent to Firebase authentication but does not store in the database? Can anyone tell me where im going wrong?
func handleRegister(){
// Validation
guard let email = emailTextField.text, let password = PassTextField.text, let name = nameTextField.text
else{
print("Please provide an Email and Password")
return
}
FIRAuth.auth()?.createUser(withEmail: email, password: password, completion: { (user: FIRUser?, error) in
if error != nil {
print(error!)
return
}
// Successfully authenticated user
// Saving Data to the Database
let ref = FIRDatabase.database().reference(fromURL: "https://chat-47e5b.firebaseio.com/")
let values = ["name": name, "email": email]
ref.updateChildValues(values, withCompletionBlock: { (err,ref)
in
if err != nil {
print(err!)
return
}
print("Saved user successfully into Firebase")
})
})
}
You are not doing it right, you should first get a reference to the db:
self.ref = FIRDatabase.database().reference()
Then:
let values = ["name": name, "email": email]
self.ref.child("users").child(user.uid).setValue(values)
As a side note, convert this:
if error != nil {
print(error!)
return
}
To this:
guard let error = error else {
print(error)
return
}