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
相关产品推荐
相关产品推荐

