310 lines
14 KiB
Swift
310 lines
14 KiB
Swift
// swiftlint:disable all
|
|
// swiftformat:disable all
|
|
|
|
//
|
|
// Request.swift
|
|
// AlamofireObjectMapper
|
|
//
|
|
// Created by Tristan Himmelman on 2015-04-30.
|
|
//
|
|
// The MIT License (MIT)
|
|
//
|
|
// Copyright (c) 2014-2015 Tristan Himmelman
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
import Foundation
|
|
import Alamofire
|
|
import ObjectMapper
|
|
|
|
extension DataRequest {
|
|
|
|
enum ErrorCode: Int {
|
|
case noData = 1
|
|
case dataSerializationFailed = 2
|
|
}
|
|
|
|
/// Utility function for extracting JSON from response
|
|
internal static func processResponse(request: URLRequest?, response: HTTPURLResponse?, data: Data?, keyPath: String?) -> Any? {
|
|
|
|
let jsonResponseSerializer = JSONResponseSerializer(options: .allowFragments)
|
|
if let result = try? jsonResponseSerializer.serialize(request: request, response: response, data: data, error: nil) {
|
|
|
|
let JSON: Any?
|
|
if let keyPath = keyPath , keyPath.isEmpty == false {
|
|
JSON = (result as AnyObject?)?.value(forKeyPath: keyPath)
|
|
} else {
|
|
JSON = result
|
|
}
|
|
|
|
return JSON
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
internal static func newError(_ code: ErrorCode, failureReason: String) -> NSError {
|
|
let errorDomain = "com.alamofireobjectmapper.error"
|
|
|
|
let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason]
|
|
let returnError = NSError(domain: errorDomain, code: code.rawValue, userInfo: userInfo)
|
|
|
|
return returnError
|
|
}
|
|
|
|
/// Utility function for checking for errors in response
|
|
internal static func checkResponseForError(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) -> Error? {
|
|
if let error = error {
|
|
return error
|
|
}
|
|
guard let _ = data else {
|
|
let failureReason = "Data could not be serialized. Input data was nil."
|
|
let error = newError(.noData, failureReason: failureReason)
|
|
return error
|
|
}
|
|
return nil
|
|
}
|
|
|
|
|
|
/// BaseMappable Object Serializer
|
|
public static func ObjectMapperSerializer<T: BaseMappable>(_ keyPath: String?, mapToObject object: T? = nil, context: MapContext? = nil) -> MappableResponseSerializer<T> {
|
|
|
|
return MappableResponseSerializer(keyPath, mapToObject: object, context: context, serializeCallback: {
|
|
request, response, data, error in
|
|
|
|
let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath)
|
|
|
|
if let object = object {
|
|
_ = Mapper<T>(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject, toObject: object)
|
|
return object
|
|
} else if let parsedObject = Mapper<T>(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject){
|
|
return parsedObject
|
|
}
|
|
|
|
let failureReason = "ObjectMapper failed to serialize response."
|
|
throw AFError.responseSerializationFailed(reason: .decodingFailed(error: newError(.dataSerializationFailed, failureReason: failureReason)))
|
|
|
|
})
|
|
}
|
|
|
|
/// ImmutableMappable Array Serializer
|
|
public static func ObjectMapperImmutableSerializer<T: ImmutableMappable>(_ keyPath: String?, context: MapContext? = nil) -> MappableResponseSerializer<T> {
|
|
|
|
return MappableResponseSerializer(keyPath, context: context, serializeCallback: {
|
|
request, response, data, error in
|
|
|
|
let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath)
|
|
|
|
if let JSONObject = JSONObject,
|
|
let parsedObject = (try? Mapper<T>(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject) as T) {
|
|
return parsedObject
|
|
} else {
|
|
let failureReason = "ObjectMapper failed to serialize response."
|
|
throw AFError.responseSerializationFailed(reason: .decodingFailed(error: newError(.dataSerializationFailed, failureReason: failureReason)))
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
Adds a handler to be called once the request has finished.
|
|
|
|
- parameter queue: The queue on which the completion handler is dispatched.
|
|
- parameter keyPath: The key path where object mapping should be performed
|
|
- parameter object: An object to perform the mapping on to
|
|
- parameter completionHandler: A closure to be executed once the request has finished and the data has been mapped by ObjectMapper.
|
|
|
|
- returns: The request.
|
|
*/
|
|
@discardableResult
|
|
public func responseObject<T: BaseMappable>(queue: DispatchQueue = .main, keyPath: String? = nil, mapToObject object: T? = nil, context: MapContext? = nil, completionHandler: @escaping (AFDataResponse<T>) -> Void) -> Self {
|
|
return response(queue: queue, responseSerializer: DataRequest.ObjectMapperSerializer(keyPath, mapToObject: object, context: context), completionHandler: completionHandler)
|
|
}
|
|
|
|
@discardableResult
|
|
public func responseObject<T: ImmutableMappable>(queue: DispatchQueue = .main, keyPath: String? = nil, mapToObject object: T? = nil, context: MapContext? = nil, completionHandler: @escaping (AFDataResponse<T>) -> Void) -> Self {
|
|
return response(queue: queue, responseSerializer: DataRequest.ObjectMapperImmutableSerializer(keyPath, context: context), completionHandler: completionHandler)
|
|
}
|
|
|
|
/// BaseMappable Array Serializer
|
|
public static func ObjectMapperArraySerializer<T: BaseMappable>(_ keyPath: String?, context: MapContext? = nil) -> MappableArrayResponseSerializer<T> {
|
|
|
|
|
|
|
|
return MappableArrayResponseSerializer(keyPath, context: context, serializeCallback: {
|
|
request, response, data, error in
|
|
|
|
let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath)
|
|
|
|
if let parsedObject = Mapper<T>(context: context, shouldIncludeNilValues: false).mapArray(JSONObject: JSONObject){
|
|
return parsedObject
|
|
}
|
|
|
|
let failureReason = "ObjectMapper failed to serialize response."
|
|
throw AFError.responseSerializationFailed(reason: .decodingFailed(error: newError(.dataSerializationFailed, failureReason: failureReason)))
|
|
})
|
|
}
|
|
|
|
|
|
/// ImmutableMappable Array Serializer
|
|
public static func ObjectMapperImmutableArraySerializer<T: ImmutableMappable>(_ keyPath: String?, context: MapContext? = nil) -> MappableArrayResponseSerializer<T> {
|
|
return MappableArrayResponseSerializer(keyPath, context: context, serializeCallback: {
|
|
request, response, data, error in
|
|
|
|
if let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath){
|
|
|
|
if let parsedObject = try? Mapper<T>(context: context, shouldIncludeNilValues: false).mapArray(JSONObject: JSONObject) as [T] {
|
|
return parsedObject
|
|
}
|
|
}
|
|
|
|
let failureReason = "ObjectMapper failed to serialize response."
|
|
throw AFError.responseSerializationFailed(reason: .decodingFailed(error: newError(.dataSerializationFailed, failureReason: failureReason)))
|
|
})
|
|
}
|
|
|
|
/**
|
|
Adds a handler to be called once the request has finished. T: BaseMappable
|
|
|
|
- parameter queue: The queue on which the completion handler is dispatched.
|
|
- parameter keyPath: The key path where object mapping should be performed
|
|
- parameter completionHandler: A closure to be executed once the request has finished and the data has been mapped by ObjectMapper.
|
|
|
|
- returns: The request.
|
|
*/
|
|
@discardableResult
|
|
public func responseArray<T: BaseMappable>(queue: DispatchQueue = .main, keyPath: String? = nil, context: MapContext? = nil, completionHandler: @escaping (AFDataResponse<[T]>) -> Void) -> Self {
|
|
return response(queue: queue, responseSerializer: DataRequest.ObjectMapperArraySerializer(keyPath, context: context), completionHandler: completionHandler)
|
|
}
|
|
|
|
/**
|
|
Adds a handler to be called once the request has finished. T: ImmutableMappable
|
|
|
|
- parameter queue: The queue on which the completion handler is dispatched.
|
|
- parameter keyPath: The key path where object mapping should be performed
|
|
- parameter completionHandler: A closure to be executed once the request has finished and the data has been mapped by ObjectMapper.
|
|
|
|
- returns: The request.
|
|
*/
|
|
@discardableResult
|
|
public func responseArray<T: ImmutableMappable>(queue: DispatchQueue = .main, keyPath: String? = nil, context: MapContext? = nil, completionHandler: @escaping (AFDataResponse<[T]>) -> Void) -> Self {
|
|
return response(queue: queue, responseSerializer: DataRequest.ObjectMapperImmutableArraySerializer(keyPath, context: context), completionHandler: completionHandler)
|
|
}
|
|
}
|
|
|
|
public final class MappableResponseSerializer<T: BaseMappable>: ResponseSerializer {
|
|
/// The `JSONDecoder` instance used to decode responses.
|
|
public let decoder: DataDecoder = 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>
|
|
|
|
public let keyPath: String?
|
|
public let context: MapContext?
|
|
public let object: T?
|
|
|
|
public let serializeCallback: (URLRequest?,HTTPURLResponse?, Data?,Error?) throws -> T
|
|
|
|
/// Creates an instance using the values provided.
|
|
///
|
|
/// - Parameters:
|
|
/// - keyPath:
|
|
/// - object:
|
|
/// - context:
|
|
/// - 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]`.
|
|
/// - serializeCallback:
|
|
public init(_ keyPath: String?, mapToObject object: T? = nil, context: MapContext? = nil,
|
|
emptyResponseCodes: Set<Int> = MappableResponseSerializer.defaultEmptyResponseCodes,
|
|
emptyRequestMethods: Set<HTTPMethod> = MappableResponseSerializer.defaultEmptyRequestMethods, serializeCallback: @escaping (URLRequest?,HTTPURLResponse?, Data?,Error?) throws -> T) {
|
|
|
|
self.emptyResponseCodes = emptyResponseCodes
|
|
self.emptyRequestMethods = emptyRequestMethods
|
|
|
|
self.keyPath = keyPath
|
|
self.context = context
|
|
self.object = object
|
|
self.serializeCallback = serializeCallback
|
|
}
|
|
|
|
public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> T {
|
|
guard error == nil else { throw error! }
|
|
|
|
guard let data = data, !data.isEmpty else {
|
|
guard emptyResponseAllowed(forRequest: request, response: response) else {
|
|
throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
|
|
}
|
|
|
|
guard let emptyValue = Empty.value as? T else {
|
|
throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)"))
|
|
}
|
|
|
|
return emptyValue
|
|
}
|
|
return try self.serializeCallback(request, response, data, error)
|
|
}
|
|
}
|
|
|
|
public final class MappableArrayResponseSerializer<T: BaseMappable>: ResponseSerializer {
|
|
/// The `JSONDecoder` instance used to decode responses.
|
|
public let decoder: DataDecoder = 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>
|
|
|
|
public let keyPath: String?
|
|
public let context: MapContext?
|
|
|
|
public let serializeCallback: (URLRequest?,HTTPURLResponse?, Data?,Error?) throws -> [T]
|
|
/// Creates an instance using the values provided.
|
|
///
|
|
/// - Parameters:
|
|
/// - keyPath:
|
|
/// - context:
|
|
/// - 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]`.
|
|
/// - serializeCallback:
|
|
public init(_ keyPath: String?, context: MapContext? = nil, serializeCallback: @escaping (URLRequest?,HTTPURLResponse?, Data?,Error?) throws -> [T],
|
|
emptyResponseCodes: Set<Int> = MappableArrayResponseSerializer.defaultEmptyResponseCodes,
|
|
emptyRequestMethods: Set<HTTPMethod> = MappableArrayResponseSerializer.defaultEmptyRequestMethods) {
|
|
self.emptyResponseCodes = emptyResponseCodes
|
|
self.emptyRequestMethods = emptyRequestMethods
|
|
|
|
self.keyPath = keyPath
|
|
self.context = context
|
|
self.serializeCallback = serializeCallback
|
|
}
|
|
|
|
public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> [T] {
|
|
guard error == nil else { throw error! }
|
|
|
|
guard let data = data, !data.isEmpty else {
|
|
guard emptyResponseAllowed(forRequest: request, response: response) else {
|
|
throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength)
|
|
}
|
|
|
|
return []
|
|
}
|
|
return try self.serializeCallback(request, response, data, error)
|
|
}
|
|
}
|