Checking username before signing up Firebase - ios

After using snapshot.exists(), username will always return as available upon signup. How can I alter my code to properly check whether or not a username has already been taken?
Here is a snippet of the json under "users" in the database:
{
"UserID#" : {
"credentials" : {
"name" : "testuser",
},
}
}
My code currently looks like:
let usersDB = Database.database().reference()
var taken = false
usersDB.child("users").child("credentials").queryOrdered(byChild:"name").queryEqual(toValue: username.lowercased()).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
taken = true
print("Username is not available.")
usernameAlert()
} else {
print("User is available")
}
No matter if the username is already taken on the database, the snapshot will say it does not exist.
Solutions I've tried with no success:
check if the username exist in Firebase
Query users in Firebase to check if username exists during sign up process
Firebase querying for unique Username swift
Check if user exists with username using Swift and Firebase
Swift & Firebase | Checking if a user exists with a username

You're now checking under each child node of /users/credentials for a property named name. What you instead want is to check each child node of /users for a property at credentials/name, which you do with:
usersDB.child("users").queryOrdered(byChild:"credentials/name").queryEqual(toValue: ...

Related

why do i get two user ids when creating a user in firebase using swift?

when i sign up a user in my ios app it generates a user id and adds that to the data base with the users name and surname and username but it is generating a user id and another random number/id and i dont know what that is for:
i dont know what the Roy... is and dont know where its coming from.
so when i try and access the users uid to access the information such as the name and surname i keep getting the following error because its using the wc7... number and not the other one:
Listener at /Users/wc7VyejKlDNfcAhFu3AkIX9Y9on1/Username failed: permission_denied
this is my code that i use to try and access the users information:
func fetchUsersData() {
guard let currentUser = Auth.auth().currentUser?.uid else { return }
print("Current user id is \(currentUser)")
Database.database().reference().child("Users").child(currentUser).child(USER_NAME).observeSingleEvent(of: .value) { (snapshot) in
guard let username = snapshot.value as? String else {return}
self.navigationItem.title = username
}
}
how do i fix this?
That code is for the Firebase Real Time Database and the screen shot is for Cloud Firestore. They are totally different and unrelated.
If you want to read the data shown in your screenshot you need to use the Cloud Firestore documentation.
The documentID 'Roy...' happens when you don't assign a document an ID... it will generate one automatically.
Also, change your Firestore structure to use the users uid as the documentID. So it would look like this
users //the collection
uid_0 //the document with documentID = a users uid
first_name: "Hank"
last_name: "Jones"
user_name: "Hankster"
uid_1
first_name: "Leroy"
last_name: "Jenkins"
user_name: "Leeeerrroooyyy"
and then the code to read a specific user name based on a uid is this
func readUserName() {
let users = self.db.collection("users")
let thisUser = users.document(the users uid)
thisUser.getDocument(completion: { documentSnapshot, error in
if let error = error {
print(error.localizedDescription)
return
}
guard let snap = documentSnapshot else { return }
let docId = snap.documentID
let userName = snap.get("user_name") as? String ?? "No Name"
print(userName)
})
}
Also note that you will need to be authenticated to Firestore to read any data or adjust the Security Rules to allow anyone to read. That's not generally a good idea but when you're just getting started it's ok.

How to add a username to Firebase database upon registration?

Upon logging in I want my app to make a new field in the Firebase database with the child named after the username.
if let email = emailField.text, let pass = passwordField.text {
// Check if it's sign in or register
if isLogin {
// Sign in the user with Firebase
Auth.auth().signIn(withEmail: email, password: pass, completion: { (user, error) in
// Check that user isn't nil
if let u = user {
// User is found, go to home screen
self.ref?.child(email).childByAutoId().setValue("1")
self.performSegue(withIdentifier: "goHome", sender: self)
print("yes")
}
When I try to do this, it gives me an error called SIGABART which I believe is associated with not having segues connected properly.
Yet if I delete this line:
self.ref?.child(email).childByAutoId().setValue("1")
or change the email field to a random string like "test", it works fine and appears in Firebase.
If I remember correctly, you can't use symbol # in nodes names. It's first problem. You can do it another, I think better, way:
You need to create user to ref like:
/users/uid from created FIRUser.
You can do it with next steps:
For example, your registration page will have 3 UITextFields: userEmail, userLogin and userPassword.
// *1* Create user
FIRAuth.auth()!.createUser(withEmail: userEmail.text!,
password: userPassword.text!)
{ user, error in
if error == nil {
// *2* Then log him in
FIRAuth.auth()!.signIn(withEmail: self.userEmail.text!,
password: self.userPassword.text!)
{ result in
// *3* Create new user in database, not in FIRAuth
let uid = (FIRAuth.auth()?.currentUser?.uid)!
let ref = FIRDatabase.database().reference(withPath: "someStartPart/users").child(uid)
ref.setValue(["uid": uid, "email": userEmail.text!, "login": userLogin.text!, "creationDate": String(describing: Date())])
self.performSegue(withIdentifier: "fromRegistrationToMainPage", sender: self)
}
} else {
print("\(String(describing: error?.localizedDescription))")
}
}
Like this. Hope it helps.
Firebase Auth will not allow you to store such a value. You will need to save this into Firebase Database or another service. e.g. In the Firebase Database:
userRef.setValue("username123")
You should save the username value in the database. Something like this:
FIRdatabase.database().reference().child("yourchildname").updateChild(u)
(Im on my phone, so the call might not be EXACTLY like that, but should be very close)
Cheers.
Swift 4:
//***** for realTime database
let ref = Database.database().reference(fromURL: "https://add your project URL")
let userRef = ref.child("users").child(user.user.uid)
let values = ["Email": email, "UserName": userName] //userName is the name of textField

Read and Write rules In a proper way Firebase

How can I test if a username exists in the proper way? This code is not working. Do I have to update my rules in some sort of way? Im not too familiar with firebase rules.
Rules:
{
"rules": {
"Users":{
".read": "true",
".write": "true"
}
}
}
Current JSON Tree Setup:
+Users
+iq5oM0XlgAa9K78EuEFdZ0ZVTsm1
Email: "test#gmail.com"
Name: "jj"
Password: "***"
Username: "jjj"
CODE:
let username : String = self.usernameField.text!
//Checking username existence
FIRDatabase.database().reference().child("Users/Username").child(username).observeSingleEvent(of: .value, with: {(usernameSnap) in
if usernameSnap.exists(){
//This username already exists
print("The user name already exists")
}else{
//Yippee!.. This can be my username
print("user good to go")
print(username)
}
})
You need to make a separate node in your database called usernames. Here you save the username as the key, and the userID as the value. Now you can check the usernames node to see if a username already exists.

Facebook login using Firebase - Swift iOS

I'm implementing login with Facebook using Firebase, I have this code which searches my database after a successful facebook authentication for the email if exists in database and logs in the app if found, I want to direct the user to registration view controller if not found but its not working since this method is asynchronous. I appreciate if anyone can help. Here is my code :
func getFacebookUserInfo() {
if(FBSDKAccessToken.current() != nil){
let graphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields" : "id,name,gender,email,education"])
let connection = FBSDKGraphRequestConnection()
connection.add(graphRequest, completionHandler: { (connection, result, error) -> Void in
let data = result as! [String : AnyObject]
let email = data["email"] as? String
let emailRef = FIRDatabase.database().reference().child("usernameEmailLink")
emailRef.queryOrderedByValue().observe(.childAdded, with: { snapshot in
if let snapshotValue = snapshot.value as? [String: AnyObject] {
for (key, value) in snapshotValue {
if(value as? String == email){
self.stringMode = snapshotValue["mode"]! as! String
self.username = key
self.parseUserInfoFromJSON()
return
}
}
}
})
})
connection.start()
}
}
Thank you.
The registration/existence of the user in Firebase should probably be determined before the graphRequest code in the question.
Most importantly, (and this is critical), email addresses are dynamic so they should not be used to verify if a user exists. i.e. user with email address of 'leroy#gmail.com' updates his email to 'leroy.j#gmail.com'. If emails are used to verify registration, it can totally break if that email changes.
Please use Firebase uid's for that purpose as they are static and unique.
Since we only have a small snippet of code, we don't know the exact sequence being used. This answer is pseudo-code to outline a possible sequence.
We assume that by 'registered' it means that the user has gone through some kind of app registration sequence and the user has been created (and now exists/is registered) in Firebase.
In general there would be a login button and a delegate method to handle the actual login action.
The user enters their login and taps the login button
func loginButton(loginButton: FBSDKLoginButton!,
didCompleteWithResult result: FBSDKLoginManagerLoginResult!,
error: NSError?) {
Firebase can then get the credentials for that user (see Firebase doc quote below)
let credential = FIRFacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
At that point, sign in the user and check to see if they are registered (exist) in the Firebase user node.
FIRAuth.auth()?.signIn(with: credential) { (user, error) in
if let error = error { //failed due to an error
return
}
let uid = user.uid //the firebase uid
let thisUserRef = userRef.child(uid) //a reference to the user node
//check to see if the user exists in firebase (i.e. is Registered)
thisUserRef.observeSingleEvent(of: .value, with: { (snapshot) in
//if snapshot exists
//then the user is already 'registered' in the user node
// so continue the app with a registered user
//if not, then need to have the user go through a registration sequence and
// then create the user (make them registered) in the user node
doRegisterUser(user)
})
func doRegisterUser(user: FIRUser) {
//get what you need from the user to register them
// and write it to the users node. This could be from additional
// questions or from their Facebook graph, as in the code in the
// question
//for this example, we'll just write their email address
let email = user.email
let dict = ["email": email]
//create a child node in the users node with a parent of uid
// and a child of email: their email
thisUserRef.setValue(node)
//next time the user logs in via FB authentication, their user node
// will be found as they are now a 'registered' user
}
From the Firebase docs
After a user signs in for the first time, a new user account is
created and linked to the credentials—that is, the user name and
password, or auth provider information—the user signed in with. This
new account is stored as part of your Firebase project, and can be
used to identify a user across every app in your project, regardless
of how the user signs in.
As I mentioned, this is very pseudo code but offers a possible sequence for a solution.

How do I validate if a username exists before sign up in Firebase and Swift 3?

I've incorporated a sign-up flow of five view controllers as opposed to one central VC for Firebase sign up.
Normally there would be the problem of data being lost before sign up, but I'm pushing all the entered values via segue programmatically to the final confirmation page and sending the data to Firebase for auth eventually.
My problem lies therein - I want to check if the username exists before signing up. This is imperative for the UX of my app and I can't just do this all one VC.
Here's the code I have for the UsernameViewController:
let rootRef = FIRDatabase.database().reference()
rootRef.queryOrdered(byChild: "users").queryEqual(toValue: self.chooseUsernameTextField.text!)
.observe(FIRDataEventType.value, with: { (snapshot: FIRDataSnapshot!) in
if snapshot.hasChildren() == false {
print("not found")
} else {
print("usr found")
}
});
Here's the code I have for the Firebase Database Security Rules
{
"rules": {
".read": false,
".write": false,
"users": {
".validate": "data.child('username').val() === true",
".read": true
}
}
}
and finally, a screenshot of my Data tree (My Data tree won't let me nest any new users or create a user node without a value for some reason):
Picture of Firebase Data Tree: App Name/Users
I have a nagging suspicion that my data rules and tree are configured properly to match the code, but I'm stuck in a XY problem of not knowing what to do for Firebase security to get my code of username checking complete.
Please help me! :(
If there's a user created within the Auth section of Firebase as well, then you can actually use the fetchProviders method, and if no providers are returned, you have no user in you Auth section.
FIRAuth.auth()?.fetchProviders(forEmail: email, completion: { (providers, error) in
if providers == nil {
// user doesn't exist
} else {
// user does exist
}
})
I have not tested the code but the theory would be to order by username that equals to the username you are looking for. If this snapshot exists you already have a user in the database with that name if it doesn't you do not.
Will run and test code when i have a chance to and update my post.
let rootRef = FIRDatabase.database().reference()
rootRef.child("users").queryOrdered(byChild:"username").queryEqual(toValue: self.chooseUsernameTextField.text!)
.observe(.value, with: { (snapshot) in
if snapshot.exists() == false {
print("not found")
} else {
print("usr found")
}
});
When using Firestore to save users, I did this
Auth.auth().addStateDidChangeListener({ (auth, user) in
if let id = user?.uid{
let userDoc = db.collection("users").document(id)
userDoc.getDocument { (document, error) in
if document == nil{// if user doesn't exist, create one
self.db.collection("users").document(id).setData(userProfile.toDictionary()){ error in
}
}
}
}
})

Resources