'SwiftUI: Check if Firebase RealtimeDatabase has a specific Child the register the value or return error

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()
  }
}


Solution 1:[1]

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()
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 HunterLion