diff --git a/.travis.yml b/.travis.yml index ae21af5..1d3b8ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode9 +osx_image: xcode10 env: global: diff --git a/AlamofireObjectMapper.podspec b/AlamofireObjectMapper.podspec index bdc78be..276b1a2 100644 --- a/AlamofireObjectMapper.podspec +++ b/AlamofireObjectMapper.podspec @@ -8,8 +8,8 @@ Pod::Spec.new do |s| s.author = { "Tristan Himmelman" => "tristanhimmelman@gmail.com" } s.source = { :git => 'https://github.com/tristanhimmelman/AlamofireObjectMapper.git', :tag => s.version.to_s } - s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.ios.deployment_target = '10.0' + s.osx.deployment_target = '10.0' s.watchos.deployment_target = '2.0' s.tvos.deployment_target = '9.0' diff --git a/AlamofireObjectMapper.xcodeproj/project.pbxproj b/AlamofireObjectMapper.xcodeproj/project.pbxproj index 1d1b23d..6b26ebf 100644 --- a/AlamofireObjectMapper.xcodeproj/project.pbxproj +++ b/AlamofireObjectMapper.xcodeproj/project.pbxproj @@ -323,7 +323,7 @@ E89A901B1D6F5F520023C364 /* ObjectMapper.framework */, E89A901D1D6F5F520023C364 /* ObjectMapper.framework */, E89A901F1D6F5F520023C364 /* ObjectMapper-iOSTests.xctest */, - E89A90211D6F5F520023C364 /* ObjectMapper-MacTests.xctest */, + E89A90211D6F5F520023C364 /* ObjectMapper-macOSTests.xctest */, E89A90231D6F5F520023C364 /* ObjectMapper-tvOSTests.xctest */, 6A20983C219CDEA5009AB4DF /* libObjectMapper.a */, ); @@ -654,10 +654,10 @@ remoteRef = E89A901E1D6F5F520023C364 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - E89A90211D6F5F520023C364 /* ObjectMapper-MacTests.xctest */ = { + E89A90211D6F5F520023C364 /* ObjectMapper-macOSTests.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; - path = "ObjectMapper-MacTests.xctest"; + path = "ObjectMapper-macOSTests.xctest"; remoteRef = E89A90201D6F5F520023C364 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -835,7 +835,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -889,7 +889,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_VERSION = 4.2; @@ -914,7 +914,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AlamofireObjectMapper/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.tristanhimmelman.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = AlamofireObjectMapper; @@ -938,7 +938,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AlamofireObjectMapper/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.tristanhimmelman.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = AlamofireObjectMapper; diff --git a/AlamofireObjectMapper.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/AlamofireObjectMapper.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/AlamofireObjectMapper.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/AlamofireObjectMapper/AlamofireObjectMapper.swift b/AlamofireObjectMapper/AlamofireObjectMapper.swift index 658f606..bc78327 100644 --- a/AlamofireObjectMapper/AlamofireObjectMapper.swift +++ b/AlamofireObjectMapper/AlamofireObjectMapper.swift @@ -32,6 +32,38 @@ 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? { @@ -50,13 +82,40 @@ extension DataRequest { /// BaseMappable Object Serializer public static func ObjectMapperSerializer(_ keyPath: String?, mapToObject object: T? = nil, context: MapContext? = nil) -> MappableResponseSerializer { - return MappableResponseSerializer(keyPath, mapToObject: object, context: context) + 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(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))) + + }) } /// ImmutableMappable Array Serializer - public static func ObjectMapperImmutableSerializer(_ keyPath: String?, context: MapContext? = nil) -> ImmutableMappableResponseSerializer { + public static func ObjectMapperImmutableSerializer(_ keyPath: String?, context: MapContext? = nil) -> MappableResponseSerializer { - return ImmutableMappableResponseSerializer(keyPath, context: context) + 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(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))) + } + }) } /** @@ -82,12 +141,38 @@ extension DataRequest { /// BaseMappable Array Serializer public static func ObjectMapperArraySerializer(_ keyPath: String?, context: MapContext? = nil) -> MappableArrayResponseSerializer { - return MappableArrayResponseSerializer(keyPath, context: context) + + + 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(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(_ keyPath: String?, context: MapContext? = nil) -> ImmutableMappableArrayResponseSerializer { - return ImmutableMappableArrayResponseSerializer(keyPath, context: context) + public static func ObjectMapperImmutableArraySerializer(_ keyPath: String?, context: MapContext? = nil) -> MappableArrayResponseSerializer { + 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(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))) + }) } /** @@ -121,7 +206,7 @@ extension DataRequest { public final class MappableResponseSerializer: ResponseSerializer { /// The `JSONDecoder` instance used to decode responses. - public let decoder: DataDecoder + public let decoder: DataDecoder = JSONDecoder() /// HTTP response codes for which empty responses are allowed. public let emptyResponseCodes: Set /// HTTP request methods for which empty responses are allowed. @@ -130,81 +215,30 @@ public final class MappableResponseSerializer: ResponseSerializ 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: - /// - decoder: The `JSONDecoder`. Defaults to a `JSONDecoder()`. + /// - 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]`. - public init(_ keyPath: String?, mapToObject object: T? = nil, context: MapContext? = nil, decoder: DataDecoder = JSONDecoder(), + /// - serializeCallback: + public init(_ keyPath: String?, mapToObject object: T? = nil, context: MapContext? = nil, 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! } - - 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))) - - - } -} + emptyRequestMethods: Set = MappableResponseSerializer.defaultEmptyRequestMethods, serializeCallback: @escaping (URLRequest?,HTTPURLResponse?, Data?,Error?) throws -> T) { -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 + self.object = object + self.serializeCallback = serializeCallback } public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> T { @@ -221,81 +255,13 @@ public final class ImmutableMappableResponseSerializer: Re return emptyValue } - - 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! } - - guard let data = data, !data.isEmpty else { - guard emptyResponseAllowed(forRequest: request, response: response) else { - throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) - } - - // TODO / FIX - Empty Response JSON Decodable Array Fix - "Cast from empty always fails..." - guard let emptyValue = Empty.value as? [T] else { - throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)")) - } - - return emptyValue - } - - 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))) - - + return try self.serializeCallback(request, response, data, error) } } public final class MappableArrayResponseSerializer: ResponseSerializer { /// The `JSONDecoder` instance used to decode responses. - public let decoder: DataDecoder + public let decoder: DataDecoder = JSONDecoder() /// HTTP response codes for which empty responses are allowed. public let emptyResponseCodes: Set /// HTTP request methods for which empty responses are allowed. @@ -303,22 +269,26 @@ public final class MappableArrayResponseSerializer: ResponseSer 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: - /// - decoder: The `JSONDecoder`. Defaults to a `JSONDecoder()`. + /// - 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]`. - public init(_ keyPath: String?, context: MapContext? = nil, decoder: DataDecoder = JSONDecoder(), + /// - serializeCallback: + public init(_ keyPath: String?, context: MapContext? = nil, serializeCallback: @escaping (URLRequest?,HTTPURLResponse?, Data?,Error?) throws -> [T], emptyResponseCodes: Set = MappableArrayResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = MappableArrayResponseSerializer.defaultEmptyRequestMethods) { - self.decoder = decoder 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] { @@ -336,48 +306,6 @@ public final class MappableArrayResponseSerializer: ResponseSer return emptyValue } - - 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? { - - 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 try self.serializeCallback(request, response, data, error) } - - 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 } diff --git a/Cartfile b/Cartfile index 662080e..0e23659 100644 --- a/Cartfile +++ b/Cartfile @@ -1,2 +1,2 @@ -github "Alamofire/Alamofire" ~> 4.7 +github "Alamofire/Alamofire" "5.0.0.beta.1" github "tristanhimmelman/ObjectMapper" ~> 3.4 diff --git a/Cartfile.resolved b/Cartfile.resolved index 1462415..1b98deb 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "Alamofire/Alamofire" "4.7.3" -github "tristanhimmelman/ObjectMapper" "3.4.1" +github "Alamofire/Alamofire" "5.0.0.beta.1" +github "tristanhimmelman/ObjectMapper" "3.4.2" diff --git a/Carthage/Checkouts/Alamofire b/Carthage/Checkouts/Alamofire index 61a780f..3e488dc 160000 --- a/Carthage/Checkouts/Alamofire +++ b/Carthage/Checkouts/Alamofire @@ -1 +1 @@ -Subproject commit 61a780f3b58ee0d2bb76fdb7b89dbc9751d521d7 +Subproject commit 3e488dcd5caf8d76f3da79c7b6d7f4d784ca3296 diff --git a/Carthage/Checkouts/ObjectMapper b/Carthage/Checkouts/ObjectMapper index feb763a..83848fe 160000 --- a/Carthage/Checkouts/ObjectMapper +++ b/Carthage/Checkouts/ObjectMapper @@ -1 +1 @@ -Subproject commit feb763a93755b5b576e09a9db2cb8d380fd65ba2 +Subproject commit 83848fe8baa6fab47c7c325514956d4c9b2b88e6