SwiftUI 解析和显示值形成一个 JSON 调用

SwiftUI Parsing and displaying values form a JSON call

最终目标:拥有一个 macOS 应用程序来监控我的蓝牙 bbq 探测器的温度值,每隔 X 分钟刷新一次数据,以便在我的办公桌上随时关注它。

macOS 应用程序

JSON结构

{
    "status": "OK",
    "statusCode": 200,
    "data": {
        "devices": [
            {
                "id": "xxxxxxxxxxxxxxxxx",
                "temperature": {
                    "internal": 25.8,
                    "ambient": 25.8
                },
                "cook": {
                    "id": "xxxxxxxxxxxxxxxxx",
                    "name": "Whole Chicken",
                    "state": "Configured",
                    "temperature": {
                        "target": 74,
                        "peak": 25.8
                    },
                    "time": {
                        "elapsed": 232,
                        "remaining": -1
                    }
                },
                "updated_at": 1622816968 
            }
        ]
    },
    "meta": {}
}

ContentView.swift

import SwiftUI

struct ContentView: View {

    @State var probeData = [Meater]()


    var body: some View {
        
        VStack {
            
              HStack{
            
            List{
                Text("Current Cook: \(probeData.Devices.cook.name)")
                    .font(.system(size: 20))
                    .fontWeight(.light)
                    .foregroundColor(.primary)
                
                Text("Target Temparature: \(probeData.Devices.cook.temperature.target) as String")
                    .font(.callout)
                    .fontWeight(.light)
                    .foregroundColor(.primary)
                Text("Internal Temperature: \(probeData.Devices.cook.temperature.internal) as String")
                    .font(.callout)
                    .fontWeight(.light)
                    .foregroundColor(.primary)   
                Text("Ambient Temperature: \(probeData.Devices.cook.temperature.ambient) as String")
                    .font(.callout)
                        .fontWeight(.light)
                        .foregroundColor(.primary)                      
            }

Text("Time Elapsed: \(probeData.Devices.cook.time.elapsed)")
                        .font(.system(size: 20))
                        .fontWeight(.light)
                        .foregroundColor(.primary)
                    
                    Text("Time Remaining: \(probeData.Devices.cook.time.remaining)")
                        .font(.callout)
                        .fontWeight(.light)
                        .foregroundColor(.primary)
          
        }
        }
    }

    //MARK: - Web Service

    func loadData() {
        
        let url = URL(string: "https://xxxxxxxx")
               var request = URLRequest(url: url!)
               request.httpMethod = "GET"
            request.addValue("Bearer xxxxxxxxxxxxxx", forHTTPHeaderField: "Authorization")

               let task = URLSession.shared.dataTask(with: request) { data, response, error in
               
                do {
                    if let d = data {
                        let decodedLists = try JSONDecoder().decode(Meater.self, from: d)

                       
                        DispatchQueue.main.async {

                            self.probeData = [decodedLists]
                            print(probeData)
                        }
                    }else {
                        print("No Data")
                    }
                }catch DecodingError.keyNotFound(let key, let context) {
                    Swift.print("could not find key \(key) in JSON: \(context.debugDescription)")
                } catch DecodingError.valueNotFound(let type, let context) {
                    Swift.print("could not find type \(type) in JSON: \(context.debugDescription)")
                } catch DecodingError.typeMismatch(let type, let context) {
                    Swift.print("type mismatch for type \(type) in JSON: \(context.debugDescription)")
                } catch DecodingError.dataCorrupted(let context) {
                    Swift.print("data found to be corrupted in JSON: \(context.debugDescription)")
                } catch let error as NSError {
                    NSLog("Error in read(from:ofType:) domain= \(error.domain), description= \(error.localizedDescription)")
                }
               }
               task.resume()

    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
            ContentView()
    }
}

// MARK: - Data Model

struct Meater: Codable {
  struct Data: Codable {
    struct Devices: Codable {
      struct Temperature: Codable {
        let `internal`: Double
        let ambient: Double
      }

      struct Cook: Codable {
        struct Temperature: Codable {
          let target: Int
          let peak: Double
        }

        struct Time: Codable {
          let elapsed: Int
          let remaining: Int
        }

        let id: String
        let name: String
        let state: String
        let temperature: Temperature
        let time: Time
      }

      let id: String
      let temperature: Temperature
      let cook: Cook
      let updatedAt: Date

      private enum CodingKeys: String, CodingKey {
        case id
        case temperature
        case cook
        case updatedAt = "updated_at"
      }
    }

    let devices: [Devices]
  }

  struct Meta: Codable {
  }

  let status: String
  let statusCode: Int
  let data: Data
  let meta: Meta
}

调试器似乎从 probeData 打印了正确的信息,但似乎无法弄清楚为什么不显示这些值。

如有任何正确方向的指示,我们将不胜感激。谢谢

为了使其能够编译和工作,必须更改一些内容:

  1. probeData 不应该是一个数组——它应该是一个可选的 属性
  2. 也就是说你解码的时候,你不应该把它放在里面[ ]
  3. 然后,在您的列表中,您必须解决 devices 属性 中的每个项目(参见 ForEach
struct ContentView: View {
    
    @State var probeData : Meater? //<-- Here
    
    var body: some View {
        VStack {
            HStack{
                List {
                    ForEach(probeData?.data.devices ?? [], id: \.id) { item in //<-- Here
                        Text("Current Cook: \(item.cook.name)")
                            .font(.system(size: 20))
                            .fontWeight(.light)
                            .foregroundColor(.primary)
                        
                        Text("Target Temparature: \(item.cook.temperature.target)")
                            .font(.callout)
                            .fontWeight(.light)
                            .foregroundColor(.primary)
                        Text("Internal Temperature: \(item.temperature.internal)")
                            .font(.callout)
                            .fontWeight(.light)
                            .foregroundColor(.primary)
                        Text("Ambient Temperature: \(item.temperature.ambient)")
                            .font(.callout)
                            .fontWeight(.light)
                            .foregroundColor(.primary)
                        
                        Text("Time Elapsed: \(item.cook.time.elapsed)")
                            .font(.system(size: 20))
                            .fontWeight(.light)
                            .foregroundColor(.primary)
        
                        Text("Time Remaining: \(item.cook.time.remaining)")
                            .font(.callout)
                            .fontWeight(.light)
                            .foregroundColor(.primary)
                    }
                }
            }
        }
    }
    
    //MARK: - Web Service
    
    func loadData() {
        
        let url = URL(string: "https://xxxxxxxx")
        var request = URLRequest(url: url!)
        request.httpMethod = "GET"
        request.addValue("Bearer xxxxxxxxxxxxxx", forHTTPHeaderField: "Authorization")
        
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            
            do {
                if let d = data {
                    let decodedLists = try JSONDecoder().decode(Meater.self, from: d)
                    
                    
                    DispatchQueue.main.async {
                        self.probeData = decodedLists //<-- Here
                        print(probeData)
                    }
                }else {
                    print("No Data")
                }
            }catch DecodingError.keyNotFound(let key, let context) {
                Swift.print("could not find key \(key) in JSON: \(context.debugDescription)")
            } catch DecodingError.valueNotFound(let type, let context) {
                Swift.print("could not find type \(type) in JSON: \(context.debugDescription)")
            } catch DecodingError.typeMismatch(let type, let context) {
                Swift.print("type mismatch for type \(type) in JSON: \(context.debugDescription)")
            } catch DecodingError.dataCorrupted(let context) {
                Swift.print("data found to be corrupted in JSON: \(context.debugDescription)")
            } catch let error as NSError {
                NSLog("Error in read(from:ofType:) domain= \(error.domain), description= \(error.localizedDescription)")
            }
        }
        task.resume()
        
    }
    
}

一般来说,我还建议将您的 loadData 函数移动到 ObservableObject 视图模型——如果在异步调用期间查看重新加载。