SwiftUI解码PHP JSON API并在ScrollView显示文本的问题
SwiftUI JSON解码与数据展示问题修复方案
核心问题梳理
- 结构体解码逻辑错误:原
CallData错误将records解码为[String:[String]],但接口返回的records是字典数组;Record未遵循Decodable协议,字段设计也未匹配JSON结构。 - 视图与数据模型未关联:
SecondTabView自行定义了gmodel数组,但未绑定ContentModel的@Published数据,导致接口数据无法传递到视图。 - 错误处理不完善:未处理
data为空、URL无效、响应状态码异常等情况,难以排查请求失败原因。
1. 修正解码结构体
严格匹配接口返回的JSON结构,使用CodingKeys处理蛇形命名到驼峰命名的映射:
// 匹配JSON根结构 struct CallData: Decodable { let records: [Record] } // 匹配单个记录的JSON结构,遵循Identifiable便于SwiftUI遍历 struct Record: Decodable, Identifiable { let id: String let stopName: String let stopTime: String private enum CodingKeys: String, CodingKey { case id = "ID" case stopName = "stop_name" case stopTime = "stop_time" } }
2. 修正数据模型类(ContentModel)
完善错误处理逻辑,确保数据正确解析并更新到视图:
class ContentModel: ObservableObject { @Published var records = [Record]() init() { getData() } func getData() { guard let url = URL(string: "https://not.the.real.php?route=Green") else { print("无效的URL") return } var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 10) request.httpMethod = "GET" URLSession.shared.dataTask(with: request) { [weak self] data, response, error in // 处理网络错误 if let error = error { print("网络请求错误: \(error.localizedDescription)") return } // 处理数据为空的情况 guard let data = data else { print("接口未返回数据") return } // 校验响应状态码 if let httpResponse = response as? HTTPURLResponse, !(200...299).contains(httpResponse.statusCode) { print("接口响应错误,状态码: \(httpResponse.statusCode)") return } do { let decoder = JSONDecoder() let result = try decoder.decode(CallData.self, from: data) // 按ID排序(可选,根据需求调整) let sortedRecords = result.records.sorted { $0.id < $1.id } DispatchQueue.main.async { self?.records = sortedRecords } } catch { print("JSON解码错误: \(error.localizedDescription)") print("错误详情: \(error)") } }.resume() } }
3. 修正SecondTabView视图
绑定数据模型,直接遍历展示所需字段:
struct SecondTabView: View { @Namespace var mapScope // 使用@StateObject绑定数据模型,确保视图响应数据变化 @StateObject private var model = ContentModel() var body: some View { VStack { // 展示站点名称与停留时间 ScrollView { VStack(spacing: 16) { ForEach(model.records) { record in VStack(alignment: .leading) { Text("站点名称: \(record.stopName)") .font(.headline) Text("停留时间: \(record.stopTime)分钟") .font(.subheadline) .foregroundColor(.secondary) } .padding(.horizontal) .frame(maxWidth: .infinity, alignment: .leading) .border(.gray.opacity(0.2), cornerRadius: 8) .padding(.horizontal) } } } Map(scope: mapScope) { Marker("Here", coordinate: .Youtoo) } .mapStyle(.hybrid(elevation: .realistic)) MapUserLocationButton(scope: mapScope).tint(.red) } .mapScope(mapScope) } } // 补充自定义坐标扩展(如果需要) extension CLLocationCoordinate2D { static let Youtoo = CLLocationCoordinate2D(latitude: -11.11, longitude: 11.11) }
内容的提问来源于stack exchange,提问作者HKelz
相关产品推荐
相关产品推荐

