Swift Combine实现基于成功响应的API最多3次递归重试
问题
需要在Swift Combine中实现:当API返回成功响应但status不是预期的Paid时,最多递归调用该API3次,每次重试间隔5秒。即使第三次仍未得到Paid状态,也要将showConfirmDetails设为true并继续流程。现有代码仅处理了错误重试场景,不清楚如何处理成功但不符合条件的重试需求。
原代码如下:
struct DetailModel { var status: String } func details(url: URL) -> AnyPublisher<DetailModel, NetworkCommunicationError> { let getDetailsRequest = NetworkRequest(url: url, queryType: .path, responseDataType: .json, isProprietary: true, useJsonSerialization: true) return network.handleRequest(request: getDetailsRequest) } func getDetialsById(url: URL) { self.responseModel = nil self.service.details(url: url) .receive(on: DispatchQueue.main) .sink( { (compleation) in if case let.failure(error) = completion { self.handleErrorCase(error) } } receiveValue: { (value) in self.responseModel = value if value.status != "Paid" { self.showConfirmDetails = false /* Retry the same call self.service.details for 2 more times with the delay of 5 seconds to check if i can get the value.status == Paid on the third try even if i dont get value.status == Paid status i will make self.showConfirmDetails = true and proceed with the status i have received */ } else { self.showConfirmDetails = true } }).store(in: &self.cancellebles) }
解决方案
核心思路是将「成功响应但状态不符合预期」的场景转换为自定义错误,借助Combine的重试逻辑处理,最后再捕获该错误并转为正常流程。
步骤1:定义自定义重试错误
先扩展原有网络错误类型,新增对应重试场景的错误(如果原有错误类型无法扩展,可自定义枚举包装):
extension NetworkCommunicationError { static let nonPaidStatus = Self(rawValue: "non_paid_status") // 根据你的错误类型实际初始化逻辑调整 }
步骤2:实现带条件的重试逻辑
修改getDetialsById函数,用tryMap转换不符合条件的响应为错误,再结合retry和delay实现间隔重试,最后在completion中处理最终结果:
func getDetialsById(url: URL) { self.responseModel = nil let maxRetries = 2 // 加上初始请求共3次 let retryDelay: TimeInterval = 5 self.service.details(url: url) .tryMap { model -> DetailModel in // 状态不是Paid时抛出错误,触发重试 guard model.status == "Paid" else { throw NetworkCommunicationError.nonPaidStatus } return model } .retry(maxRetries) { error -> Bool in // 仅对自定义的nonPaidStatus错误进行重试 guard case NetworkCommunicationError.nonPaidStatus = error else { return false } return true } .delay(for: .seconds(retryDelay), scheduler: DispatchQueue.global()) .receive(on: DispatchQueue.main) .sink(receiveCompletion: { completion in switch completion { case .failure(let error): if case NetworkCommunicationError.nonPaidStatus = error { // 重试耗尽仍未拿到Paid,设置showConfirmDetails为true self.showConfirmDetails = true } else { // 处理其他网络错误 self.handleErrorCase(error) } case .finished: // 成功拿到Paid状态 self.showConfirmDetails = true } }, receiveValue: { value in self.responseModel = value }) .store(in: &self.cancellebles) }
关键说明
tryMap转换条件为错误:把不符合预期的成功响应转为错误,让Combine的重试逻辑能触发。retry(_:if:)控制重试规则:限制仅对nonPaidStatus错误重试,最多重试2次(加上初始请求共3次)。delay实现间隔:每次重试前等待5秒,使用全局队列避免阻塞主线程。- Completion最终处理:区分错误类型,重试耗尽后的非Paid错误直接设置
showConfirmDetails = true,其他错误走原有错误处理逻辑。
如果原有NetworkCommunicationError无法扩展,可改用自定义错误枚举包装:
enum DetailError: Error { case network(NetworkCommunicationError) case nonPaidStatus } // 对应的管道修改 self.service.details(url: url) .mapError(DetailError.network) .tryMap { model -> DetailModel in guard model.status == "Paid" else { throw DetailError.nonPaidStatus } return model } .retry(maxRetries) { error -> Bool in guard case DetailError.nonPaidStatus = error else { return false } return true } .delay(for: .seconds(retryDelay), scheduler: DispatchQueue.global()) .receive(on: DispatchQueue.main) .sink(receiveCompletion: { completion in switch completion { case .failure(let error): switch error { case .network(let netError): self.handleErrorCase(netError) case .nonPaidStatus: self.showConfirmDetails = true } case .finished: self.showConfirmDetails = true } }, receiveValue: { value in self.responseModel = value }) .store(in: &self.cancellebles)
内容的提问来源于stack exchange,提问作者Varun Kashyap
相关产品推荐
相关产品推荐

