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
}
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.