I'm new to SwiftUI and trying to build a simple single-player Quiz app. I started by following this tutorial and then tried to continue on my own. Currently, the questions are hardcoded in my ViewModel but I'd like to change it to fetching from a local JSON file instead. Anyone that can point me in the right direction?
Here's a part of my ViewModel with the questions as static data:
extension GameManagerVM {
static var questions = quizData.shuffled()
static var quizData: [QuizModel] {
[
QuizModel(
id: 1,
question: "Question title 1",
category: "Sport",
answer: "A",
options: [
QuizOption(id: 11, optionId: "A", option: "A"),
QuizOption(id: 12, optionId: "B", option: "B"),
QuizOption(id: 13, optionId: "C", option: "C"),
QuizOption(id: 14, optionId: "D", option: "D")
]
),
...
}
}
Here's a test I did, but I get the error that Xcode can't decode it. I replace the above code with this:
extension GameManagerVM {
static var questions = quizData.shuffled()
static var quizData: [QuizModel] = Bundle.main.decode("quizquestions2022.json")
}
And here's the JSON.
[
{
"id": "1",
"question": "Question title 1",
"category": "Sport",
"answer": "A",
"options": [
{
"id": "1001",
"optionId": "A",
"option": "A"
},
{
"id": "1002",
"optionId": "B",
"option": "B"
},
{
"id": "1003",
"optionId": "C",
"option": "C"
},
{
"id": "1004",
"optionId": "D",
"option": "D"
}
]
},
]
Here are my models
struct Quiz {
var currentQuestionIndex: Int
var quizModel: QuizModel
var quizCompleted: Bool = false
var quizWinningStatus: Bool = false
var score: Int = 0
}
struct QuizModel: Identifiable, Codable {
var id: Int
var question: String
var category: String
var answer: String
var options: [QuizOption]
}
struct QuizOption: Identifiable, Codable {
var id: Int
var optionId: String
var option: String
var isSelected: Bool = false
var isMatched: Bool = false
}
CodePudding user response:
When you are decoding, unless you make your own decoding like this sample init from decoder Then all of non-optional the vars or lets in the struct need to be in the json. Your data doesn't have isSelected or isMatched in the options, so those need to be optional
struct QuizOption: Identifiable, Codable {
var id: Int
var optionId: String
var option: String
var isSelected: Bool?
var isMatched: Bool?
CodePudding user response:
to fetch your data from a local JSON file, you could try
this approach, where you need to have a model (QuizModel) to
match the json data in your file. Also I used a class GameManagerVM: ObservableObject
to hold/publish your data as an example:
import SwiftUI
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@StateObject var gameManager = GameManagerVM()
var body: some View {
List {
ForEach(gameManager.quizData) { quiz in
Text(quiz.question)
}
}
}
}
struct QuizModel: Identifiable, Codable {
let id, question, category, answer: String
let options: [QuizOption]
enum CodingKeys: String, CodingKey {
case id, question, category, answer, options
}
}
struct QuizOption: Codable {
let id, optionId, option: String
var isSelected: Bool = false
var isMatched: Bool = false
enum CodingKeys: String, CodingKey {
case id, optionId, option
}
}
class GameManagerVM: ObservableObject {
@Published var questions: [QuizModel] = []
@Published var quizData: [QuizModel] = []
init() {
quizData = Bundle.main.decode("quizquestions2022.json")
questions = quizData.shuffled()
}
}
