dvd.Void
dvd.Void

Reputation: 369

Using Decodable and JSONs

I have a JSON file that is formatted in the following way:

[
  {
    "QID": "B002",
    "Stash": "Basic",
    "Category": "Geography",
    "Question": "What is the highest mountain on earth?",
    "Answer": "Mt Everest"
  },
  {
    "QID": "B003",
    "Stash": "Basic",
    "Category": "General",
    "Question": "What is the gestation period for a pregnant elephant?",
    "Answer": "2 years"
  }
]

I'm trying to make a structure so that I can load all the questions in my JSON file into my quiz-app. So far from what I've researched from JSON and the new "Decodable" thing apple added I have my Swift code as follows (Note there's a failed attempt commented out):

var STASHES_SELECTED = ["BasicStash", "MediumStash", "HardStash"]

    struct TriviaQuestion: Decodable {
        let QID: String
        let Stash: String
        let Categoroy: String
        let Question: String
        let Answer: String
    }

    func loadQuestionStash()
    {
        /*
        for var i in STASHES_SELECTED
        {
         let url = Bundle.main.url(forResource: STASHES_SELECTED[i], withExtension: "JSON") //CANT GET THIS TO WORK!, SAYS CANNOT SUBSCRIPT TYPE [STRING] WITH INDEX TYPE 'STRING'
        }*/

        if let url = Bundle.main.url(forResource: "BasicStash", withExtension: "JSON")
        {
            let json = try? Data(contentsOf: url)

            let questions = try? JSONDecoder().decode(TriviaQuestion.self, from: json!)
            print (questions!) //FATAL ERROR, FOUND NIL

        }
    }

As you can see from the code comments, that currently gives me a fatal error "found nil while unwrapping". So I assume that the previous line JSONDecoder(). failed horribly.
I am not sure if I am doing this correctly as it's my firs time working with JSONs and I've just been pretty much cookie-cutter following tutorials and posts online. I'd really appreciate some help here. Also the .self after TriviaQuestion was added by the system (I think the problem might be somewhere in there)

Upvotes: 0

Views: 621

Answers (2)

Umair M
Umair M

Reputation: 10720

As @Oxthor mentioned the typing error,

I just want to add that always use quicktype.io to create your data struct. You will avoid typos and save your time:

// To parse the JSON, add this file to your project and do:
//
//   let triviaQuestion = Array.from(json: jsonString)!

import Foundation


struct TriviaQuestion: Codable {
    let answer: String
    let category: String
    let qID: String
    let question: String
    let stash: String
}

// MARK: Top-level extensions -

extension Array where Element == TriviaQuestion {
    static func from(json: String, using encoding: String.Encoding = .utf8) -> [PurpleTriviaQuestion]? {
        guard let data = json.data(using: encoding) else { return nil }
        return from(data: data)
    }

    static func from(data: Data) -> [TriviaQuestion]? {
        let decoder = JSONDecoder()
        return try? decoder.decode([TriviaQuestion].self, from: data)
    }

    static func from(url urlString: String) -> [TriviaQuestion]? {
        guard let url = URL(string: urlString) else { return nil }
        guard let data = try? Data(contentsOf: url) else { return nil }
        return from(data: data)
    }

    var jsonData: Data? {
        let encoder = JSONEncoder()
        return try? encoder.encode(self)
    }

    var jsonString: String? {
        guard let data = self.jsonData else { return nil }
        return String(data: data, encoding: .utf8)
    }
}

// MARK: Codable extensions -

extension TriviaQuestion {
    enum CodingKeys: String, CodingKey {
        case answer = "Answer"
        case category = "Category"
        case qID = "QID"
        case question = "Question"
        case stash = "Stash"
    }
}

Upvotes: 2

Oxthor
Oxthor

Reputation: 522

You mistyped the category attribute in the TriviaQuestion struct. You have categoroy but it should be category.

Upvotes: 1

Related Questions