SwiftUI中无法展示服务器返回的字典数据问题排查
SwiftUI请求数据成功但无法展示字典内容的问题排查与解决
问题描述
使用SwiftUI向服务器发起POST请求获取数据,请求本身正常,但无法在List视图中展示Result结构体里COURSE字典中的TITLE值。
现有代码
// an array of what comes back from the server struct Response: Codable { var results: [Result] } // this is what comes back from the server struct Result: Codable, Hashable { var STATUS_CODE : String var STATUS_MESSAGE : String var COURSE: [String : String] = ["ID" : "", "TITLE" : "", "DATE" : "", "DEPT" : "", "CATEGORY" : "", "COURSE_URL" : "", "COURSE_ID" : ""] } struct ContentView: View { @State private var results = [Result]() var body: some View { List(results, id: \.self) { item in VStack() { Text(item.COURSE["TITLE"] ?? "n/a") }// end VStack }// end List .task { await loadData() }// end task }// end some View func loadData() async { // set url guard let url = URL(string: "theurl.com") else { print("Invalid url") return } let json: [String: String] = ["ACTION" : "GET_COURSES"] let jsonData = try? JSONSerialization.data(withJSONObject: json) var request = URLRequest(url: url) request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.httpMethod = "POST" request.httpBody = jsonData // this is what does the call do { let (data, _) = try await URLSession.shared.data(for: request) if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) { results = decodedResponse.results } } catch { print("invalid data") } } }// end ContentView
问题分析
解码错误被隐藏:
try? JSONDecoder().decode(...)会将解码失败的错误转为nil,导致你无法得知具体的解码问题(比如字段名不匹配、COURSE结构不符等),即便请求成功,也可能因为解码失败导致results始终为空。COURSE默认值干扰解码:给COURSE字典设置默认值后,若服务器返回的COURSE字段结构与默认字典不完全匹配(比如键名大小写不一致、缺少某个键),会导致解码失败,且因为
try?的存在你无法察觉。Hashable实现不稳定:用
\.self作为List的id依赖Result的Hashable实现,而字典COURSE的Hash值会随内容变化,可能导致List刷新异常。
解决方案
1. 修复解码错误处理,暴露问题根源
将try?替换为try,并打印具体错误信息,快速定位解码失败原因:
do { let (data, _) = try await URLSession.shared.data(for: request) // 替换try?为try,捕获并打印解码错误 let decodedResponse = try JSONDecoder().decode(Response.self, from: data) results = decodedResponse.results } catch { print("请求或解码失败:\(error)") }
2. 移除COURSE默认值,或改用结构体替代字典
- 若服务器返回符合
[String:String]格式的COURSE,先移除默认值避免干扰解码:
struct Result: Codable, Hashable { var STATUS_CODE : String var STATUS_MESSAGE : String var COURSE: [String : String] // 移除默认值 }
- 更可靠的方式是将COURSE定义为独立结构体,避免字典键名拼写错误,提升代码可读性:
// 定义Course结构体对应服务器返回的COURSE对象 struct Course: Codable, Hashable { let ID: String let TITLE: String let DATE: String let DEPT: String let CATEGORY: String let COURSE_URL: String let COURSE_ID: String } struct Result: Codable, Hashable { var STATUS_CODE : String var STATUS_MESSAGE : String var COURSE: Course // 替换字典为结构体 }
此时List中的Text可以改为:
Text(item.COURSE.TITLE)
3. 优化List的id标识
避免用\.self作为List的id,改用COURSE中的唯一标识(比如COURSE_ID),提升列表稳定性:
// 若用字典: List(results, id: \.COURSE["COURSE_ID"] ?? "") { item in // ... } // 若用结构体: List(results, id: \.COURSE.COURSE_ID) { item in // ... }
4. 检查字段名大小写匹配
确保服务器返回的JSON字段名(如STATUS_CODE、TITLE)与你结构体中的属性名完全一致,Codable默认是大小写敏感的。如果服务器返回的是下划线命名(如status_code),可设置解码策略自动转换:
var decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase let decodedResponse = try decoder.decode(Response.self, from: data)
内容的提问来源于stack exchange,提问作者thejdah
相关产品推荐
相关产品推荐

