0

I try to prevent the creation of already existing usernames.

This is my code where I upload usernames to the database:

ref?.child("users").child(FIRAuth.auth()!.currentUser!.uid).child("username").setValue(self.createUserName.text)

And this is the code where I try to get if the username already exists or not

ref?.child("users")
    .queryOrdered(byChild: "username")
    .queryEqual(toValue: self.createUserName.text?.uppercased())
    .observeSingleEvent(of: .value, with: { (snapshot) in

        if !snapshot.exists() {

            print("")

        }

    }) { error in

        print("")

    }

The database looks like this

Database_App {

    users {

        -3bSRmS4PHXUwsr7XbMBwgPozNfK2 {

            username: "sebas.varela"

        }

    }     

} 

And appear this line in the console:

Consider adding ".indexOn": "username" at /users to your security rules for better performance

The problem is that I always get . What is the problem with this?

AL.
  • 36,815
  • 10
  • 142
  • 281
sebas.varela
  • 1,535
  • 2
  • 10
  • 12
  • Have you considered making use of the Firebase Database Rules to prevent a duplicate data? See this [answer](http://stackoverflow.com/a/20291163/4625829) by @Kato – AL. Dec 13 '16 at 02:07
  • In order to verify unique usernames with the way you have your firebase database setup you will have to -> get a snapshot of the entire node at `ref.child("users") `, cast that to -> `let value = sanpshot.value as! [String: [String: String]]` and then iterate through the dictionary keys, checking if the value for the key `username` of the nested dictionary is equal to the username that the user has asked for. This is inefficient , follow @FrankvanPuffelen advice – MikeG Dec 13 '16 at 05:13
  • You can accomplish this in a couple of ways. 1) Let Firebase do the heavy lifting by trying to create the user name and if it's in use Firebase will generate an error. See [Username Uniqueness](http://stackoverflow.com/questions/40619433/firebase-username-uniqueness-in-swift/40640229#40640229) 2) Keep user info in a /users node with usernames as a child. Query for it to determine if it exists. See [make username unique](http://stackoverflow.com/questions/35243492/firebase-android-make-username-unique) which is not swift but covers the basics. – Jay Dec 13 '16 at 23:08

1 Answers1

2

You can only query for values that are at a directly under the reference you query at. For your case that would be with a data model like:

Database_App {
    users {
        -3bSRmS4PHXUwsr7XbMBwgPozNfK2: "sebas.varela"
    }     
} 

This will work in your code, but is hard to get secure and performant. The more common approach is to work with a extra node where you map user names to uids:

Database_App {
    userNames {
        "sebas,varela": "-3bSRmS4PHXUwsr7XbMBwgPozNfK2"
    }     
} 

In this case the node essentially allows a user to claim their name. The advantage of this system is that the keys are automatically guaranteed to be unique on the server, no client-side code needed for that part.

You will want to:

  • add security rules that ensure a user can only claim a username that hasn't been claimed yet
  • also in these security rules allow a user to release their claim on a username
  • add client-side code to handle conflicts in a nicer way than the default "permission denied" error you'll get from the server
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Ok, but I'm new to Firebase and I do not know how to add the rules you mention. – sebas.varela Dec 13 '16 at 17:49
  • Another problem I have is that trying to call the node as the user name and placing the value of the user_id executes an error since you can not place signs like dots or commas in the name of the node. – sebas.varela Dec 13 '16 at 18:24
  • You might have noticed that I use a comma instead of the period in my data above, for that specific reason. Encoding values to make them acceptable as keys is a normal part of this type of validation. – Frank van Puffelen Dec 13 '16 at 23:29