diff --git a/AlamofireObjectMapper.podspec b/AlamofireObjectMapper.podspec index b58efb3..bdc78be 100644 --- a/AlamofireObjectMapper.podspec +++ b/AlamofireObjectMapper.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AlamofireObjectMapper" - s.version = "5.2.0" + s.version = "5.2.1" s.license = { :type => "MIT", :file => "LICENSE" } s.summary = "An extension to Alamofire which automatically converts JSON response data into swift objects using ObjectMapper" s.homepage = "https://github.com/tristanhimmelman/AlamofireObjectMapper" @@ -17,6 +17,6 @@ Pod::Spec.new do |s| s.requires_arc = true s.source_files = 'AlamofireObjectMapper/**/*.swift' - s.dependency 'Alamofire', '~> 4.7' + s.dependency 'Alamofire', '~> 5.0.0.beta.1' s.dependency 'ObjectMapper', '~> 3.4' end diff --git a/AlamofireObjectMapper/AlamofireObjectMapper.swift b/AlamofireObjectMapper/AlamofireObjectMapper.swift index b181e84..64e7e80 100644 --- a/AlamofireObjectMapper/AlamofireObjectMapper.swift +++ b/AlamofireObjectMapper/AlamofireObjectMapper.swift @@ -32,19 +32,6 @@ import ObjectMapper extension DataRequest { - enum ErrorCode: Int { - case noData = 1 - case dataSerializationFailed = 2 - } - - 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? { @@ -59,61 +46,17 @@ extension DataRequest { return nil } - /// Utility function for extracting JSON from response - internal static func processResponse(request: URLRequest?, response: HTTPURLResponse?, data: Data?, keyPath: String?) -> Any? { - let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) - let result = jsonResponseSerializer.serializeResponse(request, response, data, nil) - - let JSON: Any? - if let keyPath = keyPath , keyPath.isEmpty == false { - JSON = (result.value as AnyObject?)?.value(forKeyPath: keyPath) - } else { - JSON = result.value - } - - return JSON - } /// BaseMappable Object Serializer - public static func ObjectMapperSerializer(_ keyPath: String?, mapToObject object: T? = nil, context: MapContext? = nil) -> DataResponseSerializer { - return DataResponseSerializer { request, response, data, error in - if let error = checkResponseForError(request: request, response: response, data: data, error: error){ - return .failure(error) - } - - let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath) - - if let object = object { - _ = Mapper(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject, toObject: object) - return .success(object) - } else if let parsedObject = Mapper(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject){ - return .success(parsedObject) - } - - let failureReason = "ObjectMapper failed to serialize response." - let error = newError(.dataSerializationFailed, failureReason: failureReason) - return .failure(error) - } + public static func ObjectMapperSerializer(_ keyPath: String?, mapToObject object: T? = nil, context: MapContext? = nil) -> MappableResponseSerializer { + + return MappableResponseSerializer(keyPath, mapToObject: object, context: context) } /// ImmutableMappable Array Serializer - public static func ObjectMapperImmutableSerializer(_ keyPath: String?, context: MapContext? = nil) -> DataResponseSerializer { - return DataResponseSerializer { request, response, data, error in - if let error = checkResponseForError(request: request, response: response, data: data, error: error){ - return .failure(error) - } - - let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath) - - if let JSONObject = JSONObject, - let parsedObject = (try? Mapper(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject)){ - return .success(parsedObject) - } - - let failureReason = "ObjectMapper failed to serialize response." - let error = newError(.dataSerializationFailed, failureReason: failureReason) - return .failure(error) - } + public static func ObjectMapperImmutableSerializer(_ keyPath: String?, context: MapContext? = nil) -> ImmutableMappableResponseSerializer { + + return ImmutableMappableResponseSerializer(keyPath, context: context) } /** @@ -137,8 +80,12 @@ extension DataRequest { } /// BaseMappable Array Serializer - public static func ObjectMapperArraySerializer(_ keyPath: String?, context: MapContext? = nil) -> DataResponseSerializer<[T]> { - return DataResponseSerializer { request, response, data, error in + public static func ObjectMapperArraySerializer(_ keyPath: String?, context: MapContext? = nil) -> MappableArrayResponseSerializer { + + return MappableArrayResponseSerializer(keyPath, context: context) + + + /* return DataResponseSerializer { request, response, data, error in if let error = checkResponseForError(request: request, response: response, data: data, error: error){ return .failure(error) } @@ -151,13 +98,16 @@ extension DataRequest { let failureReason = "ObjectMapper failed to serialize response." let error = newError(.dataSerializationFailed, failureReason: failureReason) - return .failure(error) - } + return .failure(error) + } */ } /// ImmutableMappable Array Serializer - public static func ObjectMapperImmutableArraySerializer(_ keyPath: String?, context: MapContext? = nil) -> DataResponseSerializer<[T]> { - return DataResponseSerializer { request, response, data, error in + public static func ObjectMapperImmutableArraySerializer(_ keyPath: String?, context: MapContext? = nil) -> ImmutableMappableArrayResponseSerializer { + + return ImmutableMappableArrayResponseSerializer(keyPath, context: context) + + /* return DataResponseSerializer { request, response, data, error in if let error = checkResponseForError(request: request, response: response, data: data, error: error){ return .failure(error) } @@ -172,7 +122,7 @@ extension DataRequest { let failureReason = "ObjectMapper failed to serialize response." let error = newError(.dataSerializationFailed, failureReason: failureReason) return .failure(error) - } + } */ } /** @@ -203,3 +153,361 @@ extension DataRequest { return response(queue: queue, responseSerializer: DataRequest.ObjectMapperImmutableArraySerializer(keyPath, context: context), completionHandler: completionHandler) } } + +public final class MappableResponseSerializer: ResponseSerializer { + /// The `JSONDecoder` instance used to decode responses. + public let decoder: DataDecoder + /// HTTP response codes for which empty responses are allowed. + public let emptyResponseCodes: Set + /// HTTP request methods for which empty responses are allowed. + public let emptyRequestMethods: Set + + public let keyPath: String? + public let context: MapContext? + public let object: T? + /// Creates an instance using the values provided. + /// + /// - 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(_ keyPath: String?, mapToObject object: T? = nil, context: MapContext? = nil, decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = MappableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = MappableResponseSerializer.defaultEmptyRequestMethods) { + self.object = object + self.keyPath = keyPath + self.context = context + 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! } + + /* return DataResponseSerializer { request, response, data, error in + if let error = checkResponseForError(request: request, response: response, data: data, error: error){ + return .failure(error) + } + + let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath) + + if let JSONObject = JSONObject, + let parsedObject = (try? Mapper(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject)){ + return .success(parsedObject) + } + + let failureReason = "ObjectMapper failed to serialize response." + let error = newError(.dataSerializationFailed, failureReason: failureReason) + return .failure(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 + } + + let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath) + + if let object = object { + _ = Mapper(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject, toObject: object) + return object + } else if let parsedObject = Mapper(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))) + + + } +} + +public final class ImmutableMappableResponseSerializer: ResponseSerializer { + /// The `JSONDecoder` instance used to decode responses. + public let decoder: DataDecoder + /// HTTP response codes for which empty responses are allowed. + public let emptyResponseCodes: Set + /// HTTP request methods for which empty responses are allowed. + public let emptyRequestMethods: Set + + public let keyPath: String? + public let context: MapContext? + /// Creates an instance using the values provided. + /// + /// - 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(_ keyPath: String?, context: MapContext? = nil, decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = ImmutableMappableResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = ImmutableMappableResponseSerializer.defaultEmptyRequestMethods) { + self.decoder = decoder + self.emptyResponseCodes = emptyResponseCodes + self.emptyRequestMethods = emptyRequestMethods + + self.keyPath = keyPath + self.context = context + } + + public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> T { + guard error == nil else { throw error! } + + /* return DataResponseSerializer { request, response, data, error in + if let error = checkResponseForError(request: request, response: response, data: data, error: error){ + return .failure(error) + } + + let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath) + + if let JSONObject = JSONObject, + let parsedObject = (try? Mapper(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject)){ + return .success(parsedObject) + } + + let failureReason = "ObjectMapper failed to serialize response." + let error = newError(.dataSerializationFailed, failureReason: failureReason) + return .failure(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 + } + + /* do { + return try decoder.decode(T.self, from: data) + } catch { + throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error)) + } */ + + let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath) + + if let JSONObject = JSONObject, + let parsedObject = (try? Mapper(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject)){ + return parsedObject + } else { + let failureReason = "ObjectMapper failed to serialize response." + throw AFError.responseSerializationFailed(reason: .decodingFailed(error: newError(.dataSerializationFailed, failureReason: failureReason))) + } + + } +} + +public final class ImmutableMappableArrayResponseSerializer: ResponseSerializer { + /// The `JSONDecoder` instance used to decode responses. + public let decoder: DataDecoder + /// HTTP response codes for which empty responses are allowed. + public let emptyResponseCodes: Set + /// HTTP request methods for which empty responses are allowed. + public let emptyRequestMethods: Set + + public let keyPath: String? + public let context: MapContext? + /// Creates an instance using the values provided. + /// + /// - 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(_ keyPath: String?, context: MapContext? = nil, decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = ImmutableMappableArrayResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = ImmutableMappableArrayResponseSerializer.defaultEmptyRequestMethods) { + self.decoder = decoder + self.emptyResponseCodes = emptyResponseCodes + self.emptyRequestMethods = emptyRequestMethods + + self.keyPath = keyPath + self.context = context + } + + public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> [T] { + guard error == nil else { throw error! } + + /* return DataResponseSerializer { request, response, data, error in + if let error = checkResponseForError(request: request, response: response, data: data, error: error){ + return .failure(error) + } + + let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath) + + if let JSONObject = JSONObject, + let parsedObject = (try? Mapper(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject)){ + return .success(parsedObject) + } + + let failureReason = "ObjectMapper failed to serialize response." + let error = newError(.dataSerializationFailed, failureReason: failureReason) + return .failure(error) + } */ + + + guard let data = data, !data.isEmpty else { + guard emptyResponseAllowed(forRequest: request, response: response) else { + throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) + } + + // TODO - Response JSON Decodable Array + guard let emptyValue = Empty.value as? [T] else { + throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)")) + } + + return emptyValue + } + + /* do { + return try decoder.decode(T.self, from: data) + } catch { + throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error)) + } */ + + + if let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath){ + + if let parsedObject = try? Mapper(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))) + + + } +} + +public final class MappableArrayResponseSerializer: ResponseSerializer { + /// The `JSONDecoder` instance used to decode responses. + public let decoder: DataDecoder + /// HTTP response codes for which empty responses are allowed. + public let emptyResponseCodes: Set + /// HTTP request methods for which empty responses are allowed. + public let emptyRequestMethods: Set + + public let keyPath: String? + public let context: MapContext? + /// Creates an instance using the values provided. + /// + /// - 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(_ keyPath: String?, context: MapContext? = nil, decoder: DataDecoder = JSONDecoder(), + emptyResponseCodes: Set = MappableArrayResponseSerializer.defaultEmptyResponseCodes, + emptyRequestMethods: Set = MappableArrayResponseSerializer.defaultEmptyRequestMethods) { + self.decoder = decoder + self.emptyResponseCodes = emptyResponseCodes + self.emptyRequestMethods = emptyRequestMethods + + self.keyPath = keyPath + self.context = context + } + + public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> [T] { + guard error == nil else { throw error! } + + /* return DataResponseSerializer { request, response, data, error in + if let error = checkResponseForError(request: request, response: response, data: data, error: error){ + return .failure(error) + } + + let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath) + + if let JSONObject = JSONObject, + let parsedObject = (try? Mapper(context: context, shouldIncludeNilValues: false).map(JSONObject: JSONObject)){ + return .success(parsedObject) + } + + let failureReason = "ObjectMapper failed to serialize response." + let error = newError(.dataSerializationFailed, failureReason: failureReason) + return .failure(error) + } */ + + + guard let data = data, !data.isEmpty else { + guard emptyResponseAllowed(forRequest: request, response: response) else { + throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) + } + + // TODO - Response JSON Decodable Array + guard let emptyValue = Empty.value as? [T] else { + throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)")) + } + + return emptyValue + } + + /* do { + return try decoder.decode(T.self, from: data) + } catch { + throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error)) + } */ + + let JSONObject = processResponse(request: request, response: response, data: data, keyPath: keyPath) + + if let parsedObject = Mapper(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))) + + } +} + + + +/// Utility function for extracting JSON from response +internal func processResponse(request: URLRequest?, response: HTTPURLResponse?, data: Data?, keyPath: String?) -> Any? { + + // ResponseSerializer( + let jsonResponseSerializer = JSONResponseSerializer(options: .allowFragments) + //let jsonResponseSerializer = DataRequest.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 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 +} + +enum ErrorCode: Int { + case noData = 1 + case dataSerializationFailed = 2 +}