TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

Reading JSON after successful decoding

Forums > SwiftUI

Hi everyone, I need help pulling individual data out of a JSON file I made for an app I am building. The JSON might not be set up ideally, so please feel free to comment on that, too. (I eventually want to filter the meridian element chosen and then pass the point name to my UI, as well as eventually showing a photo + description in a dedicated view)

I am able to successfully decode the JSON file, so when I pull up the count I get the right number (12), but I cant seem to figure out how to get to the individual data points. I am linking my GitHub repo with all of the files, and pasting the relevant parts of my code here.

Please help, I have been stuck on this for weeks, I redid the Moonshot app 3 times, and have read anything I could on working with JSONs, but I can't seem to get unstuck, and the more I read, the more I dig myself in this rut.

Meridian and Acupoint Structs + ContentView

struct Meridian: Codable, Identifiable {
    let id: String
    let yinYang: String
    let points: [Acupoint]
}

struct Acupoint: Codable, Identifiable {
    let id: String
    let name: String
    let level: Int?
    let element: String
    let description: String
}

struct ContentView: View {

    let meridians: [String: Meridian] = Bundle.main.decode("meridiansNacupoints.json")

    var body: some View {
        VStack(alignment: .leading){
            Text("Total meridians: \(meridians.count)") // currently correctly prints "12"
            Text("First meridian: ") // should print -> "Liver"
            Text("Acupoints on meridian: ") // liver.points.count, should print ->  "6"
            Text("Element of meridian: ") // should print -> "wood"
            HStack {
                VStack(alignment: .leading) {
                    Text("Level 2 point: ")
                }
                VStack(alignment: .trailing) {
                    Text("-") // name, should print ->  "Liv-2"
                    Text("-") // element, should print ->  "fire"
                    Text("-") // description, should print ->  "Description"
                }
            }
        }
        .padding()
    }
}

JSON (shortened for convenience's sake)

{
  "liver": {
    "id": "liver",
    "element": "wood",
    "yinYang": "yin",
    "points": [
      {
        "id": "LIV-1",
        "name": "LIV-1",
        "level": 1,
        "element": "wood",
        "description": "Description"
      },
      {
        "id": "LIV-2",
        "name": "LIV-2",
        "level": 2,
        "element": "fire",
        "description": "Description"
      },
      ... more points here with th same format...
    ]
  },
  ... more meridians here with the same format ...
  }

3      

While you have used a Dictionary to decode and works for you to get field out you have to do

meridians["liver", default: Meridian(id: "Not Known", yinYang: "yinYang", element: "Unknown", points: [])]
struct ContentView: View {

    let meridians: [String: Meridian] = Bundle.main.decode(from: "meridiansNacupoints.json")

    var body: some View {
        VStack(alignment: .leading, spacing: 10){
            let liver = meridians["liver", default: Meridian(id: "Not Known", yinYang: "yinYang", element: "Unknown", points: [])] // Added this to make each call smaller

            Text("Total meridians: \(meridians.count)") // "12"
            Text("First meridian: \(liver.id)") // "Liver"
            Text("Acupoints on meridian: \(liver.points.count)") // liver.points.count -> "6"
            Text("Element of meridian: \(liver.element)") // "wood"

            HStack {
                VStack(alignment: .leading) {
                    Text("Level 2 point: ")
                }
                VStack(alignment: .trailing) {
                    Text(liver.points[1].id) // name -> "Liv-2"
                    Text(liver.points[1].element) // element -> "fire"
                    Text(liver.points[1].description) // description -> "Description"
                }
            }
        }
        .padding()
    }
}

or you can add this to Meridian

static let defaultMeridian = Meridian(id: "Unknown", yinYang: "yinYang", element: "element", points: [])

then use it

Text("First meridian: \(meridians["liver", default: .defaultMeridian].id)") // "Liver"

however you would be better off doing then add the each one as needed. This will be eliminate the uses of String keys and default

struct Meridian: Codable {
    var liver: Liver
//    var gallbladder: Gallbladder
//    var heart: Heart
//    var smallintestine: Smallintestine
//    var pericardium: Pericardium
//    var tripleheater: Tripleheater
//    var spleen: Spleen
//    var stomach: Stomach
//    var lung: Lung
//    var largeintestine: Largeintestine
//    var kidney: Kidney
//    var bladder: Bladder
}
struct Liver: Codable {
    var id: String
    var element: String
    var yinYang: String
    var points: [Acupoint]
}

and then

struct ContentView: View {

    let meridian: Meridian = Bundle.main.decode(from: "meridiansNacupoints.json")

    var body: some View {
        List {

            Text("Total meridians: NOT AN ARRAY") // "12"
            Text("First meridian: \(meridian.liver.id)") // "Liver"
            Text("Acupoints on meridian: \(meridian.liver.points.count)") // liver.points.count -> "6"
            Text("Element of meridian: \(meridian.liver.element)") // "wood"

            HStack {
                VStack(alignment: .leading) {
                    Text("Level 2 point: ")
                }
                VStack(alignment: .trailing) {
                    Text(meridian.liver.points[1].id) // name -> "Liv-2"
                    Text(meridian.liver.points[1].element) // element -> "fire"
                    Text(meridian.liver.points[1].description) // description -> "Description"
                }
            }

            ForEach(meridian.liver.points) { point in
                HStack {
                    Text(point.name)
                    Text(point.element)
                    Text(point.description)
                }
            }
        }
        .padding()
    }
}

PS careful as in Liver json you have two IDs the same, this will cause expected results

3      

Hacking with Swift is sponsored by Blaze.

SPONSORED Still waiting on your CI build? Speed it up ~3x with Blaze - change one line, pay less, keep your existing GitHub workflows. First 25 HWS readers to use code HACKING at checkout get 50% off the first year. Try it now for free!

Reserve your spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.