Browse Source

Generic Static Accessors in Swift 5.5 (#3499)

* Add CachedResponseHandler static accessors.

* Add ParameterEncoder static accessors.

* Add RedirectHandler static accessors.

* Add RequestInterceptor static accessors.

* Add ResponseSerializer static accessors.

* Add ServerTrust static accessors.

* Formatting.

* Fix compilation.

* Add overloads to make members visible.

* Add iOS 15 testing.

* Update expected user agent test.

* Add default static accessors.
Jon Shier 4 years ago
parent
commit
f3e26fb618

+ 15 - 0
.github/workflows/ci.yml

@@ -94,6 +94,21 @@ jobs:
         run: arch -arch arm64e brew install alamofire/alamofire/firewalk || arch -arch arm64e brew upgrade alamofire/alamofire/firewalk && arch -arch x86_64 firewalk &
       - name: iOS - ${{ matrix.destination }}
         run: set -o pipefail && arch -arch arm64e env NSUnbufferedIO=YES xcodebuild -project "Alamofire.xcodeproj" -scheme "Alamofire iOS" -destination "${{ matrix.destination }}" clean test | xcpretty
+  iOS_15:
+    name: Test iOS 15
+    runs-on: firebreak
+    env:
+      DEVELOPER_DIR: /Applications/Xcode_13.0.app/Contents/Developer
+    timeout-minutes: 10
+    strategy:
+      matrix:
+        destination: ["OS=15.0,name=iPhone 13 Pro"]
+    steps:
+      - uses: actions/checkout@v2
+      - name: Install Firewalk
+        run: arch -arch arm64e brew install alamofire/alamofire/firewalk || arch -arch arm64e brew upgrade alamofire/alamofire/firewalk && arch -arch x86_64 firewalk &
+      - name: iOS - ${{ matrix.destination }}
+        run: set -o pipefail && arch -arch arm64e env NSUnbufferedIO=YES xcodebuild -project "Alamofire.xcodeproj" -scheme "Alamofire iOS" -destination "${{ matrix.destination }}" clean test | xcpretty
   tvOS:
     name: Test tvOS
     runs-on: firebreak

+ 1 - 1
Example/Source/DetailViewController.swift

@@ -38,7 +38,7 @@ class DetailViewController: UITableViewController {
             request?.onURLRequestCreation { [weak self] _ in
                 self?.title = self?.request?.description
             }
-            
+
             refreshControl?.endRefreshing()
             headers.removeAll()
             body = nil

+ 18 - 0
Source/CachedResponseHandler.swift

@@ -89,3 +89,21 @@ extension ResponseCacher: CachedResponseHandler {
         }
     }
 }
+
+#if swift(>=5.5)
+extension CachedResponseHandler where Self == ResponseCacher {
+    /// Provides a `ResponseCacher` which caches the response, if allowed. Equivalent to `ResponseCacher.cache`.
+    public static var cache: ResponseCacher { .cache }
+
+    /// Provides a `ResponseCacher` which does not cache the response. Equivalent to `ResponseCacher.doNotCache`.
+    public static var doNotCache: ResponseCacher { .doNotCache }
+
+    /// Creates a `ResponseCacher` which modifies the proposed `CachedURLResponse` using the provided closure.
+    ///
+    /// - Parameter closure: Closure used to modify the `CachedURLResponse`.
+    /// - Returns:           The `ResponseCacher`.
+    public static func modify(using closure: @escaping ((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)) -> ResponseCacher {
+        ResponseCacher(behavior: .modify(closure))
+    }
+}
+#endif

+ 33 - 0
Source/ParameterEncoder.swift

@@ -92,6 +92,21 @@ open class JSONParameterEncoder: ParameterEncoder {
     }
 }
 
+#if swift(>=5.5)
+extension ParameterEncoder where Self == JSONParameterEncoder {
+    /// Provides a default `JSONParameterEncoder` instance.
+    public static var json: JSONParameterEncoder { JSONParameterEncoder() }
+
+    /// Creates a `JSONParameterEncoder` using the provided `JSONEncoder`.
+    ///
+    /// - Parameter encoder: `JSONEncoder` used to encode parameters. `JSONEncoder()` by default.
+    /// - Returns:           The `JSONParameterEncoder`.
+    public static func json(encoder: JSONEncoder = JSONEncoder()) -> JSONParameterEncoder {
+        JSONParameterEncoder(encoder: encoder)
+    }
+}
+#endif
+
 /// A `ParameterEncoder` that encodes types as URL-encoded query strings to be set on the URL or as body data, depending
 /// on the `Destination` set.
 ///
@@ -182,3 +197,21 @@ open class URLEncodedFormParameterEncoder: ParameterEncoder {
         return request
     }
 }
+
+#if swift(>=5.5)
+extension ParameterEncoder where Self == URLEncodedFormParameterEncoder {
+    /// Provides a default `URLEncodedFormParameterEncoder` instance.
+    public static var urlEncodedForm: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() }
+
+    /// Creates a `URLEncodedFormParameterEncoder` with the provided encoder and destination.
+    ///
+    /// - Parameters:
+    ///   - encoder:     `URLEncodedFormEncoder` used to encode the parameters. `URLEncodedFormEncoder()` by default.
+    ///   - destination: `Destination` to which to encode the parameters. `.methodDependent` by default.
+    /// - Returns:       The `URLEncodedFormParameterEncoder`.
+    public static func urlEncodedForm(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(),
+                                      destination: URLEncodedFormParameterEncoder.Destination = .methodDependent) -> URLEncodedFormParameterEncoder {
+        URLEncodedFormParameterEncoder(encoder: encoder, destination: destination)
+    }
+}
+#endif

+ 18 - 0
Source/RedirectHandler.swift

@@ -93,3 +93,21 @@ extension Redirector: RedirectHandler {
         }
     }
 }
+
+#if swift(>=5.5)
+extension RedirectHandler where Self == Redirector {
+    /// Provides a `Redirector` which follows redirects. Equivalent to `Redirector.follow`.
+    public static var follow: Redirector { .follow }
+
+    /// Provides a `Redirector` which does not follow redirects. Equivalent to `Redirector.doNotFollow`.
+    public static var doNotFollow: Redirector { .doNotFollow }
+
+    /// Creates a `Redirector` which modifies the redirected `URLRequest` using the provided closure.
+    ///
+    /// - Parameter closure: Closure used to modify the redirect.
+    /// - Returns:           The `Redirector`.
+    public static func modify(using closure: @escaping (URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?) -> Redirector {
+        Redirector(behavior: .modify(closure))
+    }
+}
+#endif

+ 60 - 0
Source/RequestInterceptor.swift

@@ -128,6 +128,18 @@ open class Adapter: RequestInterceptor {
     }
 }
 
+#if swift(>=5.5)
+extension RequestAdapter where Self == Adapter {
+    /// Creates an `Adapter` using the provided `AdaptHandler` closure.
+    ///
+    /// - Parameter closure: `AdaptHandler` to use to adapt the request.
+    /// - Returns:           The `Adapter`.
+    public static func adapter(using closure: @escaping AdaptHandler) -> Adapter {
+        Adapter(closure)
+    }
+}
+#endif
+
 // MARK: -
 
 /// Closure-based `RequestRetrier`.
@@ -149,6 +161,18 @@ open class Retrier: RequestInterceptor {
     }
 }
 
+#if swift(>=5.5)
+extension RequestRetrier where Self == Retrier {
+    /// Creates a `Retrier` using the provided `RetryHandler` closure.
+    ///
+    /// - Parameter closure: `RetryHandler` to use to retry the request.
+    /// - Returns:           The `Retrier`.
+    public static func retrier(using closure: @escaping RetryHandler) -> Retrier {
+        Retrier(closure)
+    }
+}
+#endif
+
 // MARK: -
 
 /// `RequestInterceptor` which can use multiple `RequestAdapter` and `RequestRetrier` values.
@@ -242,3 +266,39 @@ open class Interceptor: RequestInterceptor {
         }
     }
 }
+
+#if swift(>=5.5)
+extension RequestInterceptor where Self == Interceptor {
+    /// Creates an `Interceptor` using the provided `AdaptHandler` and `RetryHandler` closures.
+    ///
+    /// - Parameters:
+    ///   - adapter: `AdapterHandler`to use to adapt the request.
+    ///   - retrier: `RetryHandler` to use to retry the request.
+    /// - Returns:   The `Interceptor`.
+    public static func interceptor(adapter: @escaping AdaptHandler, retrier: @escaping RetryHandler) -> Interceptor {
+        Interceptor(adaptHandler: adapter, retryHandler: retrier)
+    }
+
+    /// Creates an `Interceptor` using the provided `RequestAdapter` and `RequestRetrier` instances.
+    /// - Parameters:
+    ///   - adapter: `RequestAdapter` to use to adapt the request
+    ///   - retrier: `RequestRetrier` to use to retry the request.
+    /// - Returns:   The `Interceptor`.
+    public static func interceptor(adapter: RequestAdapter, retrier: RequestRetrier) -> Interceptor {
+        Interceptor(adapter: adapter, retrier: retrier)
+    }
+
+    /// Creates an `Interceptor` using the provided `RequestAdapter`s, `RequestRetrier`s, and `RequestInterceptor`s.
+    /// - Parameters:
+    ///   - adapters:     `RequestAdapter`s to use to adapt the request. These adapters will be run until one fails.
+    ///   - retriers:     `RequestRetrier`s to use to retry the request. These retriers will be run one at a time until
+    ///                   a retry is triggered.
+    ///   - interceptors: `RequestInterceptor`s to use to intercept the request.
+    /// - Returns:        The `Interceptor`.
+    public static func interceptor(adapters: [RequestAdapter] = [],
+                                   retriers: [RequestRetrier] = [],
+                                   interceptors: [RequestInterceptor] = []) -> Interceptor {
+        Interceptor(adapters: adapters, retriers: retriers, interceptors: interceptors)
+    }
+}
+#endif

+ 197 - 32
Source/ResponseSerialization.swift

@@ -95,6 +95,18 @@ public struct GoogleXSSIPreprocessor: DataPreprocessor {
     }
 }
 
+#if swift(>=5.5)
+extension DataPreprocessor where Self == PassthroughPreprocessor {
+    /// Provides a `PassthroughPreprocessor` instance.
+    public static var passthrough: PassthroughPreprocessor { PassthroughPreprocessor() }
+}
+
+extension DataPreprocessor where Self == GoogleXSSIPreprocessor {
+    /// Provides a `GoogleXSSIPreprocessor` instance.
+    public static var googleXSSI: GoogleXSSIPreprocessor { GoogleXSSIPreprocessor() }
+}
+#endif
+
 extension ResponseSerializer {
     /// Default `DataPreprocessor`. `PassthroughPreprocessor` by default.
     public static var defaultDataPreprocessor: DataPreprocessor { PassthroughPreprocessor() }
@@ -199,18 +211,9 @@ extension DataRequest {
         return self
     }
 
-    /// Adds a handler to be called once the request has finished.
-    ///
-    /// - Parameters:
-    ///   - queue:              The queue on which the completion handler is dispatched. `.main` by default
-    ///   - responseSerializer: The response serializer responsible for serializing the request, response, and data.
-    ///   - completionHandler:  The code to be executed once the request has finished.
-    ///
-    /// - Returns:              The request.
-    @discardableResult
-    public func response<Serializer: DataResponseSerializerProtocol>(queue: DispatchQueue = .main,
-                                                                     responseSerializer: Serializer,
-                                                                     completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void)
+    private func _response<Serializer: DataResponseSerializerProtocol>(queue: DispatchQueue = .main,
+                                                                       responseSerializer: Serializer,
+                                                                       completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void)
         -> Self {
         appendResponseSerializer {
             // Start work that should be on the serialization queue.
@@ -276,6 +279,38 @@ extension DataRequest {
 
         return self
     }
+
+    /// Adds a handler to be called once the request has finished.
+    ///
+    /// - Parameters:
+    ///   - queue:              The queue on which the completion handler is dispatched. `.main` by default
+    ///   - responseSerializer: The response serializer responsible for serializing the request, response, and data.
+    ///   - completionHandler:  The code to be executed once the request has finished.
+    ///
+    /// - Returns:              The request.
+    @discardableResult
+    public func response<Serializer: DataResponseSerializerProtocol>(queue: DispatchQueue = .main,
+                                                                     responseSerializer: Serializer,
+                                                                     completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void)
+        -> Self {
+        _response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
+    }
+
+    /// Adds a handler to be called once the request has finished.
+    ///
+    /// - Parameters:
+    ///   - queue:              The queue on which the completion handler is dispatched. `.main` by default
+    ///   - responseSerializer: The response serializer responsible for serializing the request, response, and data.
+    ///   - completionHandler:  The code to be executed once the request has finished.
+    ///
+    /// - Returns:              The request.
+    @discardableResult
+    public func response<Serializer: ResponseSerializer>(queue: DispatchQueue = .main,
+                                                         responseSerializer: Serializer,
+                                                         completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void)
+        -> Self {
+        _response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
+    }
 }
 
 extension DownloadRequest {
@@ -313,19 +348,9 @@ extension DownloadRequest {
         return self
     }
 
-    /// Adds a handler to be called once the request has finished.
-    ///
-    /// - Parameters:
-    ///   - queue:              The queue on which the completion handler is dispatched. `.main` by default.
-    ///   - responseSerializer: The response serializer responsible for serializing the request, response, and data
-    ///                         contained in the destination `URL`.
-    ///   - completionHandler:  The code to be executed once the request has finished.
-    ///
-    /// - Returns:              The request.
-    @discardableResult
-    public func response<Serializer: DownloadResponseSerializerProtocol>(queue: DispatchQueue = .main,
-                                                                         responseSerializer: Serializer,
-                                                                         completionHandler: @escaping (AFDownloadResponse<Serializer.SerializedObject>) -> Void)
+    private func _response<Serializer: DownloadResponseSerializerProtocol>(queue: DispatchQueue = .main,
+                                                                           responseSerializer: Serializer,
+                                                                           completionHandler: @escaping (AFDownloadResponse<Serializer.SerializedObject>) -> Void)
         -> Self {
         appendResponseSerializer {
             // Start work that should be on the serialization queue.
@@ -392,6 +417,40 @@ extension DownloadRequest {
 
         return self
     }
+
+    /// Adds a handler to be called once the request has finished.
+    ///
+    /// - Parameters:
+    ///   - queue:              The queue on which the completion handler is dispatched. `.main` by default.
+    ///   - responseSerializer: The response serializer responsible for serializing the request, response, and data
+    ///                         contained in the destination `URL`.
+    ///   - completionHandler:  The code to be executed once the request has finished.
+    ///
+    /// - Returns:              The request.
+    @discardableResult
+    public func response<Serializer: DownloadResponseSerializerProtocol>(queue: DispatchQueue = .main,
+                                                                         responseSerializer: Serializer,
+                                                                         completionHandler: @escaping (AFDownloadResponse<Serializer.SerializedObject>) -> Void)
+        -> Self {
+        _response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
+    }
+
+    /// Adds a handler to be called once the request has finished.
+    ///
+    /// - Parameters:
+    ///   - queue:              The queue on which the completion handler is dispatched. `.main` by default.
+    ///   - responseSerializer: The response serializer responsible for serializing the request, response, and data
+    ///                         contained in the destination `URL`.
+    ///   - completionHandler:  The code to be executed once the request has finished.
+    ///
+    /// - Returns:              The request.
+    @discardableResult
+    public func response<Serializer: ResponseSerializer>(queue: DispatchQueue = .main,
+                                                         responseSerializer: Serializer,
+                                                         completionHandler: @escaping (AFDownloadResponse<Serializer.SerializedObject>) -> Void)
+        -> Self {
+        _response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
+    }
 }
 
 // MARK: - URL
@@ -416,6 +475,13 @@ public struct URLResponseSerializer: DownloadResponseSerializerProtocol {
     }
 }
 
+#if swift(>=5.5)
+extension DownloadResponseSerializerProtocol where Self == URLResponseSerializer {
+    /// Provides a `URLResponseSerializer` instance.
+    public static var url: URLResponseSerializer { URLResponseSerializer() }
+}
+#endif
+
 extension DownloadRequest {
     /// Adds a handler using a `URLResponseSerializer` to be called once the request is finished.
     ///
@@ -441,7 +507,7 @@ public final class DataResponseSerializer: ResponseSerializer {
     public let emptyResponseCodes: Set<Int>
     public let emptyRequestMethods: Set<HTTPMethod>
 
-    /// Creates an instance using the provided values.
+    /// Creates a `DataResponseSerializer` using the provided parameters.
     ///
     /// - Parameters:
     ///   - dataPreprocessor:    `DataPreprocessor` used to prepare the received `Data` for serialization.
@@ -472,6 +538,29 @@ public final class DataResponseSerializer: ResponseSerializer {
     }
 }
 
+#if swift(>=5.5)
+extension ResponseSerializer where Self == DataResponseSerializer {
+    /// Provides a default `DataResponseSerializer` instance.
+    public static var data: DataResponseSerializer { DataResponseSerializer() }
+
+    /// Creates a `DataResponseSerializer` using the provided parameters.
+    ///
+    /// - Parameters:
+    ///   - dataPreprocessor:    `DataPreprocessor` used to prepare the received `Data` for serialization.
+    ///   - emptyResponseCodes:  The HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
+    ///   - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default.
+    ///
+    /// - Returns:               The `DataResponseSerializer`.
+    public static func data(dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
+                            emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
+                            emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DataResponseSerializer {
+        DataResponseSerializer(dataPreprocessor: dataPreprocessor,
+                               emptyResponseCodes: emptyResponseCodes,
+                               emptyRequestMethods: emptyRequestMethods)
+    }
+}
+#endif
+
 extension DataRequest {
     /// Adds a handler using a `DataResponseSerializer` to be called once the request has finished.
     ///
@@ -583,6 +672,33 @@ public final class StringResponseSerializer: ResponseSerializer {
     }
 }
 
+#if swift(>=5.5)
+extension ResponseSerializer where Self == StringResponseSerializer {
+    /// Provides a default `StringResponseSerializer` instance.
+    public static var string: StringResponseSerializer { StringResponseSerializer() }
+
+    /// Creates a `StringResponseSerializer` with the provided values.
+    ///
+    /// - Parameters:
+    ///   - dataPreprocessor:    `DataPreprocessor` used to prepare the received `Data` for serialization.
+    ///   - encoding:            A string encoding. Defaults to `nil`, in which case the encoding will be determined
+    ///                          from the server response, falling back to the default HTTP character set, `ISO-8859-1`.
+    ///   - emptyResponseCodes:  The HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
+    ///   - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default.
+    ///
+    /// - Returns:               The `StringResponseSerializer`.
+    public static func string(dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
+                              encoding: String.Encoding? = nil,
+                              emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
+                              emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> StringResponseSerializer {
+        StringResponseSerializer(dataPreprocessor: dataPreprocessor,
+                                 encoding: encoding,
+                                 emptyResponseCodes: emptyResponseCodes,
+                                 emptyRequestMethods: emptyRequestMethods)
+    }
+}
+#endif
+
 extension DataRequest {
     /// Adds a handler using a `StringResponseSerializer` to be called once the request has finished.
     ///
@@ -856,6 +972,31 @@ public final class DecodableResponseSerializer<T: Decodable>: ResponseSerializer
     }
 }
 
+#if swift(>=5.5)
+extension ResponseSerializer {
+    /// Creates a `DecodableResponseSerializer` using the values provided.
+    ///
+    /// - Parameters:
+    ///   - type:                `Decodable` type to decode from response data.
+    ///   - dataPreprocessor:    `DataPreprocessor` used to prepare the received `Data` for serialization.
+    ///   - decoder:             The `DataDecoder`. `JSONDecoder()` by default.
+    ///   - emptyResponseCodes:  The HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
+    ///   - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default.
+    ///
+    /// - Returns:               The `DecodableResponseSerializer`.
+    public static func decodable<T: Decodable>(of type: T.Type,
+                                               dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
+                                               decoder: DataDecoder = JSONDecoder(),
+                                               emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
+                                               emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DecodableResponseSerializer<T> where Self == DecodableResponseSerializer<T> {
+        DecodableResponseSerializer<T>(dataPreprocessor: dataPreprocessor,
+                                       decoder: decoder,
+                                       emptyResponseCodes: emptyResponseCodes,
+                                       emptyRequestMethods: emptyRequestMethods)
+    }
+}
+#endif
+
 extension DataRequest {
     /// Adds a handler using a `DecodableResponseSerializer` to be called once the request has finished.
     ///
@@ -869,8 +1010,6 @@ extension DataRequest {
     ///                          from the server response, falling back to the default HTTP character set, `ISO-8859-1`.
     ///   - emptyResponseCodes:  HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
     ///   - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
-    ///   - options:             `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments`
-    ///                          by default.
     ///   - completionHandler:   A closure to be executed once the request has finished.
     ///
     /// - Returns:               The request.
@@ -904,8 +1043,6 @@ extension DownloadRequest {
     ///                          from the server response, falling back to the default HTTP character set, `ISO-8859-1`.
     ///   - emptyResponseCodes:  HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
     ///   - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
-    ///   - options:             `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments`
-    ///                          by default.
     ///   - completionHandler:   A closure to be executed once the request has finished.
     ///
     /// - Returns:               The request.
@@ -950,8 +1087,9 @@ public struct DecodableStreamSerializer<T: Decodable>: DataStreamSerializer {
 
     /// Creates an instance with the provided `DataDecoder` and `DataPreprocessor`.
     /// - Parameters:
-    ///   - decoder: `        DataDecoder` used to decode incoming `Data`.
-    ///   - dataPreprocessor: `DataPreprocessor` used to process incoming `Data` before it's passed through the `decoder`.
+    ///   - decoder: `        DataDecoder` used to decode incoming `Data`. `JSONDecoder()` by default.
+    ///   - dataPreprocessor: `DataPreprocessor` used to process incoming `Data` before it's passed through the
+    ///                       `decoder`. `PassthroughPreprocessor()` by default.
     public init(decoder: DataDecoder = JSONDecoder(), dataPreprocessor: DataPreprocessor = PassthroughPreprocessor()) {
         self.decoder = decoder
         self.dataPreprocessor = dataPreprocessor
@@ -979,6 +1117,33 @@ public struct StringStreamSerializer: DataStreamSerializer {
     }
 }
 
+#if swift(>=5.5)
+extension DataStreamSerializer {
+    /// Creates a `DecodableStreamSerializer` instance with the provided `DataDecoder` and `DataPreprocessor`.
+    ///
+    /// - Parameters:
+    ///   - type:             `Decodable` type to decode from stream data.
+    ///   - decoder: `        DataDecoder` used to decode incoming `Data`. `JSONDecoder()` by default.
+    ///   - dataPreprocessor: `DataPreprocessor` used to process incoming `Data` before it's passed through the
+    ///                       `decoder`. `PassthroughPreprocessor()` by default.
+    public static func decodable<T: Decodable>(of type: T.Type,
+                                               decoder: DataDecoder = JSONDecoder(),
+                                               dataPreprocessor: DataPreprocessor = PassthroughPreprocessor()) -> Self where Self == DecodableStreamSerializer<T> {
+        DecodableStreamSerializer<T>(decoder: decoder, dataPreprocessor: dataPreprocessor)
+    }
+}
+
+extension DataStreamSerializer where Self == PassthroughStreamSerializer {
+    /// Provides a `PassthroughStreamSerializer` instance.
+    public static var passthrough: PassthroughStreamSerializer { PassthroughStreamSerializer() }
+}
+
+extension DataStreamSerializer where Self == StringStreamSerializer {
+    /// Provides a `StringStreamSerializer` instance.
+    public static var string: StringStreamSerializer { StringStreamSerializer() }
+}
+#endif
+
 extension DataStreamRequest {
     /// Adds a `StreamHandler` which performs no parsing on incoming `Data`.
     ///

+ 65 - 1
Source/RetryPolicy.swift

@@ -277,7 +277,7 @@ open class RetryPolicy: RequestInterceptor {
     /// The URL error codes that are automatically retried by the policy.
     public let retryableURLErrorCodes: Set<URLError.Code>
 
-    /// Creates an `ExponentialBackoffRetryPolicy` from the specified parameters.
+    /// Creates a `RetryPolicy` from the specified parameters.
     ///
     /// - Parameters:
     ///   - retryLimit:               The total number of times the request is allowed to be retried. `2` by default.
@@ -339,6 +339,41 @@ open class RetryPolicy: RequestInterceptor {
     }
 }
 
+#if swift(>=5.5)
+extension RequestInterceptor where Self == RetryPolicy {
+    /// Provides a default `RetryPolicy` instance.
+    public static var retryPolicy: RetryPolicy { RetryPolicy() }
+
+    /// Creates an `RetryPolicy` from the specified parameters.
+    ///
+    /// - Parameters:
+    ///   - retryLimit:               The total number of times the request is allowed to be retried. `2` by default.
+    ///   - exponentialBackoffBase:   The base of the exponential backoff policy. `2` by default.
+    ///   - exponentialBackoffScale:  The scale of the exponential backoff. `0.5` by default.
+    ///   - retryableHTTPMethods:     The HTTP methods that are allowed to be retried.
+    ///                               `RetryPolicy.defaultRetryableHTTPMethods` by default.
+    ///   - retryableHTTPStatusCodes: The HTTP status codes that are automatically retried by the policy.
+    ///                               `RetryPolicy.defaultRetryableHTTPStatusCodes` by default.
+    ///   - retryableURLErrorCodes:   The URL error codes that are automatically retried by the policy.
+    ///                               `RetryPolicy.defaultRetryableURLErrorCodes` by default.
+    ///
+    /// - Returns:                    The `RetryPolicy`
+    public static func retryPolicy(retryLimit: UInt = RetryPolicy.defaultRetryLimit,
+                                   exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase,
+                                   exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale,
+                                   retryableHTTPMethods: Set<HTTPMethod> = RetryPolicy.defaultRetryableHTTPMethods,
+                                   retryableHTTPStatusCodes: Set<Int> = RetryPolicy.defaultRetryableHTTPStatusCodes,
+                                   retryableURLErrorCodes: Set<URLError.Code> = RetryPolicy.defaultRetryableURLErrorCodes) -> RetryPolicy {
+        RetryPolicy(retryLimit: retryLimit,
+                    exponentialBackoffBase: exponentialBackoffBase,
+                    exponentialBackoffScale: exponentialBackoffScale,
+                    retryableHTTPMethods: retryableHTTPMethods,
+                    retryableHTTPStatusCodes: retryableHTTPStatusCodes,
+                    retryableURLErrorCodes: retryableURLErrorCodes)
+    }
+}
+#endif
+
 // MARK: -
 
 /// A retry policy that automatically retries idempotent requests for network connection lost errors. For more
@@ -368,3 +403,32 @@ open class ConnectionLostRetryPolicy: RetryPolicy {
                    retryableURLErrorCodes: [.networkConnectionLost])
     }
 }
+
+#if swift(>=5.5)
+extension RequestInterceptor where Self == ConnectionLostRetryPolicy {
+    /// Provides a default `ConnectionLostRetryPolicy` instance.
+    public static var connectionLostRetryPolicy: ConnectionLostRetryPolicy { ConnectionLostRetryPolicy() }
+
+    /// Creates a `ConnectionLostRetryPolicy` instance from the specified parameters.
+    ///
+    /// - Parameters:
+    ///   - retryLimit:              The total number of times the request is allowed to be retried.
+    ///                              `RetryPolicy.defaultRetryLimit` by default.
+    ///   - exponentialBackoffBase:  The base of the exponential backoff policy.
+    ///                              `RetryPolicy.defaultExponentialBackoffBase` by default.
+    ///   - exponentialBackoffScale: The scale of the exponential backoff.
+    ///                              `RetryPolicy.defaultExponentialBackoffScale` by default.
+    ///   - retryableHTTPMethods:    The idempotent http methods to retry.
+    ///
+    /// - Returns:                   The `ConnectionLostRetryPolicy`.
+    public static func connectionLostRetryPolicy(retryLimit: UInt = RetryPolicy.defaultRetryLimit,
+                                                 exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase,
+                                                 exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale,
+                                                 retryableHTTPMethods: Set<HTTPMethod> = RetryPolicy.defaultRetryableHTTPMethods) -> ConnectionLostRetryPolicy {
+        ConnectionLostRetryPolicy(retryLimit: retryLimit,
+                                  exponentialBackoffBase: exponentialBackoffBase,
+                                  exponentialBackoffScale: exponentialBackoffScale,
+                                  retryableHTTPMethods: retryableHTTPMethods)
+    }
+}
+#endif

+ 107 - 11
Source/ServerTrustEvaluation.swift

@@ -153,19 +153,19 @@ public final class RevocationTrustEvaluator: ServerTrustEvaluating {
     private let validateHost: Bool
     private let options: Options
 
-    /// Creates a `RevocationTrustEvaluator`.
+    /// Creates a `RevocationTrustEvaluator` using the provided parameters.
     ///
     /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
     ///         `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
     ///
     /// - Parameters:
-    ///   - performDefaultValidation:     Determines whether default validation should be performed in addition to
-    ///                                   evaluating the pinned certificates. `true` by default.
-    ///   - validateHost:                 Determines whether or not the evaluator should validate the host, in addition
-    ///                                   to performing the default evaluation, even if `performDefaultValidation` is
-    ///                                   `false`. `true` by default.
-    ///   - options:                      The `Options` to use to check the revocation status of the certificate. `.any`
-    ///                                   by default.
+    ///   - performDefaultValidation: Determines whether default validation should be performed in addition to
+    ///                               evaluating the pinned certificates. `true` by default.
+    ///   - validateHost:             Determines whether or not the evaluator should validate the host, in addition to
+    ///                               performing the default evaluation, even if `performDefaultValidation` is `false`.
+    ///                               `true` by default.
+    ///   - options:                  The `Options` to use to check the revocation status of the certificate. `.any` by
+    ///                               default.
     public init(performDefaultValidation: Bool = true, validateHost: Bool = true, options: Options = .any) {
         self.performDefaultValidation = performDefaultValidation
         self.validateHost = validateHost
@@ -191,6 +191,35 @@ public final class RevocationTrustEvaluator: ServerTrustEvaluating {
     }
 }
 
+#if swift(>=5.5)
+extension ServerTrustEvaluating where Self == RevocationTrustEvaluator {
+    /// Provides a default `RevocationTrustEvaluator` instance.
+    public static var revocationChecking: RevocationTrustEvaluator { RevocationTrustEvaluator() }
+
+    /// Creates a `RevocationTrustEvaluator` using the provided parameters.
+    ///
+    /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
+    ///         `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
+    ///
+    /// - Parameters:
+    ///   - performDefaultValidation: Determines whether default validation should be performed in addition to
+    ///                               evaluating the pinned certificates. `true` by default.
+    ///   - validateHost:             Determines whether or not the evaluator should validate the host, in addition
+    ///                               to performing the default evaluation, even if `performDefaultValidation` is
+    ///                               `false`. `true` by default.
+    ///   - options:                  The `Options` to use to check the revocation status of the certificate. `.any`
+    ///                               by default.
+    /// - Returns:                    The `RevocationTrustEvaluator`.
+    public static func revocationChecking(performDefaultValidation: Bool = true,
+                                          validateHost: Bool = true,
+                                          options: RevocationTrustEvaluator.Options = .any) -> RevocationTrustEvaluator {
+        RevocationTrustEvaluator(performDefaultValidation: performDefaultValidation,
+                                 validateHost: validateHost,
+                                 options: options)
+    }
+}
+#endif
+
 /// Uses the pinned certificates to validate the server trust. The server trust is considered valid if one of the pinned
 /// certificates match one of the server certificates. By validating both the certificate chain and host, certificate
 /// pinning provides a very secure form of server trust validation mitigating most, if not all, MITM attacks.
@@ -202,7 +231,7 @@ public final class PinnedCertificatesTrustEvaluator: ServerTrustEvaluating {
     private let performDefaultValidation: Bool
     private let validateHost: Bool
 
-    /// Creates a `PinnedCertificatesTrustEvaluator`.
+    /// Creates a `PinnedCertificatesTrustEvaluator` from the provided parameters.
     ///
     /// - Parameters:
     ///   - certificates:                 The certificates to use to evaluate the trust. All `cer`, `crt`, and `der`
@@ -254,6 +283,36 @@ public final class PinnedCertificatesTrustEvaluator: ServerTrustEvaluating {
     }
 }
 
+#if swift(>=5.5)
+extension ServerTrustEvaluating where Self == PinnedCertificatesTrustEvaluator {
+    /// Provides a default `PinnedCertificatesTrustEvaluator` instance.
+    public static var pinnedCertificates: PinnedCertificatesTrustEvaluator { PinnedCertificatesTrustEvaluator() }
+
+    /// Creates a `PinnedCertificatesTrustEvaluator` using the provided parameters.
+    ///
+    /// - Parameters:
+    ///   - certificates:                 The certificates to use to evaluate the trust. All `cer`, `crt`, and `der`
+    ///                                   certificates in `Bundle.main` by default.
+    ///   - acceptSelfSignedCertificates: Adds the provided certificates as anchors for the trust evaluation, allowing
+    ///                                   self-signed certificates to pass. `false` by default. THIS SETTING SHOULD BE
+    ///                                   FALSE IN PRODUCTION!
+    ///   - performDefaultValidation:     Determines whether default validation should be performed in addition to
+    ///                                   evaluating the pinned certificates. `true` by default.
+    ///   - validateHost:                 Determines whether or not the evaluator should validate the host, in addition
+    ///                                   to performing the default evaluation, even if `performDefaultValidation` is
+    ///                                   `false`. `true` by default.
+    public static func pinnedCertificates(certificates: [SecCertificate] = Bundle.main.af.certificates,
+                                          acceptSelfSignedCertificates: Bool = false,
+                                          performDefaultValidation: Bool = true,
+                                          validateHost: Bool = true) -> PinnedCertificatesTrustEvaluator {
+        PinnedCertificatesTrustEvaluator(certificates: certificates,
+                                         acceptSelfSignedCertificates: acceptSelfSignedCertificates,
+                                         performDefaultValidation: performDefaultValidation,
+                                         validateHost: validateHost)
+    }
+}
+#endif
+
 /// Uses the pinned public keys to validate the server trust. The server trust is considered valid if one of the pinned
 /// public keys match one of the server certificate public keys. By validating both the certificate chain and host,
 /// public key pinning provides a very secure form of server trust validation mitigating most, if not all, MITM attacks.
@@ -264,7 +323,7 @@ public final class PublicKeysTrustEvaluator: ServerTrustEvaluating {
     private let performDefaultValidation: Bool
     private let validateHost: Bool
 
-    /// Creates a `PublicKeysTrustEvaluator`.
+    /// Creates a `PublicKeysTrustEvaluator` from the provided parameters.
     ///
     /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
     ///         `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
@@ -318,12 +377,38 @@ public final class PublicKeysTrustEvaluator: ServerTrustEvaluating {
     }
 }
 
+#if swift(>=5.5)
+extension ServerTrustEvaluating where Self == PublicKeysTrustEvaluator {
+    /// Provides a default `PublicKeysTrustEvaluator` instance.
+    public static var publicKeys: PublicKeysTrustEvaluator { PublicKeysTrustEvaluator() }
+
+    /// Creates a `PublicKeysTrustEvaluator` from the provided parameters.
+    ///
+    /// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
+    ///         `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
+    ///
+    /// - Parameters:
+    ///   - keys:                     The `SecKey`s to use to validate public keys. Defaults to the public keys of all
+    ///                               certificates included in the main bundle.
+    ///   - performDefaultValidation: Determines whether default validation should be performed in addition to
+    ///                               evaluating the pinned certificates. `true` by default.
+    ///   - validateHost:             Determines whether or not the evaluator should validate the host, in addition to
+    ///                               performing the default evaluation, even if `performDefaultValidation` is `false`.
+    ///                               `true` by default.
+    public static func publicKeys(keys: [SecKey] = Bundle.main.af.publicKeys,
+                                  performDefaultValidation: Bool = true,
+                                  validateHost: Bool = true) -> PublicKeysTrustEvaluator {
+        PublicKeysTrustEvaluator(keys: keys, performDefaultValidation: performDefaultValidation, validateHost: validateHost)
+    }
+}
+#endif
+
 /// Uses the provided evaluators to validate the server trust. The trust is only considered valid if all of the
 /// evaluators consider it valid.
 public final class CompositeTrustEvaluator: ServerTrustEvaluating {
     private let evaluators: [ServerTrustEvaluating]
 
-    /// Creates a `CompositeTrustEvaluator`.
+    /// Creates a `CompositeTrustEvaluator` from the provided evaluators.
     ///
     /// - Parameter evaluators: The `ServerTrustEvaluating` values used to evaluate the server trust.
     public init(evaluators: [ServerTrustEvaluating]) {
@@ -335,6 +420,17 @@ public final class CompositeTrustEvaluator: ServerTrustEvaluating {
     }
 }
 
+#if swift(>=5.5)
+extension ServerTrustEvaluating where Self == CompositeTrustEvaluator {
+    /// Creates a `CompositeTrustEvaluator` from the provided evaluators.
+    ///
+    /// - Parameter evaluators: The `ServerTrustEvaluating` values used to evaluate the server trust.
+    public static func composite(evaluators: [ServerTrustEvaluating]) -> CompositeTrustEvaluator {
+        CompositeTrustEvaluator(evaluators: evaluators)
+    }
+}
+#endif
+
 /// Disables all evaluation which in turn will always consider any server trust as valid.
 ///
 /// - Note: Instead of disabling server trust evaluation, it's a better idea to configure systems to properly trust test

+ 23 - 0
Tests/CachedResponseHandlerTests.swift

@@ -208,6 +208,29 @@ final class CachedResponseHandlerTestCase: BaseTestCase {
     }
 }
 
+#if swift(>=5.5)
+final class StaticCachedResponseHandlerTests: BaseTestCase {
+    func takeCachedResponseHandler(_ handler: CachedResponseHandler) {
+        _ = handler
+    }
+
+    func testThatCacheResponseCacherCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        takeCachedResponseHandler(.cache)
+    }
+
+    func testThatDoNotCacheResponseCacherCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        takeCachedResponseHandler(.doNotCache)
+    }
+
+    func testThatModifyResponseCacherCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        takeCachedResponseHandler(.modify { _, _ in nil })
+    }
+}
+#endif
+
 // MARK: -
 
 extension Session {

+ 18 - 0
Tests/ParameterEncoderTests.swift

@@ -901,6 +901,24 @@ final class URLEncodedFormEncoderTests: BaseTestCase {
     }
 }
 
+#if swift(>=5.5)
+final class StaticParameterEncoderInstanceTests: BaseTestCase {
+    func takeParameterEncoder(_ parameterEncoder: ParameterEncoder) {
+        _ = parameterEncoder
+    }
+
+    func testThatJSONParameterEncoderCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        takeParameterEncoder(.json())
+    }
+
+    func testThatURLEncodedFormParameterEncoderCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        takeParameterEncoder(.urlEncodedForm())
+    }
+}
+#endif
+
 private struct EncodableStruct: Encodable {
     let one = "one"
     let two = 2

+ 23 - 0
Tests/RedirectHandlerTests.swift

@@ -219,3 +219,26 @@ final class RedirectHandlerTestCase: BaseTestCase {
         XCTAssertEqual(response?.response?.statusCode, 200)
     }
 }
+
+#if swift(>=5.5)
+final class StaticRedirectHandlerTests: BaseTestCase {
+    func takeRedirectHandler(_ handler: RedirectHandler) {
+        _ = handler
+    }
+
+    func testThatFollowRedirectorCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        takeRedirectHandler(.follow)
+    }
+
+    func testThatDoNotFollowRedirectorCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        takeRedirectHandler(.doNotFollow)
+    }
+
+    func testThatModifyRedirectorCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        takeRedirectHandler(.modify { _, _, _ in nil })
+    }
+}
+#endif

+ 43 - 0
Tests/RequestInterceptorTests.swift

@@ -527,6 +527,49 @@ final class InterceptorRequestTests: BaseTestCase {
     }
 }
 
+// MARK: - Static Accessors
+
+#if swift(>=5.5)
+final class StaticAccessorTests: BaseTestCase {
+    func consumeRequestAdapter(_ requestAdapter: RequestAdapter) {
+        _ = requestAdapter
+    }
+
+    func consumeRequestRetrier(_ requestRetrier: RequestRetrier) {
+        _ = requestRetrier
+    }
+
+    func consumeRequestInterceptor(_ requestInterceptor: RequestInterceptor) {
+        _ = requestInterceptor
+    }
+
+    func testThatAdapterCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeRequestAdapter(.adapter { request, _, completion in completion(.success(request)) })
+    }
+
+    func testThatRetrierCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeRequestRetrier(.retrier { _, _, _, completion in completion(.doNotRetry) })
+    }
+
+    func testThatInterceptorCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeRequestInterceptor(.interceptor())
+    }
+
+    func testThatRetryPolicyCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeRequestInterceptor(.retryPolicy())
+    }
+
+    func testThatConnectionLostRetryPolicyCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeRequestInterceptor(.connectionLostRetryPolicy())
+    }
+}
+#endif
+
 // MARK: - Helpers
 
 /// Class which captures the output of any underlying `RequestInterceptor`.

+ 69 - 0
Tests/ResponseSerializationTests.swift

@@ -456,6 +456,57 @@ final class DataResponseSerializationTestCase: BaseTestCase {
     }
 }
 
+#if swift(>=5.5)
+final class StaticSerializationTests: BaseTestCase {
+    func consumeDownloadResponseSerializer<Serializer: DownloadResponseSerializerProtocol>(_ responseSerializer: Serializer) {
+        _ = responseSerializer
+    }
+
+    func consumeStreamSerializer<Serializer: DataStreamSerializer>(_ serializer: Serializer) {
+        _ = serializer
+    }
+
+    func consumeResponseSerializer<Serializer: ResponseSerializer>(_ responseSerializer: Serializer) {
+        _ = responseSerializer
+    }
+
+    func testThatURLSerializerCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeDownloadResponseSerializer(.url)
+    }
+
+    func testThatDataSerializerCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeResponseSerializer(.data())
+    }
+
+    func testThatStringSerializerCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeResponseSerializer(.string())
+    }
+
+    func testThatDecodableSerializerCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeResponseSerializer(.decodable(of: TestResponse.self))
+    }
+
+    func testThatPassthroughStreamSerializerCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeStreamSerializer(.passthrough)
+    }
+
+    func testThatStringStreamSerializerCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeStreamSerializer(.string)
+    }
+
+    func testThatDecodableStreamSerializerCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeStreamSerializer(.decodable(of: TestResponse.self))
+    }
+}
+#endif
+
 // MARK: -
 
 final class URLResponseSerializerTests: BaseTestCase {
@@ -1315,6 +1366,24 @@ final class DataPreprocessorTests: BaseTestCase {
     }
 }
 
+#if swift(>=5.5)
+final class StaticDataPreprocessorTests: BaseTestCase {
+    func consumeDataPreprocessor(_ dataPreprocessor: DataPreprocessor) {
+        _ = dataPreprocessor
+    }
+
+    func testThatPassthroughCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeDataPreprocessor(.passthrough)
+    }
+
+    func testThatGoogleXSSICanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeDataPreprocessor(.googleXSSI)
+    }
+}
+#endif
+
 extension HTTPURLResponse {
     convenience init(statusCode: Int, headers: HTTPHeaders? = nil) {
         let url = Endpoint().url

+ 24 - 0
Tests/ServerTrustEvaluatorTests.swift

@@ -1415,4 +1415,28 @@ class ServerTrustPolicyCertificatesInBundleTestCase: ServerTrustPolicyTestCase {
         #endif
     }
 }
+
+#if swift(>=5.5)
+final class StaticServerTrustAccessorTests: ServerTrustPolicyTestCase {
+    func consumeServerTrustEvaluator(_ evaluator: ServerTrustEvaluating) {
+        _ = evaluator
+    }
+
+    func testThatRevocationEvaluatorCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeServerTrustEvaluator(.revocation())
+    }
+
+    func testThatPinnedCertificatesEvaluatorCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeServerTrustEvaluator(.pinnedCertificates())
+    }
+
+    func testThatPublicKeysEvaluatorCanBeCreatedStaticallyFromProtocol() {
+        // Given, When, Then
+        consumeServerTrustEvaluator(.publicKeys())
+    }
+}
+#endif
+
 #endif

+ 1 - 1
Tests/SessionTests.swift

@@ -300,7 +300,7 @@ final class SessionTestCase: BaseTestCase {
 
         XCTAssertTrue(userAgent?.contains(alamofireVersion) == true)
         XCTAssertTrue(userAgent?.contains(osNameVersion) == true)
-        XCTAssertTrue(userAgent?.contains("xctest/Unknown") == true)
+        XCTAssertTrue(userAgent?.contains("xctest/") == true)
     }
 
     // MARK: Tests - Supported Accept-Encodings