0

I am currently building an app with an account system.

Firebase is very new to me, that's why I watched a lot of tutorials, and now its working fine.

I want to implement that the user can choose a unique username at the registration. My problem is, I really don't know how to check if this name is already taken.

I found some code for that, but that's not working, I will show you the code for the RegistrationService file.

I hope someone can explain to me how to implement this username verification. It should return an error if the username is already taken and do continue the registration if its a valid username.

Thank you!

import Combine
import Firebase
import FirebaseDatabase
import Foundation

enum RegistrationKeys: String {
  case firstName
  case lastname
  case info
  case username
}

protocol RegisterService {
  func register(with details: RegistrationDetails) -> AnyPublisher<Void, Error>
}

final class RegisterServiceImpl: RegisterService {
  func register(with details: RegistrationDetails) -> AnyPublisher<Void, Error> {
    Deferred {
      Future { promise in
        Auth.auth()
          .createUser(
            withEmail: details.email,
            password: details.password
          ) { res, error in
            if let err = error {
              promise(.failure(err))
            } else {
              // Success on  User creation
              if let uid = res?.user.uid {
                let values =
                  [
                    RegistrationKeys.firstName.rawValue: details.firstName,
                    RegistrationKeys.lastname.rawValue: details.lastName,
                    RegistrationKeys.info.rawValue: details.info,
                  ] as [String: Any]
                let db = Database.database(url: "theurl")
                Database.database(url: "the url")
                  .reference()
                  .child("usernames")
                  .child("\([RegistrationKeys.info.rawValue: details.username] as [String : Any])")
                // here should be the check and then continue if its valid
                db
                  .reference()
                  .child("users")
                  .child(uid)
                  .updateChildValues(values) { error, ref in
                    if let err = error {
                      promise(.failure(err))
                    } else {
                      promise(.success(()))
                    }
                  }
              } else {
                promise(.failure(NSError(domain: "Invalid user ID", code: 0, userInfo: nil)))
              }
            }
          }
      }
    }
    .receive(on: RunLoop.main)
    .eraseToAnyPublisher()
  }
}
Peter Friese
  • 6,709
  • 31
  • 43
Paullim
  • 25
  • 5
  • [This](https://stackoverflow.com/questions/35243492/firebase-android-make-username-unique) might help. – udi Feb 16 '22 at 18:21
  • All users will get a unique UID when signing in using Firebase Authentication - no matter which provider you use, the UID will be unique. To me it sounds like you want users to give the option to pick a unique _nickname_ in addition. If so, check out https://peterfriese.dev/posts/swiftui-combine-networking-efficient/ in which I describe how to use Combine to achieve this. You would have to adopt this to RTDB / Firestore obviously. BTW - Firebase supports Combine out of the box - see https://github.com/firebase/firebase-ios-sdk/tree/master/Example/CombineSample – Peter Friese Feb 17 '22 at 08:43

1 Answers1

0

I can see two possibilities to solve your problem:

If the e-mail can serve as the username

Firebase authentication already sends back an error message in case the e-mail (the one used when creating the user) already exists. If the e-mail passed in the following function is not unique, an error will be thrown:

Auth.auth()
          .createUser(
            withEmail: details.email,
            password: details.password
          ) { res, error in
            if let err = error {
              promise(.failure(err))

If an additional username besides the e-mail is required

If you need usernames in addition to the e-mails, you can store them under a node "usernames", like we see in your example. Personally, I would hash them instead of storing them plain.

The structure could simply be:

{
    usernames: {
        username_1: true,
        username_2: true,
        ...
        username_n: true
    }
}

The example below checks to see if a new username exists and stores the result in the variable isUsernameTaken:

let db = Database.database(url: "the url").reference()

let newUsername = "seeIfItIsTaken"

db.child("usernames").child(newUsername).getData() { error, snapshot in
    guard error == nil else {
        print("Found error \(error)")
        return
    }
    let isUsernameTaken = snapshot.exists()
}
HunterLion
  • 3,496
  • 1
  • 6
  • 18