am trying to load this json into my swiftui app
{
"id": "EF1CC5BB-4785-4M8E-AB98-5FA4E00B6A66",
"name" : "opening the box",
"group" : [
{
"name" : "Open box",
"windows" : "Double-click",
"mac" : "right-click"
},
{
"name" : "Duplicate",
"windows" : "right click and choose from options",
"mac" : "option + drag"
}
]
},
and there are many more elements like this
and here are the structs that I made
struct topic: Codable, Identifiable {
var id: UUID
var name: String
var group: [shortcuts]
}
struct shortcuts: Codable, Equatable, Identifiable{
var id = UUID()
var name: String
var windows: String
var mac: String
}
and here is the code for decoding it
import UIKit
extension Bundle {
func decode<T: Decodable>(_ type: T.Type, from file: String) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("failed")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("failed to do it")
}
let decoder = JSONDecoder()
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("failed")
}
return loaded
}
}
now when I create a constant like this inside my view
let Topic = Bundle.main.decode([topic].self, from: "main.json")
and then I try to load the items like this
NavigationView{
List{
ForEach(Topic) { section in
Section(header: Text(section.name)) {
ForEach(section.group) { item in
shortcutRow(item: item)
}
}
}
}.navigationBarTitle("Menu")
.listStyle(SidebarListStyle())
}
it doesn't work it shows fatal error that is present in the second last line of code of my bundle extension that I created for decoding this json
please help
"...and there are many more elements like this", that means the json you show
is probably not the json you have. Most likely it is an array of what you show.
Thus, in Bundle decode, you should decode and return [T] not T.
Note you should use the normal convention of using uppercased name for types,
such as Topic, and lowercased name for instances, topic. Given that, try this:
extension Bundle {
// todo deal with errors
func decode<T: Decodable>(from file: String) -> [T] { // <-- here
if let url = Bundle.main.url(forResource: file, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let loaded = try JSONDecoder().decode([T].self, from: data) // <-- here
return loaded
} catch {
// todo deal with errors
print("---> error: \(error)")
}
} else {
// todo deal with errors
print("---> error url: ")
}
return []
}
}
struct Topic: Codable, Identifiable {
var id, name: String
var group: [Shortcuts]
}
struct Shortcuts: Codable, Identifiable {
let id = UUID() // <-- here use let
var name, windows, mac: String
}
struct ContentView: View {
let topics: [Topic] = Bundle.main.decode(from: "main") // <-- here
var body: some View {
Text("testing topics: \(topics.count)")
.onAppear {
print("---> topics: \(topics)")
}
}
}
EDIT:
This is the json data I used to test my answer:
[
{
"id": "EF1CC5BB-4785-4M8E-AB98-5FA4E00B6A66",
"name" : "opening the box",
"group" : [
{
"name" : "Open box",
"windows" : "Double-click",
"mac" : "right-click"
},
{
"name" : "Duplicate",
"windows" : "right click and choose from options",
"mac" : "option + drag"
}
]
}
]
Related
So, new to swift and was able to peice this together by reading and watching videos however i reached a point after countless hours searching for a solution..
Basically what the app does is scan's a qr code, parses the url it reads from the qr code to get a key, then I am appending that key to the api url, and i want to output the results from the api to the screen. however I am receiving an error Type '()' cannot conform to 'View' in xcode
Here is sample json data
[
{
"id": "160468",
"sport": "BASKETBALL",
"year": "2020",
"brand": "PANINI PRIZM",
"cardNumber": "278",
"playerName": "LaMELO BALL",
"extra": "",
"gradeName": "MINT",
"grade": "9",
"serial": "63585906",
"authDate": "1656406800",
"link": "https://www.example.com/certificate-verification?certificateNumber=63585906"
}
]
here is my contentview
import SwiftUI
import CodeScanner
extension URL {
var components: URLComponents? {
return URLComponents(url: self, resolvingAgainstBaseURL: false)
}
}
extension Array where Iterator.Element == URLQueryItem {
subscript(_ key: String) -> String? {
return first(where: { $0.name == key })?.value
}
}
struct Card: Decodable {
let sport: String
let year: String
let brand: String
let cardNumber: String
let playerName: String
let extra: String
let gradeName: String
let grade: String
let serial: String
}
struct ContentView: View {
#State var isPresentingScanner = false
#State var scannedCode: String = ""
var scannerSheet : some View {
CodeScannerView(
codeTypes: [.qr],
completion: { result in
if case let .success(code) = result {
self.scannedCode = code.string
self.isPresentingScanner = false
}
}
)
}
func getQueryStringParameter(url: String, param: String) -> String? {
guard let url = URLComponents(string: url) else { return nil }
return url.queryItems?.first(where: { $0.name == param })?.value
}
var body: some View {
VStack(spacing: 10) {
Image("logo-white")
.offset(y: -200)
if let urlComponents = URL(string: scannedCode)?.components,
let cert = urlComponents.queryItems?["certificateNumber"] {
//Text(cert)
let apihit = URL(string: "https://app.example.com/api.php?apikey=xxx&cert=\(cert)")!
//Text(apihit.absoluteString)
var request = URLRequest(url: apihit)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: apihit) { data, response, error in
if let data = data {
if let cards = try? JSONDecoder().decode([Card].self, from: data) {
print(cards)
} else {
print("Invalid Response")
}
} else if let error = error {
print("HTTP Request Failed \(error)")
}
}
}
Button("Scan QR Code") {
self.isPresentingScanner = true
}
.padding()
.background(Color(red: 0, green: 0, blue: 0.5))
.foregroundColor(.white)
.clipShape(Rectangle())
.cornerRadius(20)
.sheet(isPresented: $isPresentingScanner) {
self.scannerSheet
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.gray)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I have tried countless tutorials online, however none of them show how to do it within the view, which I believe is where it belongs because I don't get the actual json url until after I read the qr code..
To populate a link within a view, use Webkit.
First you need to import Webkit. Then, create a WebView which conforms to the UIViewRepresentable protocol.
Here is the Apple documentation. This is the easiest way to integrate web content into your app.
I hope this helps!
Any pointers would be much appreciated. No build or run errors with the code below, but 239 "Fail" prints in the Console. Also, once I get it to work properly, this code should be in a separate function or extension and not part of the View, and for reusability. I am expecting to see a country's flag next to its name in a Picker listing. The 'country.name' displays and 'selectedCountry' can be selected for subsequent Core Data storage, but sadly no flag displays. The relevant code and a sample "Country" from the JSON file below:
In the Properties section:
let countryImage: [Country] = Bundle.main.decode("Countries.json")
#State private var selectedContact: [ContactEntity] = []
let flag = "flag"
In the View:
VStack {
Picker(selection: $selectedCountry,
label: Text("Host Country:")) {
ForEach(countryImage) { country in
HStack {
Text(country.name).tag(country.name)
// TODO - Find Flag
if case country.flag = Data(base64Encoded: flag), let uiImage = UIImage(data: country.flag) {
Image(uiImage: uiImage).tag(country.flag)
} else {
let _ = print("Fail")
}
//Image(flag)
}
}
}
.padding(.horizontal, 0)
.padding()
.pickerStyle(MenuPickerStyle())
}//: INNER PICKER VSTACK
The Bundle references an extension:
extension Bundle {
func decode<T: Codable>(_ file: String) -> T { // "_" obviates the need to call the name of the parameter
// 1. Locate the JSON file
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
// 2. Create a property for the data
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
// 3. Create a decoder
let decoder = JSONDecoder()
// 4. Create a property for the decoded data
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
// 5. Return the ready-to-use data
return loaded
}
}
Sample JSON item:
{
"id": 202,
"name": "Sudan",
"isoAlpha2": "SD",
"isoAlpha3": "SDN",
"isoNumeric": 736,
"currency": {
"code": "SDD",
"name": "Dinar",
"symbol": false
},
"flag": "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAUCAYAAACaq43EAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozNjc2Q0MxMjE3ODgxMUUyQTcxNDlDNEFCRkNENzc2NiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozNjc2Q0MxMzE3ODgxMUUyQTcxNDlDNEFCRkNENzc2NiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkY1RTdDNTBCMTc4NzExRTJBNzE0OUM0QUJGQ0Q3NzY2IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkY1RTdDNTBDMTc4NzExRTJBNzE0OUM0QUJGQ0Q3NzY2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+dsEThQAAAfVJREFUeNq8lT9oE1Ecxz/v3ZmkMae2xdbuiqRiqaugKDo5WFGQOiiimy5aR6dOOrhYHRx0qQoVaQOCbgpO/oHatTrVhCYNtIlJbWJJ7p6/C7aiVhfv5Qf3jrt33Of9vt/f7z118sTe+TvPE15XQzHbGaABZbAeihu7zc6sw71MB0cWXGY9n28uOJbhDgM910q9QXx8oEFnVXMsu6mVcSVuF64lZ1jUrYcrp2sMD9XwBbinpPFlzihb4LVxRQhlzdP9qxy6sMKbHT6DZYdYk9YCovd4JF2Vu7f+JpS3O4BVuPUiydXpBPMUKLLc+tweOAzhkpIVxJsMv1bm/rZTfrK7y2QrBWUi8t39qwGh9HWHiX1l9bF/znl8/rpJk1LReryxFiK7I0MPMy8nVf+lXfrhh2fqx8x/X/rf6xJdfR/60tAMOHdziMNjZyguL1rM+I+OF1caUKpXpd2MJY9/BxY/QSzJ3ZFxLh84a7G41toq3MJ0haMHj5tHFx+Y3giLa2NwIMDNMpWSje1V0WzJav/2zKjJfVlQgQksgUPo9oT4KYBMHvO+rKaYcKPeuX7+MCwYR2qtrwPmvsJUDgp1a4eE+4u0W2PwVlplMmv9PHbXpZU+5clnmF6iHeGKtB65mkgrWebrtCtc3i3lyeQ8jKGd8V2AAQDj9qv7QrTRKwAAAABJRU5ErkJggg=="
},
It's easier than expected. Decodable can decode a base64-encoded String directly as Data
In Country declare flag
let flag: Data?
and a computed property
var flagImage : UIImage? {
guard let flagData = flag else { return nil }
return UIImage(data: flagData)
}
And display the image
if let flagImage = country.flagImage {
Image(uiImage: flagImage).tag(country.flag)
} else {
let _ = print("Fail")
}
Having trouble figuring out why code won’t display default value when variable is nil. Here’s the context below. Any pointers would be greatly appreciated.
Thanks!
EXAMPLE OF DATA FROM JSON API:
NOTE: image_url is just the base name, not the full path or file extension.
[
{
"id": 1,
"title": "Autumn in New York",
"image_url": ""
}
]
DATA MODEL:
import Foundation
struct Challenge: Codable, Hashable, Identifiable {
let id: Int
let title: String
let imageURL: String?
private enum CodingKeys: String, CodingKey {
case id
case title
case imageURL = "image_url"
}
}
CODE FOR VIEW AND VIEW MODEL:
import SwiftUI
struct JSONChallengeV2: View {
#State private var challenge: [Challenge] = []
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 5) {
ScrollView(.horizontal, showsIndicators: true) {
HStack() {
ForEach (challenge) { challenge in
NavigationLink(
destination:
PlayerView(),
label: {
// PROBLEMS OCCUR IN THIS VIEW (see view code below)
JSONChallengeRowView(challenge: challenge)
})
}
}
}
}
.onAppear {
getData()
}
}
}
func getData() {
let url = URL(string: "https://example.com/jsonapi") // EXAMPLE ONLY
URLSession.shared.dataTask(with: url!) { (data, response, error) in
guard error == nil else {
print(error?.localizedDescription ?? "")
return
}
guard data != nil else {
print("No data")
return
}
let decoder = JSONDecoder()
do {
let loaded = try decoder.decode([Challenge].self, from: data!)
challenge = loaded
} catch {
print("Can't decode data")
}
}.resume()
}
}
CODE FOR SUB-VIEW ("JSONChallengeRowView" referenced in above view):
import SwiftUI
struct JSONChallengeRowView: View {
var challenge: Challenge
var body: some View {
let thumbnailPrefix = "https://example.com/" // EXAMPLE ONLY
let thumbnailSuffix = "-001.jpg"
VStack(alignment: .leading) {
// WORKS: Hardcoding a known image base (i.e., "autumn-default":
RemoteImageView(url: ("\(thumbnailPrefix)\(String(describing: "autumn-default"))\(thumbnailSuffix)"))
.scaledToFit()
.cornerRadius(10)
Link("Go", destination: (URL(string: "\(thumbnailPrefix)\("autumn-default")\(thumbnailSuffix)") ?? URL(string: "https://google.com"))!)
// DOESN'T WORK: build succeeds but no default image appears when no "imageURL" value can be found:
RemoteImageView(url: ("\(thumbnailPrefix)\(String(describing: challenge.imageURL ?? "autumn-default" ))\(thumbnailSuffix)"))
.scaledToFit()
.cornerRadius(10)
Link("Go", destination: URL(string: "\(thumbnailPrefix)\(String(describing: challenge.imageURL ?? "autumn-default"))\(thumbnailSuffix)")!)
// AND WHILE THESE WORK:
Text("\(challenge.title)")
Text(challenge.title)
// THESE SIMILARLY DISPLAY NOTHING (despite values in the "title" variable, used as a default value here for testing only):
Text("\(challenge.imageURL ?? challenge.title)")
Text(challenge.imageURL ?? challenge.title)
}
}
}
Nick's point about image_url containing an empty string, not nil, pointed me in the right direction, and with a tip from my Swift learner's forum elsewhere, I used this modification to fix the sections that weren't working:
challenge.imageURL.isEmpty ? "autumn-default" : "\(challenge.imageURL)"
For example, here:
RemoteImageView(url: "\(thumbnailPrefix)" + (challenge.imageURL.isEmpty ? "autumn-default" : "\(challenge.imageURL)") + "\(thumbnailInfix)" + (challenge.imageURL.isEmpty ? "autumn-default" : "\(challenge.imageURL)") + "\(thumbnailSuffix)")
I also applied this syntax to the Link and Text views successfully.
Thanks again, everyone!
Scenario: I want to use a common function to access various endpoints. So I'm trying to create boilerplate code to process various data models (struct.self) with their associated URLs.
Here are a couple of models that share a common URLSession + decode code:
// MARK: - Region
struct Region: Codable {
let country: String
let subregions: [String]
}
// MARK: - SubRegion
struct SubRegion: Codable {
let country: String
let subregion: Sub
let data: [Datum]
}
Here's one data vector (Region) that contains a URL with its associated data model:
struct URLDataModel {
var url = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")
var dataModel: Any = Region.self
}
The following is the entire class that contains the shared getRegionList() using the URLDataModel struct:
class CountryRegionListModelView: ObservableObject {
#Published var countryRegionList = [String]()
// Data Persistence:
var cancellables: Set<AnyCancellable> = []
// TODO: --- Get Region tag ---
func getRegionList(urlDataModel: URLDataModel) {
// MARK: - Region
struct Country: Codable {
let countries: [String]
}
struct Region: Codable {
let country: String
let subregions: [String]
}
print("url: ", urlDataModel.url)
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: urlDataModel.url!)
// the dataTaskPublisher output combination is (data: Data, response: URLResponse)
.map(\.data)
.receive(on: DispatchQueue.main)
.print("Hello Data")
.decode(type: urlDataModel.dataModel, decoder: JSONDecoder())
remoteDataPublisher
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
break
case let .failure(anError):
Swift.print("received error: ", anError)
}
}, receiveValue: { someValue in
self.countryRegionList = someValue.subregions
print(self.countryRegionList)
}).store(in: &cancellables)
}
}
Problem: I don't know how to make the JSON decoder work with a variable model type 'model'.self.
Should I use generics?
How would that work?
You can create a protocol:
protocol URLResource {
associatedtype DataModel: Decodable
var url: URL? { get }
}
and its sample implementation:
struct CovidResource: URLResource {
typealias DataModel = Region
var url = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")
}
Then you can make getRegionList generic and accept some URLResource:
func getRegionList<Resource>(urlDataModel: Resource) where Resource: URLResource {
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: resource.url!)
// the dataTaskPublisher output combination is (data: Data, response: URLResponse)
.map(\.data)
.receive(on: DispatchQueue.main)
.decode(type: Resource.DataModel.self, decoder: JSONDecoder())
// ...
}
I FINALLY got this to work; via Storyboard:
import Combine
import SwiftUI
protocol URLResource {
associatedtype DataModel: Decodable
var url: URL? { get }
}
var cancellables: Set<AnyCancellable> = []
struct CovidResource<T:Decodable>: URLResource {
typealias DataModel = T
var url = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")
}
// MARK: - Canada
struct Canada: Codable {
let country: String
let subregions: [String]
}
let myURL = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")!
func getRegionList<Resource>(urlDataModel: Resource) where Resource: URLResource {
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: urlDataModel.url!)
.map(\.data)
.receive(on: DispatchQueue.main)
.decode(type: Resource.DataModel.self, decoder: JSONDecoder())
.print("Remote Publisher \(urlDataModel.url!): ")
remoteDataPublisher
.sink(receiveCompletion: { completion in
print(completion)
switch completion {
case .finished:
print("Publisher Finished")
break
case let .failure(anError):
Swift.print("received error: ", anError)
}
}, receiveValue: { someValue in
print("someValue: ", someValue)
}).store(in: &cancellables)
}
// ---------------------------------------
let resource = CovidResource<Canada>(url: myURL)
getRegionList(urlDataModel: resource)
print("End of Proof-of-Concept")
print("The End.")
Console Output:
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : receive subscription: (Decode)
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : request unlimited
End of Proof-of-Concept
The End.
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : receive value: (Canada(country: "Canada", subregions: ["Alberta", "Calgary", "Edmonton", "British Columbia", "Vancouver", "Manitoba", "New Brunswick", "Newfoundland and Labrador", "Northwest Territories", "Halifax", "Nova Scotia", "Ontario", "Ottawa", "Toronto", "Prince Edward Island", "Montreal", "Quebec", "Saskatchewan", "All", "Yukon Territory"]))
someValue: Canada(country: "Canada", subregions: ["Alberta", "Calgary", "Edmonton", "British Columbia", "Vancouver", "Manitoba", "New Brunswick", "Newfoundland and Labrador", "Northwest Territories", "Halifax", "Nova Scotia", "Ontario", "Ottawa", "Toronto", "Prince Edward Island", "Montreal", "Quebec", "Saskatchewan", "All", "Yukon Territory"])
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : receive finished
finished
Publisher Finished
This question already has answers here:
Can comments be used in JSON?
(58 answers)
Closed 4 years ago.
I am trying to pass some data from a JSON file using the new(ish) codeable capability in Swift. I have used the below syntax before without issue. I believe I may have something set up wrong, however, as I can't seem to understand why I keep receiving the below message when the JSON format has been approved by a JSON parser.
The error message:
error:dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))
The code in my QuestionFactory file...
class QuestionFactory {
func parseJSON(filename fileName: String) -> Quiz? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
print(url)
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
print("data received is \(data.count) bytes:\n\(data)")
print(data)
print(data as NSData)
let jsonData = try decoder.decode(Quiz.self, from: data)
print(jsonData)
} catch {
print("error:\(error)")
}
}
return nil
}
}
The code in my initial ViewController:
class LaunchScreen: UIViewController {
private var quiz: Quiz?
private let jsonFileName = "QuizData"
func viewDidLoad() {
super.viewDidLoad()
createQuiz()
}
private func createQuiz() {
let questionFactory = QuestionFactory()
guard let parsedQuiz = questionFactory.parseJSON(filename: jsonFileName) else {
print("Error creating quiz")
return
}
quiz = parsedQuiz
}
func movesToMainMenuScreen() {
let transition = CATransition()
transition.duration = 1.5
transition.type = kCATransitionFade
self.navigationController?.view.layer.add(transition, forKey:nil)
let mainMenuVC: UIViewController = MainMenuViewController(quiz: quiz!) >> I am receiving an error here as well, perhaps due to my mainMenuVC's required init?
navigationController?.pushViewController(mainMenuVC, animated: false)
}
In my mainMenuViewController:
class mainMenuViewController: UIViewController {
private var quiz: Quiz! {
didSet {
tableViewAdapter = AnswerTableViewAdapter(answers: quiz.questions[0].answers) >> Although, it is not obviously reaching this far to read through the JSON.
}
required init(quiz: Quiz) {
super.init(nibName: nil, bundle: nil)
defer {
self.quiz = quiz
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The JSON looks like this...
{
"questions":[
{
"text": "1. Where will the 2022 World cup be held?",
"answers": [
{
"text": "Qatar",
"isCorrect": true,
"answerType": "2"
},
{
"text": "دولة قطر",
"isCorrect": true,
"answerType": "1"
},
{
"text": "Jamaica",
"isCorrect": false,
"answerType": "0"
},
{
"image":"qatarFlag",
"isCorrect": true,
"answerType": "3"
}
]
}]
}
The Model files....
Quiz.swift
import Foundation
struct Quiz: Decodable {
var questions: [Question]
}
Question.swift
import Foundation
struct Question: Decodable {
var text: String
var answers: [Answer]
}
Answer.swift
import Foundation
struct Answer: Decodable {
var text: String
var image: String
var isCorrect: Bool
var answerType: String
}
There is extra
]
}
added on last two lines remove these both closing brackets and try to parse JSON.
And make your model's properties to optional to avoid nullable crashes.
The Model files....
Quiz.swift
import Foundation
struct Quiz: Decodable {
var questions: [Question]?
}
Question.swift
import Foundation
struct Question: Decodable {
var text: String?
var answers: [Answer]?
}
Answer.swift
import Foundation
struct Answer: Decodable {
var text: String?
var image: String?
var isCorrect: Bool?
var answerType: String?
}