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 打印了正确的信息,但似乎无法弄清楚为什么不显示这些值。
如有任何正确方向的指示,我们将不胜感激。谢谢
为了使其能够编译和工作,必须更改一些内容:
probeData
不应该是一个数组——它应该是一个可选的 属性
- 也就是说你解码的时候,你不应该把它放在里面
[ ]
- 然后,在您的列表中,您必须解决
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
视图模型——如果在异步调用期间查看重新加载。
最终目标:拥有一个 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 打印了正确的信息,但似乎无法弄清楚为什么不显示这些值。
如有任何正确方向的指示,我们将不胜感激。谢谢
为了使其能够编译和工作,必须更改一些内容:
probeData
不应该是一个数组——它应该是一个可选的 属性- 也就是说你解码的时候,你不应该把它放在里面
[ ]
- 然后,在您的列表中,您必须解决
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
视图模型——如果在异步调用期间查看重新加载。