Search code examples
iosxcodeswiftuishuffle

Swift: Trouble Shuffling Answers in a Trivia/Quiz App


Please help me as I'm a beginner in SwiftUI and programming overall. This is my first time developing a trivia app. I'm encountering an issue with shuffled answers for trivia questions. I'm trying to create a function that contains all the answers, including the correct one, and shuffles them randomly but I really do not know how to do this. Please help me, any little help is appreciated a lot.

This is my code for TriviaAPIData:

import Foundation

struct QuestionResponse: Decodable {
   var results: [Question]
}

struct Answer: Identifiable {
   var id = UUID()
   var text: String
   var isCorrect: Bool
   
}
       struct Question: Decodable {
           var category: String
           var question: String
           var correctAnswer: String
           var incorrectAnswers: [String]
          
   }

This is my code for TriviaAPI:

import Foundation
import SwiftUI

class TriviaAPI: ObservableObject {
    
    struct TriviaAnswer {
        let text: String
        let isCorrect: Bool
    }
   
   @Published var QnAData: [Question] = [] 
    
    func getQnAData(selectedCategoryNumber: Int, selectedDifficultyInPopup: String) async throws {
        
        let endpoint = "https://opentdb.com/api.php?amount=10&category=\(selectedCategoryNumber)&difficulty=\(selectedDifficultyInPopup)&type=multiple&encode=url3986"
        
        guard let url = URL(string: endpoint) else {throw APIErrors.invalidURL}
        
        var request = URLRequest(url: url)
        
        request.httpMethod = "GET"
        
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
            throw APIErrors.invalidResponse
        }
        
        do {
            let decoder = JSONDecoder()
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            
            let questionResponse = try decoder.decode(QuestionResponse.self, from: data)
            
            DispatchQueue.main.sync {
                self.QnAData = questionResponse.results
            }
        } catch {
            throw APIErrors.invalidData
        }
    }
}

Here is my code for QuizGameScreen:

import Foundation
import SwiftUI

struct QuizGameScreen: View {
    
    @StateObject var api = TriviaAPI()
    @Binding var selectedCategoryNumber: Int
    @Binding var selectedCategoryName: String
    @State var currentQuestionIndex = 0
    @Binding var selectedDifficultyInPopup: String
    
    func shuffleAnswers() {
        var correctAnswers = [api.QnAData[currentQuestionIndex].correctAnswer]
        var incorrectAnswers = api.QnAData[currentQuestionIndex].incorrectAnswers
        
        let allAnswers = correctAnswers + incorrectAnswers
        var shuffeldQuestions = allAnswers.shuffled()
    }
    
    var body: some View {
    
        ZStack{
            
            Image("NewBgQuizUp")
                .resizable()
                .scaledToFill()
                .edgesIgnoringSafeArea(.all)
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight:.infinity)
            VStack(alignment: .leading){
                Text("QuizUp")
                    .font(.system(size: 36, design:
                            .rounded)).fontWeight(.bold)
                    .foregroundColor(.white)
                    .offset(x: -100, y: -350)
            }
            Text("1 out of 0")
                .font(.system(size: 23, design:
                        .rounded)).fontWeight(.bold)
                .foregroundColor(.white)
                .offset(x: 100, y: -300)
            
            VStack{
            
                if !api.QnAData.isEmpty {
                    if currentQuestionIndex < api.QnAData.count { // Check if the current question index is within bounds
                        if let decodedQuestion = api.QnAData[currentQuestionIndex].question.removingPercentEncoding {
                            Text(decodedQuestion)
                                .font(.system(size: 23, design: .rounded)).fontWeight(.bold)
                                .foregroundColor(.white)
                                .offset(y: -100)
                        } else {
                            Text("Failed to decode question")
                                .font(.system(size: 23, design: .rounded)).fontWeight(.bold)
                                .foregroundColor(.white)
                                .offset(y: -100)
                        }

                        ForEach(0..<api.QnAData[currentQuestionIndex].incorrectAnswers.count, id: \.self) { index in
                            if index < api.QnAData[currentQuestionIndex].incorrectAnswers.count { // Check if the index is within bounds
                                if let decodedAnswer = api.QnAData[currentQuestionIndex].incorrectAnswers[index].removingPercentEncoding {
                                    Button(action: {
                                       
                                        print("wrong answer")
                                       
                                    }) {
                                        Text(decodedAnswer)
                                            .font(.system(size: 20, design: .rounded))
                                            .foregroundColor(.purple)
                                            .frame(width: 300, alignment: .leading)
                                            .padding()
                                            .background(Color.white)
                                            .fontWeight(.bold)
                                            .cornerRadius(10)
                                    }
                                }
                            }
                        }
                     if currentQuestionIndex < api.QnAData.count { 
                            if let decodedCorrectAnswer = api.QnAData[currentQuestionIndex].correctAnswer.removingPercentEncoding {
                                Button(action: {
                                    print("correct answer")
                                }) {
                                    Text(decodedCorrectAnswer)
                                        .font(.system(size: 20, design: .rounded))
                                        .foregroundColor(.purple)
                                        .frame(width: 300, alignment: .leading)
                                        .padding()
                                        .background(Color.white)
                                        .fontWeight(.bold)
                                        .cornerRadius(10)
                                }
                            }
                        }
                    }
                } else {
                    Text("Loading...")
                        .font(.system(size: 23, design: .rounded)).fontWeight(.bold)
                        .foregroundColor(.white)
                        .offset(y: -100)
                }
            }
        }.task {
                         
            do {
                try await api.getQnAData(selectedCategoryNumber: selectedCategoryNumber, selectedDifficultyInPopup: selectedDifficultyInPopup)
                shuffleAnswers() // calling on my function
            } catch APIErrors.invalidData {
                print("Invalid Data")
            } catch APIErrors.invalidURL {
                print("Invalid Url")
            } catch APIErrors.invalidResponse {
                print("Invalid Response")
            } catch {
                print("General error")
            }
        }
    }
}
struct QuizGameScreen_Previews: PreviewProvider {
    static var previews: some View {
        QuizGameScreen(selectedCategoryNumber: .constant(21), selectedCategoryName: .constant("Sport"), selectedDifficultyInPopup: .constant("Easy"))
    }
}
enum APIErrors: Error {
    case invalidURL
    case invalidResponse
    case invalidData
}


Solution

  • If you want to have an array of shuffled answers including the correctAnswer, then you can use:

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

    This will give you (return) an array of shuffled answers that you can use in your code.

    Note you don't show where you want to use this in your code.

    Note also, although you test for bounds, using indices in your QuizGameScreen is not a good idea, especially in ForEach loops. Re-structure that code.