如何使用dataTaskPublisher处理接口返回的字符串、JSON等多类型数据
你原先的代码报错是因为JSONDecoder解码String类型时,要求输入必须是符合JSON规范的字符串(即内容带双引号,比如"\"hello, world!\""),而你的服务端返回的是没有包裹双引号的纯文本,不符合JSON格式要求所以解码失败。
解决方案
核心思路是扩展Combine的Publisher,针对Data类型的输出编写通用解码方法,根据目标解码类型自动选择适配的解码逻辑,不需要上层调用区分接口返回类型。
1. 编写通用解码扩展
import Combine extension Publisher where Output == Data, Failure == URLError { func universalDecode<T: Decodable>(_ type: T.Type, decoder: JSONDecoder = JSONDecoder()) -> AnyPublisher<T, Error> { return tryMap { data in // 处理纯字符串返回场景 if T.self == String.self { guard let result = String(data: data, encoding: .utf8) as? T else { throw DecodingError.dataCorrupted(.init( codingPath: [], debugDescription: "Failed to convert data to UTF-8 string" )) } return result } // 处理纯数值返回场景(Int) if T.self == Int.self { guard let str = String(data: data, encoding: .utf8), let result = Int(str) as? T else { throw DecodingError.dataCorrupted(.init( codingPath: [], debugDescription: "Failed to convert data to Int" )) } return result } // 处理纯数值返回场景(Double,可按需扩展其他数值类型) if T.self == Double.self { guard let str = String(data: data, encoding: .utf8), let result = Double(str) as? T else { throw DecodingError.dataCorrupted(.init( codingPath: [], debugDescription: "Failed to convert data to Double" )) } return result } // 其他Codable类型(自定义模型、数组等)走JSONDecoder解码 return try decoder.decode(T.self, from: data) } .eraseToAnyPublisher() } }
2. 统一调用示例
所有接口都可以使用同一种调用格式,只需要传入期望的返回类型即可:
调用返回纯字符串的接口
var cancellables = Set<AnyCancellable>() URLSession .shared .dataTaskPublisher(for: stringRequest) .map(\.data) .universalDecode(String.self) .receive(on: DispatchQueue.main) .sink( receiveCompletion: { completion in // 处理错误/完成逻辑 }, receiveValue: { text in print(text) // 输出 hello, world! } ) .store(in: &cancellables)
调用返回JSON模型的接口
// 定义业务模型 struct User: Codable { let id: Int let nickname: String } URLSession .shared .dataTaskPublisher(for: userRequest) .map(\.data) .universalDecode(User.self) .receive(on: DispatchQueue.main) .sink( receiveCompletion: { _ in }, receiveValue: { user in print(user.nickname) } ) .store(in: &cancellables)
调用返回Codable数组的接口
URLSession .shared .dataTaskPublisher(for: listRequest) .map(\.data) .universalDecode([User].self) .receive(on: DispatchQueue.main) .sink( receiveCompletion: { _ in }, receiveValue: { userList in print(userList.count) } ) .store(in: &cancellables)
扩展说明
如果后续需要适配其他特殊返回格式,只需要在universalDecode方法中新增对应类型的处理分支即可,不需要修改上层的调用逻辑。
内容的提问来源于stack exchange,提问作者WikipediaBrown
相关产品推荐
相关产品推荐

