0

I am trying to check wether or not a username is taken while the user types in the name. Currently I have these two functions that are working almost the way I would want it to work but not 100% correctly:

textFieldDidChange:

 @objc private func textFieldDidChange(_ textField: UITextField) {

    switch textField {

    // ... //    
    case usernameTextField:
        if textField.text?.isEmpty == false {
            checkUsername(field: textField.text!) { (success) in
                print(textField.text!)
                if success == true {
                    // username is taken
                    print("Username is taken")
                    self.setupUsernameTextField()
                    self.checkUsernameImage.image = UIImage(named: "false")
                    self.checkUserNameLabel.text = "Benutzername ist bereits vergeben"
                } else {
                    // username is not taken
                    print("Username is not taken")
                    self.checkUsernameImage.image = UIImage(named: "correct")
                    self.checkUserNameLabel.text = "gültiger Benutzername"
                }
            }
        }else {
            self.checkUsernameImage.image = UIImage(named: "false")
            self.checkUserNameLabel.text = "kein gültiger Benutzername"
        }
    default:
        break
    }

}


// helper function to check if username is in databse -> later in Datahandler
func checkUsername(field: String, completion: @escaping (Bool) -> Void) {

    let db = Firestore.firestore()
    let collectionRef = db.collection("users")
    collectionRef.whereField("username", isEqualTo: field).getDocuments { (snapshot, err) in
        if let err = err {
            print("Error getting document: \(err)")
        } else if (snapshot?.isEmpty)! {
            completion(false)
        } else {
            for document in (snapshot?.documents)! {
                if document.data()["username"] != nil {
                    completion(true)
                }
            }
        }
    }
}

Problem: If you type in something quite fast or if your internet connection isn't very good it displays Username is not taken when it actually is.

If you look at the Instagram-App, they are doing it the perfect way:

  1. type something in, even fast
  2. after you stop typing for maybe a second, a loading indicator pops up and only then u get feedback wether or not the username is taken
  3. type something in while loading indicator is active, it stops and only starts again if you stop typing again.

My Question: How do I realize that??? Like how do I know when the user "stops" typing but the textField is still editing ? I tried it with shouldEndEditing but that only works if the textfield is no longer selected and thats not what I would like to achieve. In the end I would like to have the exact same process as in the Instagram app.

Any ideas on how to realize that??

Frank van Puffelen
  • 418,229
  • 62
  • 649
  • 645
Chris
  • 420
  • 6
  • 32
  • 1
    I suggest looking into implementing a "debounce". – Doug Stevenson Mar 04 '20 at 16:16
  • 1
    This is done with a simple timer between keystrokes. If there's another keystroke while the timer is running, reset the timer. When the timer expires, lookup the existing string. You could do this within `shouldChangeCharactersInRange` for example – Jay Mar 04 '20 at 18:28
  • uff never used a timer or `shouldChangeCharactersInRange` . Could you maybe elaborate on this? :) I think that's exactly what I need! – Chris Mar 04 '20 at 21:01
  • When responding to a comment, be sure to include the @ symbol before the persons name, like @Jay - that will notify the person. See the docs for [UITextField](https://developer.apple.com/documentation/uikit/uitextfield) for a link to that function. If you need a timer, then see [Timer](https://developer.apple.com/documentation/foundation/timer) and then [this question](https://stackoverflow.com/questions/24007518/how-can-i-use-timer-formerly-nstimer-in-swift) for a bunch of examples - be sure to look at the Swift 4 and Swift 5 answers. – Jay Mar 04 '20 at 22:21
  • @Jay thanks! still not quite sure what `shouldChangeCharactersInRange` is doing. Can't I just use a `didChange`-method (add target to textfield) use for that? – Chris Mar 04 '20 at 22:32

1 Answers1

1

With @Jays suggestion I was able to make it work:

My textField is connected with a didChange method which looks like this:

timer.invalidate() // reset timer

// start the timer
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)

And the connected timerAction-method looks like this:

// called every time interval from the timer
@objc func timerAction() {
    checkUsername(field: usernameTextField.text!) { (success) in
        print(self.usernameTextField.text!)
        if success == true {
            // username is taken
            print("Username is taken")
            self.setupUsernameTextField()
            self.checkUsernameImage.image = UIImage(named: "false")
            self.checkUserNameLabel.text = "Benutzername ist bereits vergeben"
            // stop timer
            self.timer.invalidate()
        } else {
            // username is not taken
            print("Username is not taken")
            // stop timer
            self.timer.invalidate()
        }
    }
}

Works exactly the way I want it to work, thanks for the help:)

Chris
  • 420
  • 6
  • 32
  • Hey, remember to accept your own answer after [48 hours](https://stackoverflow.com/help/self-answer) in order to help the community in case somebody else has the same issue as you. – Ajordat Mar 05 '20 at 09:49