Combine配合URLSession开发时如何捕获无网络错误并统一封装所有异常
问题原因
URLSession.DataTaskPublisher遇到无网络连接、无法连接服务端、请求超时等系统级错误时,会直接发送Failure类型的错误事件,不会进入tryMap执行逻辑,因此这类错误无法被原有逻辑处理。
你之前使用mapError报错,是因为mapError仅用于转换Publisher的错误类型,无法将错误事件转换为正常的输出值(即你需要的Resource实例)。
解决方案
使用Combine的catch操作符捕获上游所有错误,将错误事件转换为输出Resource.networkError(data: nil)的Publisher即可,修改后的代码如下:
func request<T>(type: T.Type, urlRequest: URLRequest) -> AnyPublisher<Resource<Response<T>>, Never> where T : GenericDecodable { return URLSession.shared.dataTaskPublisher(for: urlRequest) .tryMap({ try NetworkController.handleURLResponse(output: $0, url: urlRequest) }) // 捕获所有上游错误,转换为对应的Resource返回值 .catch { _ in Just(Resource.networkError(data: nil)) } .receive(on: DispatchQueue.main) .eraseToAnyPublisher() }
修改后Publisher的错误类型会变为Never,订阅方无需再处理错误事件,所有场景的返回结果都会通过正常的输出通道传递。
如果需要针对不同网络错误做细分处理,可以在catch闭包里做类型判断:
.catch { error in if let urlError = error as? URLError { switch urlError.code { case .notConnectedToInternet: // 无网络场景可单独返回对应状态 return Just(Resource.networkError(data: nil)) case .cannotConnectToHost, .timedOut: return Just(Resource.networkError(data: nil)) default: return Just(Resource.networkError(data: nil)) } } // 其他类型错误统一处理 return Just(Resource.networkError(data: nil)) }
内容的提问来源于stack exchange,提问作者Tolgay Toklar
相关产品推荐
相关产品推荐

