Bjorn Morrhaye
Bjorn Morrhaye

Reputation: 731

Swift closures and error handling

hello, i need some help with function and or possibly closures in that function. I want my function to check the firestore users collection for duplicate documents (usernames). If a duplicate is found i want to display a message, if a duplicate is not found, create a new user. i have folowing code:

func checkIfUserExists(username: String, completion: @escaping (Bool) -> Void) {
    let docRef = db.collection("users").document(username)
    docRef.getDocument { (document, error) in
        if error != nil {
            print(error)
        } else {
            if let document = document {
                if document.exists {
                    completion(true)
                } else {
                    completion(false)
                }
            }
        }
    }
} 

and i call the function with:

if let username = textField.text, username.count > 8 {              // Username needs to be more then 8 Char
    checkIfUserExists(username: username) { (doesExist) in
        if doesExist {
            print("user exists")
        } else {
            print("new User can be created")
        }
    }
} else {
    print("Username needs to be more then 8 char")
} 

}

It works, but i have the feeling it is not good practice and i'm making detours. Is this the right way to do it ?

Upvotes: 1

Views: 371

Answers (1)

Jen Person
Jen Person

Reputation: 7546

I think the way you're doing it now should work well, but another option to prevent you from having to do a read of the database before writing is to use security rules. For example, if this is the structure of your users collection...

users: [
   username1: { // doc ID is the username
      userid: abcFirebaseUserId, // a field for the uid of the owner of the username
      //...etc
   }
]

...then you can use the following rules:

match /users/{username} {
  allow create: if request.auth.uid != null;              
  allow update, delete: if resource.data.userId = request.auth.uid;
}

This allows any authenticated user to create a new username, but only the owner of that username can update it or delete it. If you aren't allowing users to change their username, you wouldn't even have to worry about the second rule. Then, in the client, you go right to creating a username, like so:

func createUsername(username: String, completion: @escaping (String?) -> Void) {
    guard let userId = Auth.auth().currentUser.uid else {
        completion("no current user")
        return
    }
    let docRef = db.collection("users").document(username)
    docRef.setData(data:[userId: userId]) { error in
        if let error = error {
            completion(error.debugDescription)
        } else {
            completion(nil)
        }
    }
} 

This would write the new username to the database and pass an error to the closure if there is one. If the username already exists, an insufficient permissions error would be present. When checking if the user exists, you could display the error or alert the user however you wanted.

createUsername(username: username) { err in
    if let err = err {
        print("user exists")
    } else {
        print("new User has been created")
    }
}

Just a suggestion though. I think they way you're doing it now is fine, too!

Upvotes: 1

Related Questions