check if key exists firebase 4 and swift 4? - ios

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)
}
})
}
})

Related

Registering a user with a unique username for further login using the username or email in Firebase

With the help of detailed tutorials, I was able to create a registration form and an email login, including a Google login.
I ran into a number of issues where I needed a few things, firstly I can't find any relevant information on the web regarding creating unique usernames so that I can log in via them other than email.
The second problem: I need to create the simplest user page, where all the necessary information about the user would be located, such as: avatar, email and username, as well as the function to change the avatar and password.
At the moment my database looks like this:
enter image description here
LoginViewController:
#IBAction func loginTapped(_ sender: Any) {
// Create cleaned version of the text fields
let email = usernameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let password = passwordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
// Sign in in the user
Auth.auth().signIn(withEmail: email, password: password) { (result, error) in
if error != nil {
// Couldn't sign in
self.errorLabel.text = error!.localizedDescription
self.errorLabel.alpha = 1
}
else {
let homeViewController =
self.storyboard?.instantiateViewController(identifier: Constants.Storyboard.homeViewController) as?
UITabBarController
self.view.window?.rootViewController = homeViewController
self.view.window?.makeKeyAndVisible()
UserDefaults.standard.set("email", forKey: "email")
}
}
}
SignUpViewController:
#IBAction func signUpTapped(_ sender: Any) {
guard let password = passwordTextField.text, !password.isEmpty,
let confirm = confirmPasswordTextField.text, !confirm.isEmpty else {
showError(message: "Password field is empty.")
return
}
guard password == confirm else {
showError(message: "Passwords do not match.")
return
}
// Validate the fields
let error = validateFields()
if error != nil {
// There's somthing wrong with the field, show the error message
showError(message: error!)
}
else {
// Create cleaned versions of the data
let username = usernameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let email = emailTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let password = passwordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
//let confirmpassword = confirmPasswordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
// Create the user
Auth.auth().createUser(withEmail: email, password: password) { (result, err) in
// Check for errors
if err != nil {
// There was an error creating the user
self.showError(message: "Error creating user.")
}
else {
// User was created successfully, now store username and other data needed
let db = Firestore.firestore()
db.collection("users").addDocument(data: ["username":username, "uid": result!.user.uid ]) {
(error) in
if error != nil {
// Show error message
self.showError(message: "Error saving user data.")
}
}
// Transition to the home screen
self.transitionToHome()
}
}
}
}
func showError(message:String) {
errorLabel.text = message
errorLabel.alpha = 1
}
func transitionToHome() {
let homeViewController =
storyboard?.instantiateViewController(identifier: Constants.Storyboard.homeViewController) as?
UITabBarController
view.window?.rootViewController = homeViewController
view.window?.makeKeyAndVisible()
}
If I missed any important information, I will be glad if you ask and I will provide everything to you!
Thank you very much in advance for your time!

Firebase -How to check if a phone number exists inside phone auth before creating a new user

Using email when logging in a new user or creating a new user there are 2 different method signatures. When creating a new user if the email already exists an error will be returned or logging a user in if the email doesn't exist an error will be returned:
// create account
Auth.auth().createUser(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: { (authDataResult, error)
if let error = error {
// if this email address already exists an error will be returned
return
}
})
// login
Auth.auth().signIn(withEmail: emailTextField.text!, password: self.passwordTextField.text!, completion: { (authDataResult, error) in
if let error = error {
// if this email address isn't inside the system then an error will be returned
return
}
})
But when using a user's phone number to log them is or create a new account I have to use the same method signature for both situations.
func loginExistingUserOrCreateNewOne(phoneNumber: String, verificationCode: String) {
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate: nil) { (verificationID, error) in
if let error = error { return }
guard let verificationId = verificationID else { return }
let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationId, verificationCode: verificationCode)
Auth.auth().signIn(with: credential, completion: { (authDataResult, error) in
guard let authUser = authDataResult else { return }
let checkUsersRef = Database.database().reference().child("users").child(authUser.user.uid)
checkExistingUsersRef.observeSingleEvent(of: .value, with: { (snapshot) in
if !snapshot.exists() {
// this is a new user, now add them to the users ref
let newUserDict = ["signupDate": Date().timeIntervalSince1970]
checkUsersRef.updateChildValues(newUserDict, withCompletionBlock: { (error, ref) in
if let error = error {
// because there is an error this ref was never updated so now I have to sign this user out and they have to start over agin
do {
try Auth.auth().signOut()
} catch let err as NSError {
// alert user there is a major problem
}
return
}
// if no error let them go to HomeVC
})
return
}
// this is a previous user fetch dict data and let them proceed to HomeVC
guard let previousUserDict = snapshot.value as? [String: Any] else { return }
// get newUserDict values and let them go to HomeVC
})
})
}
}
If a user already has an account I need to fetch some data from the users ref and then I let them proceed to HomeVC. If the user has never signed up before then I have to add them to the users ref and then let them proceed. It's a 2 step process.
The problem is these extra steps seems unnecessary. For example using email sign or login an error is returned so there is no need to create and check inside another ref to see if that email already exists.
Outside of using the process in my above code is there any other way that I can determine if a phone number exists before creating a new account or if it doesn't exist when logging in?
You will need to use the admin sdk to lookup a user by phone number:
admin.auth().getUserByPhoneNumber(phoneNumber)
.then(function(userRecord) {
// User exists.
})
.catch(function(error) {
if (error.code === 'auth/user-not-found') {
// User not found.
}
});
You can use a Cloud Function to host an HTTP endpoint. Looking up a user by phone number is only possible via authenticated APIs running server side (using the Firebase Admin SDKs).

Not finding usernames already in the Firebase Database with Swift

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)
})
}
})
}
}

Firebase reading returns null

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)
}

Firebase create user with email, password, display name and photo url

According to Firebase site, I am using this code to create a new user:
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {});
How can I add display name and photo url to Auth when creating the new user?
This link shows the supported user data returned from an identity provider in Auth.
You can update your profile with FIRUserProfileChangeRequest class .. check this Doc.
let user = FIRAuth.auth()?.currentUser
if let user = user {
let changeRequest = user.profileChangeRequest()
changeRequest.displayName = "Jane Q. User"
changeRequest.photoURL =
NSURL(string: "https://example.com/jane-q-user/profile.jpg")
changeRequest.commitChangesWithCompletion { error in
if let error = error {
// An error happened.
} else {
// Profile updated.
}
}
}
I think this should solve it for you, let me know if you need anything else. or have any further questions on this matter.
func handleSignUp() {
guard let userName = userNameTF.text else { return }
guard let email = emailTF.text else { return }
guard let password = passwordTF.text else { return }
guard let image = profileImage.image else { return }
continueButton.setBackgroundImage(#imageLiteral(resourceName: "inactiveButtonBG"), for: .normal)
activityIndicator.startAnimating()
Auth.auth().createUser(withEmail: email, password: password) { user, error in
if error == nil && user != nil {
print("User created!")
self.uploadProfileImage(image: image) { url in
if url != nil {
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = userName
changeRequest?.photoURL = url
changeRequest?.commitChanges { error in
if error == nil {
self.saveProfile(username: userName, profileImageURL: url!) { success in
if success {
print("Success upload of profile image")
self.dismiss(animated: true, completion: nil)
}
}
self.dismiss(animated: true, completion: nil)
} else {
guard let message = error?.localizedDescription else { return }
self.userAlert(message: message)
}
}
} else {
self.userAlert(message: "Unable to load profile image to Firebase Storage.")
}
}
self.dismiss(animated: true, completion: nil)
} else {
guard let message = error?.localizedDescription else { return }
self.userAlert(message: message)
}
}
}
To change/add the display name:
user!.createProfileChangeRequest().displayName = "Your name"
To change/add photoURL
user!.createProfileChangeRequest().photoURL = URL(string: "image url")
Simply you can solve your problem as follow.
1) Create a user using following statement.
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {});
2) success of above statement Please authenticate this user as follow.
self.rootRef.authUser(email, password)
// USER_ID = Here you get user_ID
3) Success of above function set user name and profile picture to user as follow.
usersRef.updateChildValues(dict, withCompletionBlock:
-Here userRef contain your userDetails/USER_ID
Might be work for you.
i have code but work for older firebase version so not work for you otherwise i had share with you.
I think you mean adding display name and photo url to Firebase Database after Auth. This is pretty much what I do all on same registration.
if let email = emailField.text where email != "", let pwd = passwordField.text where pwd != ""{
FIRAuth.auth()?.createUserWithEmail(email, password: pwd, completion: { (user, error) in
if error != nil {
print("DEVELOPER: Unable to authenticate with Firebase using email")
}else {
print("DEVELOPER: Successfully authenticated with Firebase using email")
if let user = user {
let userData = ["provider": user.providerID, "userName": "\(user.displayName)", "profileImg": "\(user.photoURL)"]
self.completeMySignIn(user.uid, userData: userData)
}
}
})
} else {
// Email and Password where not filled in
}
}
Now adding your profile image and users username in DB here
func completeMySignIn(id: String, userData: Dictionary<String, String>){
{YourFirebaseUserURL}.updateChildValues(userData)
}
You can use the Firebase Admin SDK in Firebase Function exactly for your purpose, i.e. to fill up other user properties as the user is created:
const admin = require("firebase-admin");
// Put this code block in your Firebase Function:
admin.auth().createUser({
email: email,
emailVerified: false,
password: password,
displayName: `${fname} ${lname}`,
disabled: false
})
But creating user with Firebase Admin SDK may give you problem in sending email verification because the promise does not return the User object that has the sendEmailVerification() method. You may eventually need to use the Firebase client API (as shown in your own code) to create the user and update the user profile before sending the email verification:
var user = firebase.auth().currentUser;
user.updateProfile({
displayName: "Jane Q. User",
photoURL: "https://example.com/jane-q-user/profile.jpg"
}).then(function() {
// Update successful.
}).catch(function(error) {
// An error happened.
});
It make sense to update the displayName before sending email verification so that the Firebase email template will greet the new user with proper name rather than just Hello (sounds like a spam) when the displayName is not set.

Resources