diff --git a/Sources/URLSessionDecodable/DataDecoder.swift b/Sources/URLSessionDecodable/DataDecoder.swift index 0dcf371..7bebb4d 100644 --- a/Sources/URLSessionDecodable/DataDecoder.swift +++ b/Sources/URLSessionDecodable/DataDecoder.swift @@ -2,13 +2,25 @@ import Foundation -public protocol DataDecoder { +public protocol AnyTypeDecoder { /// Decodes an instance of the indicated type. func decode(_ type: T.Type, from data: Data) throws -> T where T: Decodable } +// For legacy reasons +public typealias DataDecoder = AnyTypeDecoder + +public protocol ConcreteTypeDecoder { + + associatedtype DecodedType + + /// Decodes an instance of the indicated type. + func decode(_ data: Data) throws -> DecodedType + +} + // MARK: - -extension JSONDecoder: DataDecoder {} +extension JSONDecoder: AnyTypeDecoder {} diff --git a/Sources/URLSessionDecodable/URLSessionDecodable.swift b/Sources/URLSessionDecodable/URLSessionDecodable.swift index 01028f5..dfacf11 100644 --- a/Sources/URLSessionDecodable/URLSessionDecodable.swift +++ b/Sources/URLSessionDecodable/URLSessionDecodable.swift @@ -13,19 +13,58 @@ public enum HTTPMethod: String { } extension URLSession { + //swiftlint:disable function_parameter_count + /// Creates a task that retrieves the contents of the specified URL, decodes the response, /// then calls a handler upon completion. /// - /// The response data will be decoded to `T` type when `statusCode` is in range `200..<300` - /// or `E` for other status codes. + /// The response data will be decoded to `T` type when `statusCode` is in range `200..<300`. + public func decodable( + with url: URL, + method: HTTPMethod, + parameters: ParametersEncoding?, + headers: HTTPHeaders?, + decoder: D, + completionHandler: @escaping (Result) -> Void + ) -> URLSessionDataTask where D.DecodedType == T { + return createTask( + with: url, + method: method, + parameters: parameters, + headers: headers, + completionHandler: completionHandler + ) { data -> T in + try decoder.decode(data) + } + } + public func decodable( with url: URL, method: HTTPMethod, parameters: ParametersEncoding?, headers: HTTPHeaders?, - decoder: DataDecoder, + decoder: AnyTypeDecoder, completionHandler: @escaping (Result) -> Void + ) -> URLSessionDataTask { + return createTask( + with: url, + method: method, + parameters: parameters, + headers: headers, + completionHandler: completionHandler + ) { data -> T in + try decoder.decode(T.self, from: data) + } + } + + private func createTask( + with url: URL, + method: HTTPMethod, + parameters: ParametersEncoding?, + headers: HTTPHeaders?, + completionHandler: @escaping (Result) -> Void, + decoding: @escaping (Data) throws -> T ) -> URLSessionDataTask { let request = self.request(with: url, method: method, parameters: parameters, headers: headers) let task = dataTask(with: request) { data, response, error in @@ -42,18 +81,19 @@ extension URLSession { completionHandler(.failure(URLSessionDecodableError.nonHTTPResponse(response))) return } - - completionHandler(Self.handle(response: httpResponse, data: data, decoder: decoder, url: url)) + let result = Self.handle(response: httpResponse, data: data, url: url, decoding: decoding) + completionHandler(result) } return task } + //swiftlint:enable function_parameter_count - private static func handle( + private static func handle( response: HTTPURLResponse, data: Data, - decoder: DataDecoder, - url: URL + url: URL, + decoding: (Data) throws -> T ) -> Result { guard 200..<300 ~= response.statusCode else { let serverResponse = URLSessionDecodableError.ServerResponse(statusCode: response.statusCode, @@ -63,7 +103,7 @@ extension URLSession { } do { - return try .success(decoder.decode(T.self, from: data)) + return try .success(decoding(data)) } catch { if #available(iOS 10.0, *) { os_log("%@", "Error while decoding \(String(describing: type(of: T.self))) \(error)")