You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题分析

  1. 解码错误被隐藏:try? JSONDecoder().decode(...)会将解码失败的错误转为nil,导致你无法得知具体的解码问题(比如字段名不匹配、COURSE结构不符等),即便请求成功,也可能因为解码失败导致results始终为空。

  2. COURSE默认值干扰解码:给COURSE字典设置默认值后,若服务器返回的COURSE字段结构与默认字典不完全匹配(比如键名大小写不一致、缺少某个键),会导致解码失败,且因为try?的存在你无法察觉。

  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 23:40:40