RetryPolicy is another perfect implementation of RequestInterceptor in Alamofire. As the name suggests, it mainly satisfies various retry strategies when a request goes wrong. Here is a taste of it.

Six configurations provide five features

  1. RetryLimit: Controls the maximum retry times. If this number is exceeded, the retry will not be performed. The default value is 2.

  2. ExponentialBackoffBase & exponentialBackoffScale: These two parameters, together with the current number of retries, control the direct interval between two retries. The calculation method is POW (exponentialBackoffBase, retryCount) * exponentialBackoffScale. Their default values are 2 and 0.5. You can set exponentialBackoffScale to 1 for the interval between exponential growth.

  3. RetryableHTTPMethods: Controls which HTTP methods can be retried. Default values [delete, get, head, options, put, trace]. All of these methods are idempotent.

  4. RetryableHTTPStatusCodes: Controls which HTTP response status codes can be retried. Default values [408 (request timeout), 500, 502, 503, 504].

  5. RetryableURLErrorCodes: Controls which system error status codes can be retried. Default value:

    1. backgroundSessionInUseByAnotherProcess: the backgroundSessionoccupied
    2. backgroundSessionWasDisconnected: Background during request processingSessionTo be suspended or quit
    3. badServerResponse: Error data returned by the background
    4. callIsActive: The request was interrupted by an incoming call
    5. cannotConnectToHost: Failed to link to the host
    6. cannotFindHost: The host cannot be found
    7. cannotLoadFromNetwork: Needs to download data from the network, but is restricted by the loading policyLoad from cache
    8. dataNotAllowed: There is no WIFI or cellular connection
    9. dnsLookupFailed: Failed to resolve DNS
    10. downloadDecodingFailedMidStream: Data cannot be decoded during download
    11. downloadDecodingFailedToComplete: Data cannot be decoded after downloading
    12. internationalRoamingOff: This function is disabled when roaming is required
    13. networkConnectionLost: The network is suddenly disconnected during the request process
    14. notConnectedToInternet: Failed to establish network connection. The error code is displayed when the application is installed for the first time and the network permission is not obtained.
    15. secureConnectionFailed: Failed to establish a secure network connection
    16. serverCertificateHasBadDate: The SSL certificate is invalid or expired
    17. serverCertificateNotYetValid: The SSL certificate does not take effect
    18. timedOut: timeout

If the above configuration meets the requirements, we only need to configure the class on the Session:

let session = Session(interceptor: Interceptor(adapters: [], retriers: [RetryPolicy()))Copy the code

Or configured on Request when making a Request:

AF.request("https://httpbin.org/get", interceptor: RetryPolicy())
Copy the code

In this way, failed requests that encounter the above conditions are automatically retried.

The implementation of such a rich configuration is not complicated

Given the rich configuration available, you might think the implementation would be incredibly complex. It’s not. It’s just a few lines of code:

open func shouldRetry(request: Request.dueTo error: Error) -> Bool {
    // Determine whether the request method supports retry
    guard let httpMethod = request.request?.method, retryableHTTPMethods.contains(httpMethod) else { return false }
    // Check whether the response status code supports retry
    if let statusCode = request.response?.statusCode, retryableHTTPStatusCodes.contains(statusCode) {
        return true
    } else {
        // Check whether the system status code supports retry
        let errorCode = (error as? URLError)?.code
        let afErrorCode = (error.asAFError?.underlyingError as? URLError)?.code

        guard let code = errorCode ?? afErrorCode else { return false }
        // 6. retryableURLErrorCodes
        return retryableURLErrorCodes.contains(code)
    }
}
Copy the code

The core logic is just a few lines of code. It is to determine whether the current request satisfies each state in turn. This is followed by a call to the method:

open func retry(_ request: Request.for session: Session.dueTo error: Error.completion: @escaping (RetryResult) - >Void) {
    // Determine the number of retries and three other criteria
    if request.retryCount < retryLimit, shouldRetry(request: request, dueTo: error) {
        // If a retry is required, the retry interval is calculated
        completion(.retryWithDelay(pow(Double(exponentialBackoffBase), Double(request.retryCount)) * exponentialBackoffScale))
    } else {
        completion(.doNotRetry)
    }
}
Copy the code

With this adaptation, we use all the parameters we can configure. This is the whole RetryPolicy picture. So easy for 😂 ~ ~

There are two caveats

whyretryLimitThis configuration should be carried out to judge separately

In the fit method, we see that the retryLimit is judged first and then the core judgment method is called. Why are retryLimit judgments not made in the core method?

In my opinion, the retryLimit condition is the easiest to trigger false, in which case you can save a function call. Hope to have different views of the students comment area to tell me, progress together 😁.

retryableURLErrorCodesPoints that are not covered

Although retryableURLErrorCodes this configuration supports the notConnectedToInternet error code. However, it does not perform well when the application is first installed and the network is not authorized.

Consider this scenario: the user opens the application for the first time, network authorization pops up, and then stays in this state. At this point, our application does not have network authorization, and the request is in error. According to the retry policy, it will request again, and again in error…. This loop keeps going until the number of retries runs out.

The solution

  1. openURLSessionConfigurationthewaitsForConnectivityConfiguration. This configuration, when turned on, suspends requests when there is no network connection and waits until there is a link. But only afterIOS 11.0Use it later. How many other students are following11Previous versions of the fight? Leave a message I sympathize with you 😂)
  2. Their implementationRequestInterceptor. It monitors the network status internally and decides whether to retry based on the status. This project is over to you.

conclusion

Today you learned about the use and implementation of RetryPolicy. There are also two interesting little problems. See you in the next video. Bye bye 👋 🏻.