Swift解析iTunes API遇Decodable/Encodable协议不兼容问题
问题分析
你碰到的Type '_' does not conform to protocol Decodable / Encodable错误,核心原因很明确:Swift标准库的Result类型要实现Codable,必须保证它的Success和Failure关联类型都遵循Codable协议。常见触发场景有两种:
- 给
Result指定的Success/Failure类型(比如自定义模型或错误)没实现Codable - 自己定义了和标准库同名的
Result类型,且这个自定义类型没实现Codable
修复方案
1. 确保关联类型遵循Codable
假设你要解析iTunes搜索API的返回数据,先定义符合Codable的基础模型:
// iTunes单条搜索结果模型 struct ITunesItem: Codable { let trackName: String? let artistName: String? let artworkUrl100: String? // 按需添加其他字段 } // 自定义错误类型,必须遵循Codable(标准库Error不支持Codable) enum APIError: Error, Codable { case invalidResponse case decodingFailed } // 带Result的API响应结构体 struct APIResponse: Codable { let result: Result<[ITunesItem], APIError> }
这里关键是给Result的Failure关联类型用自定义的Codable错误枚举,而非标准库的Error。
2. 检查是否存在同名自定义Result类型
如果你自己写了一个Result结构体/枚举,且没实现Codable,就会触发这个错误。解决方式二选一:
- 删掉自定义
Result,直接用Swift标准库的版本 - 给自定义
Result补全Codable实现:
enum CustomResult<Success: Codable, Failure: Codable>: Codable { case success(Success) case failure(Failure) enum CodingKeys: String, CodingKey { case success, failure } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { case .success(let value): try container.encode(value, forKey: .success) case .failure(let error): try container.encode(error, forKey: .failure) } } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) if let successVal = try container.decodeIfPresent(Success.self, forKey: .success) { self = .success(successVal) } else if let failureVal = try container.decodeIfPresent(Failure.self, forKey: .failure) { self = .failure(failureVal) } else { throw DecodingError.dataCorruptedError( forKey: .success, in: container, debugDescription: "No valid result field found" ) } } }
3. 更贴合iTunes API的优化方案
实际调用iTunes API时,官方返回的JSON里并没有success/failure这类Result对应的字段,直接把Result塞进响应结构体并不合理。更实用的方式是:
- 定义仅对应API返回格式的响应模型:
struct ITunesSearchResponse: Codable { let resultCount: Int let results: [ITunesItem] }
- 在网络请求回调中用
Result处理成功/失败:
func fetchiTunesData(searchTerm: String, completion: @escaping (Result<ITunesSearchResponse, Error>) -> Void) { guard let encodedTerm = searchTerm.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: "https://itunes.apple.com/search?term=\(encodedTerm)") else { completion(.failure(NSError(domain: "Invalid URL", code: -1, userInfo: nil))) return } URLSession.shared.dataTask(with: url) { data, _, error in if let error = error { completion(.failure(error)) return } guard let data = data else { completion(.failure(NSError(domain: "No data received", code: -2, userInfo: nil))) return } do { let response = try JSONDecoder().decode(ITunesSearchResponse.self, from: data) completion(.success(response)) } catch { completion(.failure(error)) } }.resume() }
这种方式既符合API的实际返回结构,又通过Result统一处理了请求的成功/失败场景。
总结
如果坚持要把Result放进响应结构体,只需保证其泛型参数都遵循Codable即可;但更合理的思路是把Result用于网络请求的回调逻辑,而非作为数据模型的一部分。
内容的提问来源于stack exchange,提问作者Mason Bose
相关产品推荐
相关产品推荐

