RxMoya自定义NetworkingError类型转换崩溃问题求助
Fix Crash in RxMoya's
filterSuccess Method & Properly Extract Error Messages 1. 崩溃的直接原因
看你filterSuccess()里的这段代码:
return Single.error(jsonObject ?? NetworkingError.self as! Error)
这里有两个致命问题:
NetworkingError.self是类型对象,不是NetworkingError的实例,强行转成Error必然会崩溃——只有该结构体的实例才遵循Error协议,类型本身不满足- 用
JSONSerialization.jsonObject(...) as? NetworkingError完全行不通:NetworkingError没有遵循Codable协议,而且服务器返回的是ResponseBase格式的错误JSON,根本无法直接转成NetworkingError类型
2. 完整修复方案
第一步:完善NetworkingError结构
让它能接收解析后的ResponseBase错误信息,同时补上必要的初始化和自定义描述:
public struct NetworkingError: Error, CustomStringConvertible { let httpResponse: HTTPURLResponse? let networkData: Data? let baseError: Error? // 新增:存储从ResponseBase解析出来的服务器错误信息 let responseError: ResponseBase<EmptyCodable>? // 自定义描述,方便直接打印完整错误详情 public var description: String { let statusCode = httpResponse?.statusCode ?? -1 let errorMessage = responseError?.message ?? baseError?.localizedDescription ?? "Unknown network error" return "Network Error (\(statusCode)): \(errorMessage)" } // 初始化:从Response和解析后的ResponseBase错误创建 init(response: Response, responseError: ResponseBase<EmptyCodable>?) { self.httpResponse = response.response self.networkData = response.data self.baseError = nil self.responseError = responseError } // 初始化:处理其他网络错误(比如请求失败、解析失败) init(baseError: Error, response: Response? = nil) { self.httpResponse = response?.response self.networkData = response?.data self.baseError = baseError self.responseError = nil } // 空的Codable类型,用于解析不带data字段的错误ResponseBase private struct EmptyCodable: Codable {} }
第二步:修复filterSuccess方法
现在我们要正确解析服务器返回的ResponseBase错误格式,然后创建合法的NetworkingError实例返回:
public extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response { // 保留你的mapObject、mapArray方法... func filterSuccess() -> Single<ElementType> { return flatMap { response -> Single<ElementType> in guard 200...299 ~= response.statusCode else { // 尝试解析服务器返回的ResponseBase错误格式 do { // 错误场景下ResponseBase的data字段可能为空,用EmptyCodable占位 let errorResponse = try response.map(ResponseBase<EmptyCodable>.self) return Single.error(NetworkingError(response: response, responseError: errorResponse)) } catch { // 如果解析失败(比如返回的不是标准ResponseBase格式),用解析错误创建NetworkingError return Single.error(NetworkingError(baseError: error, response: response)) } } return Single.just(response) } } }
第三步:优化Presenter中的错误处理
现在可以轻松提取完整的错误信息了:
func postVerifyApp(challenge: Int, identifier: String) { view?.setProgress(enabled: true) source.postVerifyApp(challenge: challenge, identifier: identifier) .retry(.delayed(maxCount: 2, time: 2.5), shouldRetry: networkRetryPredicate) .asSingle() .subscribe(onSuccess: { [weak self] response in guard let presenter = self, let view = presenter.view else {return} view.setProgress(enabled: false) log(response, .json) guard let data = response.data else { return } view.showVerifySuccess() }, onError: { [weak self] error in guard let presenter = self, let view = presenter.view else {return} view.setProgress(enabled: false) // 处理NetworkingError if let networkError = error as? NetworkingError { print("完整错误详情:\(networkError.description)") // 提取服务器返回的错误提示 if let serverMessage = networkError.responseError?.message { print("服务器错误提示:\(serverMessage)") // 这里可以直接把错误提示展示给用户 // view?.showError(message: serverMessage) } // 打印HTTP状态码用于调试 if let statusCode = networkError.httpResponse?.statusCode { print("HTTP状态码:\(statusCode)") } } else { // 处理其他非网络类错误 print("其他错误:\(error.localizedDescription)") } }).disposed(by: disposeBag) }
额外优化(可选):调整mapObject/mapArray解析逻辑
如果你的业务接口统一用ResponseBase包裹数据,可以调整解析方法,直接从ResponseBase中提取业务数据:
public extension Response { func mapObject<T: Codable>(_ type: T.Type, path: String? = nil) throws -> T { let responseBase = try map(ResponseBase<T>.self, path: path) // 额外校验业务成功状态(可选,因为已经在filterSuccess中处理了HTTP状态码) guard responseBase.isSucessful else { throw NetworkingError(response: self, responseError: ResponseBase<EmptyCodable>(error: responseBase.error, message: responseBase.message, data: EmptyCodable())) } return responseBase.data } func mapArray<T: Codable>(_ type: T.Type, path: String? = nil) throws -> [T] { let responseBase = try map(ResponseBase<[T]>.self, path: path) guard responseBase.isSucessful else { throw NetworkingError(response: self, responseError: ResponseBase<EmptyCodable>(error: responseBase.error, message: responseBase.message, data: EmptyCodable())) } return responseBase.data } }
3. 修复逻辑说明
- 彻底解决崩溃:不再强行把类型转为实例,而是创建合法的
NetworkingError实例返回 - 错误解析更可靠:用Moya自带的
map方法解析ResponseBase,比手动用JSONSerialization更简洁不易出错 - 错误信息更完整:
NetworkingError同时保存了HTTP响应信息、服务器业务错误提示、底层错误,既方便调试,也能直接给用户展示友好提示
内容的提问来源于stack exchange,提问作者King
相关产品推荐
相关产品推荐

