0

Question

How do I check if a username already exists during the user sign up process?

I don't know how to query Firebase to determine if a specific value exists.


Background

Firebase structure:

Users
    0BBfrF1vVBXXxNxeVMes9MFkYNJ3
    name: "SAM"

    0oU9sf7CZxaDBx03t87lqTrv9UM2
    name: "JACK"

    IsXEqXov0obuwl1WOrHhCbfdfEo1
    name: "JEREMY"

In the code below:

  • I attempt to check if value usernameField exists in: users\userID\name
  • child(userID) doesn't retrieve anything

_

let username = self.usernameField.text

        let userRef = ref.child("Users").child(userID).child("name")

        userRef.observeEventType(.Value, withBlock: { snapshot in

            if snapshot.value!.isEqual(username) {

                print("snapshot exists")

            } else {

                print("snapshot doesnt exist")

            }


            userRef.removeAllObservers()

            }, withCancelBlock: { error in

                print(error)

        })
SoAwesomeMan
  • 3,226
  • 1
  • 22
  • 25
Illan Simon
  • 29
  • 1
  • 2

1 Answers1

3

There is a pretty easy way on doing this.

Since you are using removeAllObservers right after the the first callback I'm assuming that you might take a look on using observeSingleEventOfType and you wont need to turn any observer later.

let username = self.usernameField.text
ref.child("Users").queryOrderedByChild("name").queryEqualToValue(username).observeSingleEventOfType(.Value, withBlock: { (snapshot) in 
    if snapshot.exists == true {
        print("snapshot exists")
    } else {
        print("snapshot doesnt exist")
    }
}) { (error) in
   print(error.localizedDescription)
} 

You should also write some database rules to guarantee the data consistency and performance in the server side. From the current structure you have this wont be straight-forward since you don't have the username as the key for your Users branch.

So I can see two possible solutions:

Username as the key

Saving the username as the /Users key you will just have a rule to enforce this key uniqueness.

{ "rules": { 
  "Users": {
    ".indexOn":
    ".write": "true",
    "$username": {
        ".validate": "!root.child('Users').hasChild($username)"
    }
  }
}}

This would need some changes on your application code to see if this already exists

ref.child("Users").child(username).observeSingleEventOfType(.Value, withBlock: { (snapshot) in 
        if snapshot.exists == true {
            print("snapshot exists")
        } else {
            print("snapshot doesnt exist")
        }
    }) { (error) in
       print(error.localizedDescription)
    } 

Create a new branch to handle the username uniqueness

You want to keep the same structure you have today you will need to do some other changes. Just like the question that Frank linked you will need an extra branch only to store all the taken usernames. So whenever saving a new user you will need to first store the username in this other branch.

{ "rules": { 
  "Users": {
    ".indexOn": "name",
    ".write": "true",
    "$id": {
        ".validate": "!root.child('already_taken_names').hasChild(newData.child('name').val())"
    }
  },
  "already_taken_names": {
    "$username": {
        ".validate": "!root.child('Users').hasChild($username)"
    }
  }
}}
adolfosrs
  • 9,286
  • 5
  • 39
  • 67
  • This has in inherent race condition: between the time the query listens and the time the user registers, some other user might have claimed that username. That race condition should be solved by using security rules, since those are the only way to ensure consistency on the server. By combining your client-side approach with server-side security rules, you'll get a security solution with a good UX. See http://stackoverflow.com/questions/25294478/how-do-you-prevent-duplicate-user-properties-in-firebase – Frank van Puffelen Jul 01 '16 at 00:00
  • This very insteresting Frank but actually, I try to have my solution a bit easier to begin with firebase:) adolfors, whatever what I type as username, I get a "snapshot exists".... Thanks :) – Illan Simon Jul 01 '16 at 07:02
  • I get this message in my log: snapshot exists [FirebaseDatabase] Using an unspecified index. Consider adding ".indexOn": "name" at /Users to your security rules for better performance – Illan Simon Jul 01 '16 at 07:06
  • @IllanSimon nice. so is my code working or not? I will work on adding some details for the security rules – adolfosrs Jul 01 '16 at 10:19
  • Wouldn't this take longer the more users you have in your database? – WYS May 10 '17 at 10:39