Есть ли способ сделать запросы Alamofire с повторными попытками

у меня есть много мест в коде, где обрабатывается запрос/ответ Alamofire.

каждый из этих запросов может завершиться неудачей из-за некоторой прерывистой проблемы (наиболее распространенной является flaky network).

Я хотел бы иметь возможность повторить запросы 3 раза, прежде чем выходить из игры.

простым методом было бы иметь что-то вроде этого

var errorCount = 0
func requestType1() {
   let request = Alamofire.request(...).responseJSON { response in
       if (isError(response) && errorCount < 3) {
          errorCount += 1
          request1()
       } 
       if (isError(response)) {
          handleError()
       }

       handleSuccess()
   }
}

однако мне очень не нравится этот подход по нескольким причинам. Наиболее очевидным является то, что Мне нужно реализовать такой код для каждого типа запроса (и у меня что-то вроде 15 из них).

мне любопытно, есть ли способ сделать что-то вроде (где изменения минимальны и не навязчиво)

let request = Alamofire.request(..., **3**) 

4 ответов


один из кусочков синтаксического сахара, который вы получаете с Swift, вы можете использовать это:

public func updateEvents(someNormalParam: Bool = true, someBlock: (Void->Void))

такой:

updateEvents(someNormalParam: false) {...}

Примечание блок находится за пределами () функции updateEvents, вопреки где вы обычно ожидаете. Он работает только в том случае, если блок является последним в объявлении функции.

это означает, что если у вас есть блок, такой как ваш запрос Alamofire, вы можете эффективно обернуть его с помощью функции повтора. Один немного усложняет проблему то, что вы хотите вызвать блок внутри блока. Ничего страшного:

func retryWrapper(alamoBlock: (Void->Request)) {
   alamoblock().responseJSON() {
       //Your retry logic here
   }
}

и вы используете его так:

retryWrapper() {
    Alamofire.request(method, targetUrl, parameters: parameters, encoding: encoding)
}

означает, что все, что вам нужно сделать, это найти ваши вызовы Alamofire и обернуть их в { } и поставить retryWrapper() раньше. Сама логика повторных попыток существует только один раз.


Alamofire 4.0 имеет RequestRetrier протокол, который вы можете использовать.

https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md#request-retrier

пример:

class OAuth2Handler: RequestAdapter, RequestRetrier {
    public func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: RequestRetryCompletion) {
        if let response = request.task.response as? HTTPURLResponse, response.statusCode == 401 {
            completion(true, 1.0) // retry after 1 second
        } else {
            completion(false, 0.0) // don't retry
        }

        // Or do something with the retryCount
        // i.e. completion(request.retryCount <= 10, 1.0)
    }
}

let sessionManager = SessionManager()
sessionManager.retrier = OAuth2Handler()

sessionManager.request(urlString).responseJSON { response in
    debugPrint(response)
}

у меня была такая же проблема, и я получил запросы на повторное использование RequestRetrier, should способ и request.retryCount. Что-то вроде этого:

// MARK: - RequestRetry

public func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
    lock.lock() ; defer { lock.unlock() }


    if let response = request.task?.response as? HTTPURLResponse{
        if response.statusCode == 401 {
            requestsToRetry.append(completion)

            getToken { (expires, _) in
               _ = SessionCountdownToken.sharedInstance.startCount(expirationTime: expires)
            }
        } else {

            if request.retryCount == 3 { completion(false, 0.0 ); return}
            completion(true, 1.0)
            return
        }
    } else {
        completion(false, 0.0)
    }
}

Ну, что я сделал, это следующее, если запрос терпит неудачу, я отправляю неудачное уведомление тому, кто сделал запрос. Затем он отвечает за повторное выполнение запроса. Оказалось, что это намного проще.

Alamofire.request(.GET, urlString, parameters: params, headers: defaultHeaders)
        .validate()
        .responseJSON
        {
            response in

            switch response.result
            {
            case .Success:
                print("Request Successful")

                let json = JSON(data:response.data!)

                NSNotificationCenter.defaultCenter().postNotificationName(SUCCESS_NOTIFICATION, object: movieArray)

            case .Failure(let error):
                print("Request Failed with error \(error)")
                NSNotificationCenter.defaultCenter().postNotificationName(FAILURE_NOTIFICATION, object: error)
            }
    }