1

The project can be cloned here https://github.com/randomjuniorburger/quizapp

I have a UIButton which is linked to an action which makes use of DispatchQueue.main.asyncAfter. The app works well except when the user spams the True/False buttons/presses or triggers either of them many times consecutively in a short interval, causing the delay(s) to overlap and allowing the user to achieve a higher score than should be possible (e.g. only 8 questions have the answer "True", but a score of 10 is possible due to the bug if the true button is spammed)

I am wondering how I can fix/prevent this?

NB - I am a beginner/hobbyist Swift developer. This project is my attempt to recreate a module of a course I am taking by Angela Yu.

I welcome any suggestions as to how my code can be improved and more closely follow best-practice.

The code for the button alone is;

@IBAction func answerPressed(_ sender: UIButton) {

    let userAnswer = sender.currentTitle
    let actualAnswer = quizArray[questionNumber].answer

    if questionNumber < (quizArray.count - 1) {
        self.questionNumber += 1
        updateProgressBar()
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            self.questionLabel.text = self.quizArray[self.questionNumber].text
        }

        if userAnswer == actualAnswer {
            questionLabel.text = "✅"
            score += 1
            scoreLabel.text = String("Score: \(score) / 12")

        } else {
            questionLabel.text = "❌"
        }

    } else if questionNumber == (quizArray.count - 1) && questionLabel.text != "End of Quiz" {
        if userAnswer == actualAnswer {
            questionLabel.text = "✅"
            score += 1
            scoreLabel.text = String("Score: \(score) / 12")
        } else {
            questionLabel.text = "❌"
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            self.progressBar.progress = Float(1)
            print("No more questions")
            self.questionLabel.text = "End of Quiz"
        }
    }

}
  • The question is *why* do use `DispatchQueue.main.asyncAfter` on button action? I don't see any reasonable logic to not do action exactly. – Asperi Apr 28 '20 at 08:02
  • @Asperi The delay allows the user see either a tick or a cross to determine whether their answer is correct or incorrect before the question text transitions to the next question – JuniorBurger Apr 28 '20 at 11:43

1 Answers1

0

Here is possible solution - just don't give possibility for a user to generate not desired taps

sender.isEnabled = false // don't allow action until question updated
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
    self.questionLabel.text = self.quizArray[self.questionNumber].text
    sender.isEnabled = true // << allow user interaction
}

in second do the same

Asperi
  • 228,894
  • 20
  • 464
  • 690