diff --git a/Example/Tests/ResponderTests.swift b/Example/Tests/ResponderTests.swift index 3609941..b31d0a3 100644 --- a/Example/Tests/ResponderTests.swift +++ b/Example/Tests/ResponderTests.swift @@ -29,7 +29,7 @@ class ResponderTests: XCTestCase { let response = HTTPURLResponse(url: URL(string: "https://lithobyte.co/api/v2/apps")!, statusCode: 200, httpVersion: nil, headerFields: nil) let data = Data() - let completion = responderToCompletion(responder: responder) + let completion: (Data?, URLResponse?, Error?) -> Void = responderToCompletion(responder: responder) completion(nil, nil, error) XCTAssert(wasErrorCalled) @@ -58,7 +58,7 @@ class ResponderTests: XCTestCase { let response = HTTPURLResponse(url: URL(string: "https://lithobyte.co/api/v2/apps")!, statusCode: 200, httpVersion: nil, headerFields: nil) let data = Data() - let completion = responderToCompletion(responder: responder) + let completion: (Data?, URLResponse?, Error?) -> Void = responderToCompletion(responder: responder) completion(data, nil, error) XCTAssert(wasErrorCalled) @@ -88,7 +88,7 @@ class ResponderTests: XCTestCase { let response = URLResponse()//HTTPURLResponse(url: URL(string: "https://lithobyte.co/api/v2/apps")!, statusCode: 200, httpVersion: nil, headerFields: nil) let data = Data() - let completion = responderToCompletion(responder: responder) + let completion: (Data?, URLResponse?, Error?) -> Void = responderToCompletion(responder: responder) completion(nil, response, nil) XCTAssert(!wasErrorCalled) @@ -118,7 +118,7 @@ class ResponderTests: XCTestCase { let response = HTTPURLResponse(url: URL(string: "https://lithobyte.co/api/v2/apps")!, statusCode: 200, httpVersion: nil, headerFields: nil) let data = Data() - let completion = responderToCompletion(responder: responder) + let completion: (Data?, URLResponse?, Error?) -> Void = responderToCompletion(responder: responder) completion(nil, response, nil) XCTAssert(!wasErrorCalled) @@ -148,7 +148,7 @@ class ResponderTests: XCTestCase { let response = HTTPURLResponse(url: URL(string: "https://lithobyte.co/api/v2/apps")!, statusCode: 500, httpVersion: nil, headerFields: nil) let data = Data() - let completion = responderToCompletion(responder: responder) + let completion: (Data?, URLResponse?, Error?) -> Void = responderToCompletion(responder: responder) completion(nil, response, nil) XCTAssert(!wasErrorCalled) @@ -178,7 +178,7 @@ class ResponderTests: XCTestCase { let response = HTTPURLResponse(url: URL(string: "https://lithobyte.co/api/v2/apps")!, statusCode: 200, httpVersion: nil, headerFields: nil) let data = Data() - let completion = responderToCompletion(responder: responder) + let completion: (Data?, URLResponse?, Error?) -> Void = responderToCompletion(responder: responder) completion(data, response, nil) XCTAssert(!wasErrorCalled) diff --git a/Sources/funnet/Combine/CombineNetCall.swift b/Sources/funnet/Combine/CombineNetCall.swift index 15f5024..067afb6 100644 --- a/Sources/funnet/Combine/CombineNetCall.swift +++ b/Sources/funnet/Combine/CombineNetCall.swift @@ -38,18 +38,22 @@ open class CombineNetCall: NetworkCall, Fireable { @available(iOS 13.0, *) public class CombineNetworkResponder: NetworkResponderProtocol { @Published public var dataTask: URLSessionDataTask? + @Published public var downloadTask: URLSessionDownloadTask? @Published public var response: URLResponse? @Published public var httpResponse: HTTPURLResponse? @Published public var data: Data? + @Published public var url: URL? @Published public var error: NSError? @Published public var serverError: NSError? @Published public var errorResponse: URLResponse? @Published public var errorData: Data? public lazy var taskHandler: (URLSessionDataTask?) -> Void = { [weak self] in self?.dataTask = $0 } + public lazy var downloadTaskHandler: (URLSessionDownloadTask?) -> Void = { [weak self] in self?.downloadTask = $0 } public lazy var responseHandler: (URLResponse?) -> Void = { [weak self] in self?.response = $0 } public lazy var httpResponseHandler: (HTTPURLResponse) -> Void = { [weak self] in self?.httpResponse = $0 } public lazy var dataHandler: (Data?) -> Void = { [weak self] in self?.data = $0 } + public lazy var urlHandler: (URL?) -> Void = { [weak self] in self?.url = $0 } public lazy var errorHandler: (NSError) -> Void = { [weak self] in self?.error = $0 } public lazy var serverErrorHandler: (NSError) -> Void = { [weak self] in self?.serverError = $0 } public lazy var errorDataHandler: (Data?) -> Void = { [weak self] in self?.errorData = $0 } diff --git a/Sources/funnet/Core/Endpoint.swift b/Sources/funnet/Core/Endpoint.swift index fffd25b..b04b30d 100644 --- a/Sources/funnet/Core/Endpoint.swift +++ b/Sources/funnet/Core/Endpoint.swift @@ -15,6 +15,7 @@ public protocol EndpointProtocol { var timeout: TimeInterval { get set } var postData: Data? { get set } var dataStream: InputStream? { get set } + var isDownload: Bool { get set } } public struct Endpoint: EndpointProtocol { @@ -25,6 +26,7 @@ public struct Endpoint: EndpointProtocol { public var timeout: TimeInterval = 60 public var postData: Data? public var dataStream: InputStream? + public var isDownload: Bool = false public init() {} } @@ -55,6 +57,14 @@ public func dataSetter(from model: M) -> (inout T) -> Void where M: Encoda } } +public func setDownload(_ endpoint: inout T) where T: EndpointProtocol { + endpoint.isDownload = true +} + +public func setData(_ endpoint: inout T) where T: EndpointProtocol { + endpoint.isDownload = false +} + public func addJsonHeaders(_ endpoint: inout T) where T: EndpointProtocol { endpoint.addHeaders(headers: ["Content-Type": "application/json", "Accept": "application/json"]) } diff --git a/Sources/funnet/Core/NetworkCall.swift b/Sources/funnet/Core/NetworkCall.swift index d5c55e8..3ca179a 100644 --- a/Sources/funnet/Core/NetworkCall.swift +++ b/Sources/funnet/Core/NetworkCall.swift @@ -52,16 +52,23 @@ public func fire(_ call: T) where T: NetworkCall { public func fire(_ call: T, with responder: NetworkResponderProtocol?) where T: NetworkCall { let request = generateRequest(from: call.configuration, endpoint: call.endpoint) - let dataTask: URLSessionDataTask? + let dataTask: URLSessionTask? if let responder = responder { - dataTask = generateDataTask(sessionConfiguration: call.configuration.urlConfiguration, - request: request, - responder: responder) + dataTask = call.endpoint.isDownload ? + generateDownloadTask(sessionConfiguration: call.configuration.urlConfiguration, + request: request, + responder: responder) : + generateDataTask(sessionConfiguration: call.configuration.urlConfiguration, + request: request, + responder: responder) } else { - dataTask = generateDataTask(sessionConfiguration: call.configuration.urlConfiguration, + dataTask = call.endpoint.isDownload ? + generateDownloadTask(sessionConfiguration: call.configuration.urlConfiguration, request: request) : + generateDataTask(sessionConfiguration: call.configuration.urlConfiguration, request: request) } dataTask?.resume() - call.responder?.taskHandler(dataTask) + call.responder?.taskHandler(dataTask as? URLSessionDataTask) + call.responder?.downloadTaskHandler(dataTask as? URLSessionDownloadTask) } diff --git a/Sources/funnet/Core/NetworkResponder.swift b/Sources/funnet/Core/NetworkResponder.swift index 48deb80..fa474a1 100644 --- a/Sources/funnet/Core/NetworkResponder.swift +++ b/Sources/funnet/Core/NetworkResponder.swift @@ -9,9 +9,11 @@ import Foundation public protocol NetworkResponderProtocol { var taskHandler: (URLSessionDataTask?) -> Void { get set } + var downloadTaskHandler: (URLSessionDownloadTask?) -> Void { get set } var responseHandler: (URLResponse?) -> Void { get set } var httpResponseHandler: (HTTPURLResponse) -> Void { get set } var dataHandler: (Data?) -> Void { get set } + var urlHandler: (URL?) -> Void { get set } var errorHandler: (NSError) -> Void { get set } var serverErrorHandler: (NSError) -> Void { get set } var errorDataHandler: (Data?) -> Void { get set } @@ -19,9 +21,11 @@ public protocol NetworkResponderProtocol { public struct NetworkResponder: NetworkResponderProtocol { public var taskHandler: (URLSessionDataTask?) -> Void = { _ in } + public var downloadTaskHandler: (URLSessionDownloadTask?) -> Void = { _ in } public var responseHandler: (URLResponse?) -> Void = { _ in } public var httpResponseHandler: (HTTPURLResponse) -> Void = { _ in } public var dataHandler: (Data?) -> Void = { _ in } + public var urlHandler: (URL?) -> Void = { _ in } public var errorHandler: (NSError) -> Void = { _ in } public var serverErrorHandler: (NSError) -> Void = { _ in } public var errorDataHandler: (Data?) -> Void = { _ in } diff --git a/Sources/funnet/Core/Requests.swift b/Sources/funnet/Core/Requests.swift index 7b53128..2c023d7 100644 --- a/Sources/funnet/Core/Requests.swift +++ b/Sources/funnet/Core/Requests.swift @@ -25,6 +25,12 @@ public func generateRequest(from configuration: ServerConfigurationProtocol, end return mutableRequest! as URLRequest } +public func generateDownloadTask(sessionConfiguration: URLSessionConfiguration, + request: URLRequest, + responder: NetworkResponderProtocol) -> URLSessionDownloadTask? { + return generateDownloadTask(sessionConfiguration: sessionConfiguration, request: request, responderToCompletion(responder: responder)) +} + public func generateDataTask(sessionConfiguration: URLSessionConfiguration, request: URLRequest, responder: NetworkResponderProtocol) -> URLSessionDataTask? { @@ -33,6 +39,13 @@ public func generateDataTask(sessionConfiguration: URLSessionConfiguration, responderToCompletion(responder: responder)) } +public func generateDownloadTask(sessionConfiguration: URLSessionConfiguration, + request: URLRequest, + _ completion: @escaping (URL?, URLResponse?, Error?) -> Void = { _, _, _ in }) -> URLSessionDownloadTask? { + let session = URLSession(configuration: sessionConfiguration, delegate: nil, delegateQueue: OperationQueue.main) + return generateDownloadTask(session, request, completion) +} + public func generateDataTask(sessionConfiguration: URLSessionConfiguration, request: URLRequest, _ completion: @escaping (Data?, URLResponse?, Error?) -> Void = { _, _, _ in }) @@ -44,6 +57,10 @@ public func generateDataTask(sessionConfiguration: URLSessionConfiguration, } } +public func generateDownloadTask(_ session: URLSession, _ request: URLRequest, _ completion: @escaping (URL?, URLResponse?, Error?) -> Void) -> URLSessionDownloadTask? { + return session.downloadTask(with: request, completionHandler: completion) +} + public func generateDataTask(_ session: URLSession, _ request: URLRequest, _ completion: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask? { return session.dataTask(with: request, completionHandler: completion) } @@ -57,6 +74,10 @@ public func responderToCompletion(responder: NetworkResponderProtocol) -> (Data? errorDataHandler: responder.errorDataHandler) } +public func responderToCompletion(responder: NetworkResponderProtocol) -> (URL?, URLResponse?, Error?) -> Void { + return handlersToCompletion(responseHandler: responder.responseHandler, httpResponseHandler: responder.httpResponseHandler, urlHandler: responder.urlHandler, errorHandler: responder.errorHandler, serverErrorHandler: responder.serverErrorHandler) +} + public func responderToTaskPublisherReceiver(responder: NetworkResponderProtocol) -> (Data?, URLResponse?) -> Void { return handlersToTaskPublisherBlock(responseHandler: responder.responseHandler, httpResponseHandler: responder.httpResponseHandler, @@ -95,6 +116,26 @@ public func handlersToCompletion(responseHandler: @escaping (URLResponse?) -> Vo } } +public func handlersToCompletion(responseHandler: @escaping (URLResponse?) -> Void = { _ in }, + httpResponseHandler: @escaping (HTTPURLResponse) -> Void = { _ in }, + urlHandler: @escaping (URL?) -> Void = { _ in }, + errorHandler: @escaping (NSError) -> Void = { _ in }, + serverErrorHandler: @escaping (NSError) -> Void = { _ in }) -> (URL?, URLResponse?, Error?) -> Void { + return { (url, response, error) in + if let e = error as NSError? { + errorHandler(e) + } else if let httpResponse = response as? HTTPURLResponse { + httpResponseHandler(httpResponse) + if httpResponse.statusCode > 299 { + let info: [String: Any] = ["url" : httpResponse.url?.absoluteString as Any] + serverErrorHandler(NSError(domain: "Server", code: httpResponse.statusCode, userInfo: info)) + } else { + urlHandler(url) + } + } + } +} + public func handlersToTaskPublisherBlock(responseHandler: @escaping (URLResponse?) -> Void = { _ in }, httpResponseHandler: @escaping (HTTPURLResponse) -> Void = { _ in }, dataHandler: @escaping (Data?) -> Void = { _ in }, diff --git a/Sources/funnet/ReactiveSwift/ReactiveNetCall.swift b/Sources/funnet/ReactiveSwift/ReactiveNetCall.swift index cb98680..e925dc8 100644 --- a/Sources/funnet/ReactiveSwift/ReactiveNetCall.swift +++ b/Sources/funnet/ReactiveSwift/ReactiveNetCall.swift @@ -34,35 +34,44 @@ open class ReactiveNetCall: NetworkCall, Fireable { public class ReactiveNetworkResponder: NetworkResponderProtocol { public let dataTaskSignal: Signal + public let downloadTaskSignal: Signal public let responseSignal: Signal public let httpResponseSignal: Signal public let dataSignal: Signal + public let urlSignal: Signal public let errorSignal: Signal public let serverErrorSignal: Signal public let errorDataSignal: Signal let dataTaskProperty = MutableProperty(nil) + let downloadTaskProperty = MutableProperty(nil) let responseProperty = MutableProperty(nil) let httpResponseProperty = MutableProperty(nil) let dataProperty = MutableProperty(nil) + let urlProperty = MutableProperty(nil) let errorProperty = MutableProperty(nil) let serverErrorProperty = MutableProperty(nil) let errorResponseProperty = MutableProperty(nil) let errorDataProperty = MutableProperty(nil) public lazy var taskHandler: (URLSessionDataTask?) -> Void = { [weak self] in self?.dataTaskProperty.value = $0 } + public lazy var downloadTaskHandler: (URLSessionDownloadTask?) -> Void = { [weak self] in + self?.downloadTaskProperty.value = $0 } public lazy var responseHandler: (URLResponse?) -> Void = { [weak self] in self?.responseProperty.value = $0 } public lazy var httpResponseHandler: (HTTPURLResponse) -> Void = { [weak self] in self?.httpResponseProperty.value = $0 } public lazy var dataHandler: (Data?) -> Void = { [weak self] in self?.dataProperty.value = $0 } + public lazy var urlHandler: (URL?) -> Void = { [weak self] in self?.urlProperty.value = $0 } public lazy var errorHandler: (NSError) -> Void = { [weak self] in self?.errorProperty.value = $0 } public lazy var serverErrorHandler: (NSError) -> Void = { [weak self] in self?.serverErrorProperty.value = $0 } public lazy var errorDataHandler: (Data?) -> Void = { [weak self] in self?.errorDataProperty.value = $0 } public init() { dataTaskSignal = dataTaskProperty.signal.skipNil() + downloadTaskSignal = downloadTaskProperty.signal.skipNil() responseSignal = responseProperty.signal.skipNil() httpResponseSignal = httpResponseProperty.signal.skipNil() dataSignal = dataProperty.signal + urlSignal = urlProperty.signal errorSignal = errorProperty.signal.skipNil() serverErrorSignal = serverErrorProperty.signal.skipNil() errorDataSignal = errorDataProperty.signal.skipNil()