Appearance
Alamofire
RequestAdapter
Alamofire's RequestAdapter
protocol allows each URLRequest
to be inspected and mutated before being issued over the network.
RequestRetrier
Alamofire's RequestRetrier
protocol allows a Request
that encountered an Error
while being executed to be retried.
swift
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void)
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void)
RequestInterceptors
Alamofire supports the use of multiple RequestInterceptors
at both the Session
and Request
levels through the use of the Interceptor
type.
When composed of multiple RequestAdapters
, Interceptor
will call each RequestAdapter
in succession. If they all succeed, the final URLRequest
out of the chain of RequestAdapters
will be used to perform the request. If one fails, adaptation stops and the Request
fails with the error returned.
swift
private func adapt(_ urlRequest: URLRequest,
for session: Session,
using adapters: [any RequestAdapter],
completion: @escaping @Sendable (Result<URLRequest, any Error>) -> Void) {
var pendingAdapters = adapters
guard !pendingAdapters.isEmpty else { completion(.success(urlRequest)); return }
let adapter = pendingAdapters.removeFirst()
adapter.adapt(urlRequest, for: session) { [pendingAdapters] result in
switch result {
case let .success(urlRequest):
self.adapt(urlRequest, for: session, using: pendingAdapters, completion: completion)
case .failure:
completion(result)
}
}
}
private func adapt(_ urlRequest: URLRequest,
for session: Session,
using adapters: [any RequestAdapter],
completion: @escaping @Sendable (Result<URLRequest, any Error>) -> Void) {
var pendingAdapters = adapters
guard !pendingAdapters.isEmpty else { completion(.success(urlRequest)); return }
let adapter = pendingAdapters.removeFirst()
adapter.adapt(urlRequest, for: session) { [pendingAdapters] result in
switch result {
case let .success(urlRequest):
self.adapt(urlRequest, for: session, using: pendingAdapters, completion: completion)
case .failure:
completion(result)
}
}
}
Similarly, when composed of multiple RequestRetriers
, retries are executed in the same order as the retriers were added to the instance, until either all of them complete or one of them fails with an error.
swift
private func retry(_ request: Request,
for session: Session,
dueTo error: any Error,
using retriers: [any RequestRetrier],
completion: @escaping @Sendable (RetryResult) -> Void) {
var pendingRetriers = retriers
guard !pendingRetriers.isEmpty else { completion(.doNotRetry); return }
let retrier = pendingRetriers.removeFirst()
retrier.retry(request, for: session, dueTo: error) { [pendingRetriers] result in
switch result {
case .retry, .retryWithDelay, .doNotRetryWithError:
completion(result)
case .doNotRetry:
// Only continue to the next retrier if retry was not triggered and no error was encountered
self.retry(request, for: session, dueTo: error, using: pendingRetriers, completion: completion)
}
}
}
private func retry(_ request: Request,
for session: Session,
dueTo error: any Error,
using retriers: [any RequestRetrier],
completion: @escaping @Sendable (RetryResult) -> Void) {
var pendingRetriers = retriers
guard !pendingRetriers.isEmpty else { completion(.doNotRetry); return }
let retrier = pendingRetriers.removeFirst()
retrier.retry(request, for: session, dueTo: error) { [pendingRetriers] result in
switch result {
case .retry, .retryWithDelay, .doNotRetryWithError:
completion(result)
case .doNotRetry:
// Only continue to the next retrier if retry was not triggered and no error was encountered
self.retry(request, for: session, dueTo: error, using: pendingRetriers, completion: completion)
}
}
}