You need to enable JavaScript to run this app. Swift中Codable对象转JSON提交POST请求出现无效类型写入错误
优惠活动
大模型
产品
解决方案
定价
更多

Swift中Codable对象转JSON提交POST请求出现无效类型写入错误

崩溃根因定位
  • 触发崩溃的直接原因:JSONSerialization.data(withJSONObject:) 仅支持可桥接到Objective-C的原生JSON兼容类型(包含NSString、NSNumber、NSArray、NSDictionary、NSNull)。你传入的参数字典中存储了自定义Swift结构体Space,这个值在桥接给Objective-C侧的序列化逻辑时,会被包装成__SwiftValue类型,属于序列化流程完全不识别的类型,因此直接抛出NSInvalidArgumentException中断进程。
  • 代码层面的设计缺陷:
    • 方法签名中把请求参数类型定义为[String: Codable]本身存在兼容问题:Codable是带关联类型的协议,作为存在类型使用时,无法将内部存储的自定义模型值正确传递给JSON序列化流程,从根源上不适合直接作为JSONSerialization的输入。
    • 你已经为Space结构体实现了Codable协议,却没有使用系统原生的JSONEncoder完成模型序列化,反而依赖能力受限的JSONSerialization处理自定义类型,相当于绕开了已经准备好的类型安全的序列化路径。
    • 原代码存在两个隐藏的功能bug:
      • 创建的URLSessionDataTask没有调用resume()方法,就算修复了序列化崩溃问题,网络请求也根本不会实际发出。
      • POST请求没有设置Content-Type: application/json请求头,即使序列化成功,后端服务也可能无法正确识别请求体的格式。
修复后的可运行实现

1. 重写API请求方法

将原方法调整为支持任意Encodable类型的参数,通过JSONEncoder完成序列化,从根源上避免类型不兼容的问题:

class Api {
    static let spacePostUrl = "http://localhost:3001/spaces"
    static let spaceGetUrl = "http://localhost:3001/"
    
    static func postRequest<T: Encodable>(param: T, urlString: String, completion: @escaping (Int) -> ()) {
        guard let url = URL(string: urlString) else { return }
        
        let encoder = JSONEncoder()
        // 完成自定义模型到JSON数据的转换,不会触发__SwiftValue兼容问题
        guard let body = try? encoder.encode(param) else {
            print("请求参数序列化失败")
            return
        }
        
        var request = URLRequest(url: url)
        request.httpBody = body
        request.httpMethod = "POST"
        // 补充请求头,告知服务端请求体为JSON格式
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        let task = URLSession.shared.dataTask(with: request) { (data, _, error) in
            if let error = error {
                print("网络请求错误: \(error)")
                return
            }
            guard let data = data else { return }
            do {
                let update = try JSONDecoder().decode(Int.self, from: data)
                DispatchQueue.main.async {
                    completion(update)
                }
            } catch {
                print("响应数据解析失败: \(error)")
            }
        }
        // 启动网络任务
        task.resume()
    }
}

2. 原有调用代码无需大幅修改

由于Swift中[String: Encodable]类型的字典本身默认符合Encodable协议,原来在ContentView中的调用逻辑Api.postRequest(param: ["space" : town], urlString: Api.spacePostUrl) { ... }可以直接正常工作,不需要额外调整传参格式。

补充说明:如果希望进一步简化代码,可以直接把Space模型作为参数传入请求方法,不需要手动套字典包装,只要后端接口接受对应格式的请求体即可。


内容的提问来源于stack exchange,提问作者evelland

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 20:15:01