如何在 ios 应用程序中重试具有时间延迟的失败 Web 服务调用

How to retry failed web service calls with time delay in ios app

我正在开发一个 IOS 应用程序,根据要求,我需要重试所有失败的 api 3 次,延迟 3 秒。为此,我按如下方式向 Publisher 添加了一个扩展,但此代码存在一个小问题,在最后一次尝试之后,如果我收到失败的响应,则需要 3 秒才能执行预期的失败代码。我需要在上次尝试失败后立即显示失败消息,有什么办法可以解决这个问题吗?

public extension Publisher {
    func retryWithDelay<S>(
        retries: Int,
        delay: S.SchedulerTimeType.Stride,
        scheduler: S
    ) -> AnyPublisher<Output, Failure> where S: Scheduler {
        
        self
            .delayIfFailure(for: delay, scheduler: scheduler)
            .retry(retries)
            .eraseToAnyPublisher()
    }

    private func delayIfFailure<S>(
        for delay: S.SchedulerTimeType.Stride,
        scheduler: S
    ) -> AnyPublisher<Output, Failure> where S: Scheduler {
        self.catch { error in
            Future { completion in
                scheduler.schedule(after: scheduler.now.advanced(by: delay)) {
                    completion(.failure(error))
                }
            }
        }
        .eraseToAnyPublisher()
    }
}

最好创建一个自定义 Publisher,其想法是您可以跟踪失败的次数,并且 return 延迟发布者(失败)在正常情况下,或最后一次重试的非延迟发布者。

struct RetryWithDelay<Upstream: Publisher, S: Scheduler>: Publisher {
    
   typealias Output = Upstream.Output
   typealias Failure = Upstream.Failure
    
   let upstream: Upstream
   let retries: Int
   let interval: S.SchedulerTimeType.Stride
   let scheduler: S
    
   func receive<Sub>(subscriber: Sub) 
          where Sub : Subscriber, 
                Upstream.Failure == Sub.Failure, 
                Upstream.Output == Sub.Input {

      var retries = self.retries
      let p = upstream
         .catch { err -> AnyPublisher<Output, Failure> in
            retries -= 1
            return Fail(error: err)
               .delay(for: retries > 0 ? interval : .zero, scheduler: scheduler)
               .eraseToAnyPublisher()
         }
         .retry(self.retries)

      p.subscribe(subscriber)
   }
}

为方便起见,您可以创建一个运算符,就像您在问题中所做的那样:

public extension Publisher {
    func retryWithDelay<S>(
        retries: Int,
        delay: S.SchedulerTimeType.Stride,
        scheduler: S
    ) -> RetryWithDelay<Self, S> where S: Scheduler {
        
        RetryWithDelay(upstream: self, 
                       retries: retries, delay: delay, scheduler: scheduler)
    }
}