Skip to content

Commit d1b30e9

Browse files
committed
feat: CompositeInterceptor allows separate adapters and retriers
+ documentation
1 parent 12b9d6a commit d1b30e9

2 files changed

Lines changed: 38 additions & 4 deletions

File tree

Sources/GoodNetworking/Interception/Interceptor.swift

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@ import Foundation
99

1010
// MARK: - Interceptor
1111

12+
/// Interceptor merges request adaptation and retry behavior.
1213
public protocol Interceptor: Adapter, Retrier {}
1314

1415
// MARK: - Default interceptor
1516

17+
/// Default interceptor does not adapt (modify) requests in any way.
18+
/// Default retrying behaviour is applied as per RFC9110 specification.
19+
///
20+
/// - warning: Retrying is currently not implemented and all requests
21+
/// are resolved as `.doNotRetry`.
1622
public final class DefaultInterceptor: Interceptor {
1723

1824
public init() {}
@@ -28,22 +34,42 @@ public final class DefaultInterceptor: Interceptor {
2834

2935
// MARK: - Composite interceptor
3036

37+
/// Merges multiple interceptors, adapters and retriers into single interceptor instance.
38+
///
39+
/// Adapters have priority over general interceptors and are executed first when adapting
40+
/// requests. All adapters execute in order they are passed in at initialization.
41+
///
42+
/// Retriers have priority over general interceptors and are executed first when retrying
43+
/// requests. The first retrier to allow retrying the request is used, rest are not executed.
44+
///
45+
/// This behaviour effectively accomplishes that request authentication is executed
46+
/// last, and requests are retried if a specific retrier allows it.
3147
public final class CompositeInterceptor: Interceptor {
3248

3349
private let interceptors: [Interceptor]
34-
35-
public init(interceptors: [Interceptor]) {
50+
private let adapters: [Adapter]
51+
private let retriers: [Retrier]
52+
53+
public init(
54+
interceptors: [Interceptor],
55+
adapters: [Adapter] = [],
56+
retriers: [Retrier] = []
57+
) {
3658
self.interceptors = interceptors
59+
self.adapters = adapters
60+
self.retriers = retriers
3761
}
3862

3963
public func adapt(urlRequest: inout URLRequest) async throws(NetworkError) {
40-
for adapter in interceptors {
64+
let allAdapters: [Adapter] = adapters + interceptors
65+
for adapter in allAdapters {
4166
try await adapter.adapt(urlRequest: &urlRequest)
4267
}
4368
}
4469

4570
public func retry(urlRequest: inout URLRequest, for session: NetworkSession, dueTo error: NetworkError) async throws(NetworkError) -> RetryResult {
46-
for retrier in interceptors {
71+
let allRetriers: [Retrier] = retriers + interceptors
72+
for retrier in allRetriers {
4773
let retryResult = try await retrier.retry(urlRequest: &urlRequest, for: session, dueTo: error)
4874
switch retryResult {
4975
case .doNotRetry:

Sources/GoodNetworking/Interception/Retrier.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,18 @@ public protocol Retrier: Sendable {
1717

1818
// MARK: - Retry result
1919

20+
/// Result of a retry operation.
21+
///
22+
/// See ``Retrier``.
2023
public enum RetryResult: Sendable {
2124

25+
/// Request will not be retried
2226
case doNotRetry
27+
28+
/// Request will be retried only after the specified time interval has passed
2329
case retryAfter(TimeInterval)
30+
31+
/// Request will be retried immediately
2432
case retry
2533

2634
}

0 commit comments

Comments
 (0)