I am new to firebase and I'm trying to learn how to create users. My problem is that when i use the createUserWithEmail completion block via a button, for some reason the unique identifier, the uid, is not generated. This prevents me from storing the associated username and password under the uid in the JSON tree. My code is as follows (I have defined databaseRef in a separate swift file as a global constant using "let databaseRef = FIRDatabase.database().reference()")
#IBAction func createAccount(sender: AnyObject) {
var username = usernameField.text
var email = emailField.text
var password = passwordField.text
if username != "" && email != "" && password != "" {
FIRAuth.auth()?.createUserWithEmail(email!, password: password!, completion: { (user, error) in
databaseRef.child("users/(user.uid)/username").setValue(username)
databaseRef.child("users/(user.uid)/password").setValue(password)
databaseRef.child("users/(user.uid)/uid").setValue(user?.uid)
})
} else {
print("please complete all fields")
}
}
I know the uid is not generated for two reasons. Firstly, when i run the above code and enter in values for each of my texts fields, the app crashes. Secondly, if i delete the code that deals with setting the values and replace it with print(user.uid), a value of nil is returned. What am i missing? I can understand that the uid is a very important part of creating a user.
***Here is the solution I came up with
#IBAction func createAccount(sender: AnyObject) {
var username = usernameField.text
var email = emailField.text
var password = passwordField.text
if username != "" && email != "" && password != "" {
FIRAuth.auth()?.createUserWithEmail(email!, password: password!, completion: { (user, error) in
if let user = FIRAuth.auth()?.currentUser {
databaseRef.child("users/\(user.uid)/username").setValue(username)
databaseRef.child("users/\(user.uid)/password").setValue(password)
databaseRef.child("users/\(user.uid)/email").setValue(email)
print(user.uid)
} else {
print("no user")
}
})
} else {
print("please complete all fields")
}
Swift 4, 2018 solution for anyone who's interested:
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
// guard against errors and optionals
guard error == nil else { return }
guard let user = user else { return }
let userObject = [
"uid": user.uid,
"username": username,
"password": password, // I don't recommend storing passwords like this by the way
"email": email
] as [String:Any]
databaseRef.child("users").child(user.uid).setValue(userObject)
}
Swift 5.1, 2022 solution for anyone who's interested:
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
// guard against errors and optionals
guard error == nil else { return }
guard let user = user else { return }
let userObj = ["uid": user.user.uid, "username": username, "email": email] as [String:Any]
databaseRef.child("users/\(user.user.uid))".setValue(userObj)
Related
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!
I am trying to authenticate users. But the createNewUser() firebase method is returning nil in the closure for user?.user.uid and thus I cannot add the data under the correct node.
Bellow is my user creation method:
func createNewUser(email: String, password: String) {
Auth.auth().createUser(withEmail: email, password: password) { user, error in
if error == nil && user != nil { //user create works
print(user, "<-- User Created (user)")
} else { //user not create
print("Error creating user: \(error!.localizedDescription)")
}
//database integration
let ref = Database.database().reference()
let usersRef = ref.child("users2")
let uid = user?.user.uid
print(email, "<-- email")
print(uid, "<-- this is uid")
let newUserRef = usersRef.child(uid!)//This fails because UID is nil
newUserRef.setValue(["email": self.emailTextField.text!, "password": self.passwordTextField.text!, "fullName": self.fullNameTextField.text!, "username": self.usernameTextField.text!])
print(email, "<--- this is emaiL??")
} //end of create user
}
How do I fix this?
When I look at the docs for Firebase's "createUserWithEmail:password:completion:" method, it looks like you get an authResult, error back from Auth.auth().createUser(withEmail:....
And from that, to get the user, you'd do:
func createNewUser(email: String, password: String) {
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
guard let authResult = authResult, error == nil else {
print("Error creating user: \(error!.localizedDescription)")
return // an error!
}
if let user = authResult.user {
if let uid = user.uid {
print("uid is \(uid)")
}
if let email = user.email {
print("email is \(email)")
}
}
...
...
...
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
}
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.
I am building an app that uses Firebase's email and password login feature. I am having the user register with a username, email, and password. I am struggling with how to stop the user from being created if the username is not unique. I have been reading other questions (specifically Firebase-android-make-username-unique and how-prevent-username-from-duplicate-signup-infirebase) but I have still not gotten it to fully work.
I followed the instructions in the first link above and set up my data structure as:
app : {
users: {
"some-user-uid": {
email: "test#test.com"
username: "myname"
}
},
usernames: {
"myname": "some-user-uid"
}
}
and my security rules as:
"users": {
"$uid": {
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.provider === 'password'",
"username": {
".validate": "
!root.child('usernames').child(newData.val()).exists() ||
root.child('usernames').child(newData.val()).val() == $uid"
}
}
}
With this setup, if I try to create a new user with a username that already exists, it stops the user from being added to my data structure. When the below code is called, it prints "User Data could not be saved" if the username is a duplicate.
func createNewAccount(uid: String, user: Dictionary<String, String>) {
USER_REF.childByAppendingPath(uid).setValue(user, withCompletionBlock: {
(error:NSError?, ref:Firebase!) in
if (error != nil) {
print("User Data could not be saved.")
} else {
print("User Data saved successfully!")
}
})
}
func addUsernameToUsernamePath (userData: Dictionary<String, String>) {
USERNAME_REF.updateChildValues(userData)
}
Here is where I am stuck. My create account method below doesn't call the above two methods until createUser and authUser are called (Which I need to get the uid). My problem is the user still gets created as a registered user and my security rules just keep the users information from being added to my data structure. I need to figure out how to stop the user from being created if there is a duplicate username.
#IBAction func createAccount() {
let username = usernameField.text
let email = emailField.text
let password = passwordField.text
if username != "" && email != "" && password != "" {
// Set Email and Password for the New User.
DataService.dataService.BASE_REF.createUser(email, password: password, withValueCompletionBlock: { error, result in
if error != nil {
print("Error: \(error)")
if let errorCode = FAuthenticationError(rawValue: error.code) {
switch (errorCode) {
case .EmailTaken:
self.signupErrorAlert("Email In Use", message: "An account has already been created for this email address.")
default:
self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Please try again or check your internet connection.")
}
}
} else {
DataService.dataService.BASE_REF.authUser(email, password: password, withCompletionBlock: {
err, authData in
let user = ["provider": authData.provider!, "email": email!, "username": username!]
let userData = [username!: authData.uid!]
DataService.dataService.createNewAccount(authData.uid, user: user)
DataService.dataService.addUsernameToUsernamePath(userData)
})
EDIT
Here is my updated createAccount method that solved my issue.
#IBAction func createAccount() {
let username = usernameField.text
let email = emailField.text
let password = passwordField.text
if username != "" && email != "" && password != "" {
DataService.dataService.USERNAME_REF.observeEventType(.Value, withBlock: { snapshot in
var usernamesMatched = false
if snapshot.value is NSNull {
usernamesMatched = false
} else {
let usernameDictionary = snapshot.value
let usernameArray = Array(usernameDictionary.allKeys as! [String])
for storedUserName in usernameArray {
if storedUserName == self.usernameField.text! {
usernamesMatched = true
self.signupErrorAlert("Username Already Taken", message: "Please try a different username")
}
}
}
if !usernamesMatched {
// Set Email and Password for the New User.
DataService.dataService.BASE_REF.createUser(email, password: password, withValueCompletionBlock: { error, result in
if error != nil {
print("Error: \(error)")
if let errorCode = FAuthenticationError(rawValue: error.code) {
switch (errorCode) {
case .EmailTaken:
self.signupErrorAlert("Email In Use", message: "An account has already been created for this email address.")
default:
self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Please try again or check your internet connection.")
}
}
} else {
// Create and Login the New User with authUser
DataService.dataService.BASE_REF.authUser(email, password: password, withCompletionBlock: {
err, authData in
let user = ["provider": authData.provider!, "email": email!, "username": username!]
let userData = [username!: authData.uid!]
// Seal the deal in DataService.swift.
DataService.dataService.createNewAccount(authData.uid, user: user)
DataService.dataService.addUsernameToUsernamePath(userData)
})
You could allow sign up without a valid username, and have a separate "set username" screen that you show in the event of a partial registration.
Define your security rules to check for a non-null username before allowing writes to other parts of your database.
I was able to get it working by updating createAccount() to the code below.
#IBAction func createAccount() {
let username = usernameField.text
let email = emailField.text
let password = passwordField.text
if username != "" && email != "" && password != "" {
// Checks for internet connection before saving the meetup. Returns if there is no internet connection.
let reachability = try! Reachability.reachabilityForInternetConnection()
if reachability.currentReachabilityStatus == .NotReachable {
let internetAlert = UIAlertController(title: "No Internet Connection", message: "Please make sure your device is connected to the internet.", preferredStyle: .Alert)
let internetAlertAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
internetAlert.addAction(internetAlertAction)
presentViewController(internetAlert, animated: true, completion: nil)
return
}
DataService.dataService.USERNAME_REF.observeEventType(.Value, withBlock: { snapshot in
var usernamesMatched = false
if snapshot.value is NSNull {
usernamesMatched = false
} else {
let usernameDictionary = snapshot.value
let usernameArray = Array(usernameDictionary.allKeys as! [String])
for storedUserName in usernameArray {
if storedUserName == self.usernameField.text! {
usernamesMatched = true
self.signupErrorAlert("Username Already Taken", message: "Please try a different username")
}
}
}
if !usernamesMatched {
// Set Email and Password for the New User.
DataService.dataService.BASE_REF.createUser(email, password: password, withValueCompletionBlock: { error, result in
if error != nil {
print("Error: \(error)")
if let errorCode = FAuthenticationError(rawValue: error.code) {
switch (errorCode) {
case .EmailTaken:
self.signupErrorAlert("Email In Use", message: "An account has already been created for this email address.")
default:
self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Please try again or check your internet connection.")
}
}
} else {
// Create and Login the New User with authUser
DataService.dataService.BASE_REF.authUser(email, password: password, withCompletionBlock: {
err, authData in
let user = ["provider": authData.provider!, "email": email!, "username": username!]
let userData = [username!: authData.uid!]
// Seal the deal in DataService.swift.
DataService.dataService.createNewAccount(authData.uid, user: user)
DataService.dataService.addUsernameToUsernamePath(userData)
})