title: Firebase iOS Swift: Prevent Duplicate Usernames
body: <p>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 <a href="https://stackoverflow.com/questions/35243492/firebase-android-make-username-unique">Firebase-android-make-username-unique</a> and <a href="https://stackoverflow.com/questions/36915473/how-prevent-username-from-duplicate-signup-in-firebase">how-prevent-username-from-duplicate-signup-infirebase</a>) but I have still not gotten it to fully work.</p>
  
  <p>I followed the instructions in the first link above and set up my data structure as:</p>
  
  <pre><code>app : {
      users: {
         "some-user-uid": {
              email: "test@test.com"
              username: "myname"
         }
      },
      usernames: {
          "myname": "some-user-uid"
      }
  }
  </code></pre>
  
  <p>and my security rules as:</p>
  
  <pre><code>"users": {
    "$uid": {
      ".write": "auth !== null &amp;&amp; auth.uid === $uid",
      ".read": "auth !== null &amp;&amp; auth.provider === 'password'",
      "username": {
        ".validate": "
          !root.child('usernames').child(newData.val()).exists() ||
          root.child('usernames').child(newData.val()).val() == $uid"
      }
    }
  }
  </code></pre>
  
  <p>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.</p>
  
  <pre><code>  func createNewAccount(uid: String, user: Dictionary&lt;String, String&gt;) {
  
      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&lt;String, String&gt;) {
  
      USERNAME_REF.updateChildValues(userData)
    }
  </code></pre>
  
  <p>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.</p>
  
  <pre><code>@IBAction func createAccount() {
      let username = usernameField.text
      let email = emailField.text
      let password = passwordField.text
  
      if username != "" &amp;&amp; email != "" &amp;&amp; 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)          
  
            })
  </code></pre>
  
  <p><strong><em>EDIT</em></strong></p>
  
  <p>Here is my updated createAccount method that solved my issue.</p>
  
  <pre><code>  @IBAction func createAccount() {
      let username = usernameField.text
      let email = emailField.text
      let password = passwordField.text
  
  
  if username != "" &amp;&amp; email != "" &amp;&amp; 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)
  
  
            })
  </code></pre>