Home > database >  SwiftUI: Replace static data with fetching from json instead (Quiz)
SwiftUI: Replace static data with fetching from json instead (Quiz)

Time:01-07

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()
    }

}
  •  Tags:  
  • Related