|
|
@@ -45,7 +45,33 @@ public protocol DownloadResponseSerializerProtocol {
|
|
|
}
|
|
|
|
|
|
/// A serializer that can handle both data and download responses.
|
|
|
-public protocol ResponseSerializer: DataResponseSerializerProtocol & DownloadResponseSerializerProtocol { }
|
|
|
+public protocol ResponseSerializer: DataResponseSerializerProtocol & DownloadResponseSerializerProtocol {
|
|
|
+ var emptyRequestMethods: Set<HTTPMethod> { get }
|
|
|
+ var emptyResponseCodes: Set<Int> { get }
|
|
|
+}
|
|
|
+
|
|
|
+extension ResponseSerializer {
|
|
|
+ public static var defaultEmptyRequestMethods: Set<HTTPMethod> { return [.head] }
|
|
|
+ public static var defaultEmptyResponseCodes: Set<Int> { return [204, 205] }
|
|
|
+
|
|
|
+ var emptyRequestMethods: Set<HTTPMethod> { return Self.defaultEmptyRequestMethods }
|
|
|
+ var emptyResponseCodes: Set<Int> { return Self.defaultEmptyResponseCodes }
|
|
|
+
|
|
|
+ public func requestAllowsEmptyResponseData(_ request: URLRequest?) -> Bool? {
|
|
|
+ return request.flatMap { $0.httpMethod }
|
|
|
+ .flatMap(HTTPMethod.init)
|
|
|
+ .map { emptyRequestMethods.contains($0) }
|
|
|
+ }
|
|
|
+
|
|
|
+ public func responseAllowsEmptyResponseData(_ response: HTTPURLResponse?) -> Bool? {
|
|
|
+ return response.flatMap { $0.statusCode }
|
|
|
+ .map { emptyResponseCodes.contains($0) }
|
|
|
+ }
|
|
|
+
|
|
|
+ public func emptyResponseAllowed(forRequest request: URLRequest?, response: HTTPURLResponse?) -> Bool {
|
|
|
+ return requestAllowsEmptyResponseData(request) ?? responseAllowsEmptyResponseData(response) ?? false
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
/// By default, any serializer declared to conform to both types will get file serialization for free, as it just feeds
|
|
|
/// the data read from disk into the data response serializer.
|
|
|
@@ -72,66 +98,6 @@ public extension DownloadResponseSerializerProtocol where Self: DataResponseSeri
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// MARK: - AnyResponseSerializer
|
|
|
-
|
|
|
-/// A generic `ResponseSerializer` conforming type.
|
|
|
-public final class AnyResponseSerializer<Value>: ResponseSerializer {
|
|
|
- /// A closure which can be used to serialize data responses.
|
|
|
- public typealias DataSerializer = (_ request: URLRequest?, _ response: HTTPURLResponse?, _ data: Data?, _ error: Error?) throws -> Value
|
|
|
- /// A closure which can be used to serialize download reponses.
|
|
|
- public typealias DownloadSerializer = (_ request: URLRequest?, _ response: HTTPURLResponse?, _ fileURL: URL?, _ error: Error?) throws -> Value
|
|
|
-
|
|
|
- let dataSerializer: DataSerializer
|
|
|
- let downloadSerializer: DownloadSerializer?
|
|
|
-
|
|
|
- /// Initialze the instance with both a `DataSerializer` closure and a `DownloadSerializer` closure.
|
|
|
- ///
|
|
|
- /// - Parameters:
|
|
|
- /// - dataSerializer: A `DataSerializer` closure.
|
|
|
- /// - downloadSerializer: A `DownloadSerializer` closure.
|
|
|
- public init(dataSerializer: @escaping DataSerializer, downloadSerializer: @escaping DownloadSerializer) {
|
|
|
- self.dataSerializer = dataSerializer
|
|
|
- self.downloadSerializer = downloadSerializer
|
|
|
- }
|
|
|
-
|
|
|
- /// Initialze the instance with a `DataSerializer` closure. Download serialization will fallback to a default
|
|
|
- /// implementation.
|
|
|
- ///
|
|
|
- /// - Parameters:
|
|
|
- /// - dataSerializer: A `DataSerializer` closure.
|
|
|
- public init(dataSerializer: @escaping DataSerializer) {
|
|
|
- self.dataSerializer = dataSerializer
|
|
|
- self.downloadSerializer = nil
|
|
|
- }
|
|
|
-
|
|
|
- public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Value {
|
|
|
- return try dataSerializer(request, response, data, error)
|
|
|
- }
|
|
|
-
|
|
|
- public func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> Value {
|
|
|
- return try downloadSerializer?(request, response, fileURL, error) ?? { (request, response, fileURL, error) in
|
|
|
- guard error == nil else { throw error! }
|
|
|
-
|
|
|
- guard let fileURL = fileURL else {
|
|
|
- throw AFError.responseSerializationFailed(reason: .inputFileNil)
|
|
|
- }
|
|
|
-
|
|
|
- let data: Data
|
|
|
- do {
|
|
|
- data = try Data(contentsOf: fileURL)
|
|
|
- } catch {
|
|
|
- throw AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL))
|
|
|
- }
|
|
|
-
|
|
|
- do {
|
|
|
- return try serialize(request: request, response: response, data: data, error: error)
|
|
|
- } catch {
|
|
|
- throw error
|
|
|
- }
|
|
|
- }(request, response, fileURL, error)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
// MARK: - Default
|
|
|
|
|
|
extension DataRequest {
|
|
|
@@ -303,19 +269,35 @@ extension DataRequest {
|
|
|
/// request returning `nil` or no data is considered an error. However, if the response is has a status code valid for
|
|
|
/// empty responses (`204`, `205`), then an empty `Data` value is returned.
|
|
|
public final class DataResponseSerializer: ResponseSerializer {
|
|
|
+ /// HTTP response codes for which empty responses are allowed.
|
|
|
+ public let emptyResponseCodes: Set<Int>
|
|
|
+ /// HTTP request methods for which empty responses are allowed.
|
|
|
+ public let emptyRequestMethods: Set<HTTPMethod>
|
|
|
|
|
|
- public init() { }
|
|
|
+ /// Creates an instance using the provided values.
|
|
|
+ ///
|
|
|
+ /// - Parameters:
|
|
|
+ /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. Defaults to
|
|
|
+ /// `[204, 205]`.
|
|
|
+ /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. Defaults to `[.head]`.
|
|
|
+ public init(emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
|
|
|
+ emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) {
|
|
|
+ self.emptyResponseCodes = emptyResponseCodes
|
|
|
+ self.emptyRequestMethods = emptyRequestMethods
|
|
|
+ }
|
|
|
|
|
|
public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Data {
|
|
|
guard error == nil else { throw error! }
|
|
|
|
|
|
- if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return Data() }
|
|
|
+ guard let data = data, !data.isEmpty else {
|
|
|
+ guard emptyResponseAllowed(forRequest: request, response: response) else {
|
|
|
+ throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
|
|
|
+ }
|
|
|
|
|
|
- guard let validData = data else {
|
|
|
- throw AFError.responseSerializationFailed(reason: .inputDataNil)
|
|
|
+ return Data()
|
|
|
}
|
|
|
|
|
|
- return validData
|
|
|
+ return data
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -347,23 +329,38 @@ extension DownloadRequest {
|
|
|
/// data is considered an error. However, if the response is has a status code valid for empty responses (`204`, `205`),
|
|
|
/// then an empty `String` is returned.
|
|
|
public final class StringResponseSerializer: ResponseSerializer {
|
|
|
- let encoding: String.Encoding?
|
|
|
-
|
|
|
- /// Creates an instance with the given `String.Encoding`.
|
|
|
+ /// Optional string encoding used to validate the response.
|
|
|
+ public let encoding: String.Encoding?
|
|
|
+ /// HTTP response codes for which empty responses are allowed.
|
|
|
+ public let emptyResponseCodes: Set<Int>
|
|
|
+ /// HTTP request methods for which empty responses are allowed.
|
|
|
+ public let emptyRequestMethods: Set<HTTPMethod>
|
|
|
+
|
|
|
+ /// Creates an instance with the provided values.
|
|
|
///
|
|
|
- /// - Parameter 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`.
|
|
|
- public init(encoding: String.Encoding? = nil) {
|
|
|
+ /// - Parameters:
|
|
|
+ /// - 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. Defaults to
|
|
|
+ /// `[204, 205]`.
|
|
|
+ /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. Defaults to `[.head]`.
|
|
|
+ public init(encoding: String.Encoding? = nil,
|
|
|
+ emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
|
|
|
+ emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) {
|
|
|
self.encoding = encoding
|
|
|
+ self.emptyResponseCodes = emptyResponseCodes
|
|
|
+ self.emptyRequestMethods = emptyRequestMethods
|
|
|
}
|
|
|
|
|
|
public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> String {
|
|
|
guard error == nil else { throw error! }
|
|
|
|
|
|
- if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return "" }
|
|
|
+ guard let data = data, !data.isEmpty else {
|
|
|
+ guard emptyResponseAllowed(forRequest: request, response: response) else {
|
|
|
+ throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
|
|
|
+ }
|
|
|
|
|
|
- guard let validData = data else {
|
|
|
- throw AFError.responseSerializationFailed(reason: .inputDataNil)
|
|
|
+ return ""
|
|
|
}
|
|
|
|
|
|
var convertedEncoding = encoding
|
|
|
@@ -376,7 +373,7 @@ public final class StringResponseSerializer: ResponseSerializer {
|
|
|
|
|
|
let actualEncoding = convertedEncoding ?? .isoLatin1
|
|
|
|
|
|
- guard let string = String(data: validData, encoding: actualEncoding) else {
|
|
|
+ guard let string = String(data: data, encoding: actualEncoding) else {
|
|
|
throw AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding))
|
|
|
}
|
|
|
|
|
|
@@ -435,28 +432,41 @@ extension DownloadRequest {
|
|
|
/// `nil` or no data is considered an error. However, if the response is has a status code valid for empty responses
|
|
|
/// (`204`, `205`), then an `NSNull` value is returned.
|
|
|
public final class JSONResponseSerializer: ResponseSerializer {
|
|
|
- let options: JSONSerialization.ReadingOptions
|
|
|
-
|
|
|
- /// Creates an instance with the given `JSONSerilization.ReadingOptions`.
|
|
|
+ /// `JSONSerialization.ReadingOptions` used when serializing a response.
|
|
|
+ public let options: JSONSerialization.ReadingOptions
|
|
|
+ /// HTTP response codes for which empty responses are allowed.
|
|
|
+ public let emptyResponseCodes: Set<Int>
|
|
|
+ /// HTTP request methods for which empty responses are allowed.
|
|
|
+ public let emptyRequestMethods: Set<HTTPMethod>
|
|
|
+
|
|
|
+ /// Creates an instance with the provided values.
|
|
|
///
|
|
|
- /// - Parameter options: The options to use. Defaults to `.allowFragments`.
|
|
|
- public init(options: JSONSerialization.ReadingOptions = .allowFragments) {
|
|
|
+ /// - Parameters:
|
|
|
+ /// - options: The options to use. Defaults to `.allowFragments`.
|
|
|
+ /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. Defaults to
|
|
|
+ /// `[204, 205]`.
|
|
|
+ /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. Defaults to `[.head]`.
|
|
|
+ public init(options: JSONSerialization.ReadingOptions = .allowFragments,
|
|
|
+ emptyResponseCodes: Set<Int> = JSONResponseSerializer.defaultEmptyResponseCodes,
|
|
|
+ emptyRequestMethods: Set<HTTPMethod> = JSONResponseSerializer.defaultEmptyRequestMethods) {
|
|
|
self.options = options
|
|
|
+ self.emptyResponseCodes = emptyResponseCodes
|
|
|
+ self.emptyRequestMethods = emptyRequestMethods
|
|
|
}
|
|
|
|
|
|
public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Any {
|
|
|
guard error == nil else { throw error! }
|
|
|
|
|
|
- guard let validData = data, validData.count > 0 else {
|
|
|
- if let response = response, emptyDataStatusCodes.contains(response.statusCode) {
|
|
|
- return NSNull()
|
|
|
+ guard let data = data, !data.isEmpty else {
|
|
|
+ guard emptyResponseAllowed(forRequest: request, response: response) else {
|
|
|
+ throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
|
|
|
}
|
|
|
|
|
|
- throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
|
|
|
+ return NSNull()
|
|
|
}
|
|
|
|
|
|
do {
|
|
|
- return try JSONSerialization.jsonObject(with: validData, options: options)
|
|
|
+ return try JSONSerialization.jsonObject(with: data, options: options)
|
|
|
} catch {
|
|
|
throw AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error))
|
|
|
}
|
|
|
@@ -506,45 +516,58 @@ extension DownloadRequest {
|
|
|
|
|
|
// MARK: - Empty
|
|
|
|
|
|
-/// A type representing an empty response. Use `Empty.response` to get the instance.
|
|
|
+/// A type representing an empty response. Use `Empty.value` to get the instance.
|
|
|
public struct Empty: Decodable {
|
|
|
- public static let response = Empty()
|
|
|
+ public static let value = Empty()
|
|
|
}
|
|
|
|
|
|
// MARK: - JSON Decodable
|
|
|
|
|
|
/// A `ResponseSerializer` that decodes the response data as a generic value using a `JSONDecoder`. By default, a
|
|
|
/// request returning `nil` or no data is considered an error. However, if the response is has a status code valid for
|
|
|
-/// empty responses (`204`, `205`), then the `Empty.response` value is returned.
|
|
|
+/// empty responses (`204`, `205`), then the `Empty.value` value is returned.
|
|
|
public final class JSONDecodableResponseSerializer<T: Decodable>: ResponseSerializer {
|
|
|
- let decoder: JSONDecoder
|
|
|
-
|
|
|
- /// Creates an instance with the given `JSONDecoder` instance.
|
|
|
+ /// The `JSONDecoder` instance used to decode responses.
|
|
|
+ public let decoder: JSONDecoder
|
|
|
+ /// HTTP response codes for which empty responses are allowed.
|
|
|
+ public let emptyResponseCodes: Set<Int>
|
|
|
+ /// HTTP request methods for which empty responses are allowed.
|
|
|
+ public let emptyRequestMethods: Set<HTTPMethod>
|
|
|
+
|
|
|
+ /// Creates an instance using the values provided.
|
|
|
///
|
|
|
- /// - Parameter decoder: A decoder. Defaults to a `JSONDecoder` with default settings.
|
|
|
- public init(decoder: JSONDecoder = JSONDecoder()) {
|
|
|
+ /// - Parameters:
|
|
|
+ /// - decoder: The `JSONDecoder`. Defaults to a `JSONDecoder()`.
|
|
|
+ /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. Defaults to
|
|
|
+ /// `[204, 205]`.
|
|
|
+ /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. Defaults to `[.head]`.
|
|
|
+ public init(decoder: JSONDecoder = JSONDecoder(),
|
|
|
+ emptyResponseCodes: Set<Int> = JSONDecodableResponseSerializer.defaultEmptyResponseCodes,
|
|
|
+ emptyRequestMethods: Set<HTTPMethod> = JSONDecodableResponseSerializer.defaultEmptyRequestMethods) {
|
|
|
self.decoder = decoder
|
|
|
+ self.emptyResponseCodes = emptyResponseCodes
|
|
|
+ self.emptyRequestMethods = emptyRequestMethods
|
|
|
}
|
|
|
|
|
|
public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> T {
|
|
|
guard error == nil else { throw error! }
|
|
|
|
|
|
- guard let validData = data, validData.count > 0 else {
|
|
|
- if let response = response, emptyDataStatusCodes.contains(response.statusCode) {
|
|
|
- guard let emptyResponse = Empty.response as? T else {
|
|
|
- throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)"))
|
|
|
- }
|
|
|
+ guard let data = data, !data.isEmpty else {
|
|
|
+ guard emptyResponseAllowed(forRequest: request, response: response) else {
|
|
|
+ throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
|
|
|
+ }
|
|
|
|
|
|
- return emptyResponse
|
|
|
+ guard let emptyValue = Empty.value as? T else {
|
|
|
+ throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)"))
|
|
|
}
|
|
|
|
|
|
- throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
|
|
|
+ return emptyValue
|
|
|
}
|
|
|
|
|
|
do {
|
|
|
- return try decoder.decode(T.self, from: validData)
|
|
|
+ return try decoder.decode(T.self, from: data)
|
|
|
} catch {
|
|
|
- throw error
|
|
|
+ throw AFError.responseSerializationFailed(reason: .jsonDecodingFailed(error: error))
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -568,6 +591,3 @@ extension DataRequest {
|
|
|
completionHandler: completionHandler)
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-/// A set of HTTP response status code that do not contain response data.
|
|
|
-private let emptyDataStatusCodes: Set<Int> = [204, 205]
|