Search code examples
swiftuishuffle

SwiftUI: Shuffling answers in multiple choice quiz only when loading a new question


I want to shuffle answer options from Trivia API only when loading a new question, not every time a user selects an answer. Currently, the answers shuffle each time a user makes a selection.

For example, when question a is loaded, maybe like this:

"Who won the 2014 World Cup in Brazil?"

I have 4 different answer options (buttons), maybe like this:

"Russia", "Germany", "Spain", "Italy"

When I chooses an answer by pressing one of the buttons I can see how my buttons shuffle again before I go to the next question. So it will maybe look like this after I've pressed a button in the example:

"Italy", "Spain", "Russia", "Germany".

Note that I have a button with the text "Next" that user can only can press after an answer has been chosen.

I have attempted to shuffle answer options by using a shuffleAnswers() function inside my ForEach loop. My expectation was that this would shuffle the answer options for that specific question. However, the current implementation shuffles the answers every time a user selects an answer, which is not the desired behavior.

Here is my code for my shuffleAnswers() function:

func shuffleAnswers() -> [String] {
        var answers = api.QnAData[currentQuestionIndex].incorrectAnswers
        answers.append(api.QnAData[currentQuestionIndex].correctAnswer)
        answers = answers.compactMap { $0.removingPercentEncoding }
        return answers.shuffled()
    }

And here is my code for my ForEach loop:

 if !api.QnAData.isEmpty {
            ForEach(shuffleAnswers(), id: \.self) { shuffledAnswer in
                    Button(action: {
                        if answerSelected == nil {
                            if let decodedAnswer = shuffledAnswer.removingPercentEncoding {
                                answerSelected = decodedAnswer.removingPercentEncoding
                                hasUserAnswered = true
                            
                            }
                            if shuffledAnswer.removingPercentEncoding == api.QnAData[currentQuestionIndex].correctAnswer.removingPercentEncoding {
                                
                                answerSelected = shuffledAnswer.removingPercentEncoding
                                
                                
                                if let userAnswer = answerSelected?.removingPercentEncoding {
                                    if userAnswer == api.QnAData[currentQuestionIndex].correctAnswer.removingPercentEncoding {
                                        score += 1
                                        print("Correct answer, your score: \(score)")
                                    } else {
                                        print("Wrong answer, your score: \(score)")
                                    }
                                }
                                
                            } else {
                                
                                print("wrong answer, player score: \(score)")
                                
                            }
                        }
                    }) {
                        
                        Text(shuffledAnswer)

Solution

  • It's because shuffleAnswers() returns shuffled array every time you access it.

    Try to cache it somehow to prevent this. For example, you can make an state and only shuffle onAppear (as you asked):

    @Stated cachedShuffledAnswers = [String]()
    
    var body: some View {
        ,,,
        .onAppear {
            self.cachedShuffledAnswers = shuffleAnswers()
        }
    }
    

    Then use the cached version in the ForEach:

    ForEach(cachedShuffledAnswers, id: \.self) ...
    

    Note that this is just a preview to a possibility and I recommend you to try decouple the logic from the UI soon in the future