// // NetworkTask.swift // Cyberlock // // Created by Jura on 8/21/19. // Copyright © 2019 Omicronmedia. All rights reserved. // import Foundation public typealias CancellationToken = String internal typealias NetworkingTaskCompletionClosure = (_ token: CancellationToken, _ result: Result) -> Void public protocol Cancellable { var token: CancellationToken { get } func cancel() } final class NetworkTask: Cancellable { private let request: URLRequest private let completion: NetworkingTaskCompletionClosure private var task: URLSessionDataTask? init(request: URLRequest, token: String? = nil, completion: @escaping NetworkingTaskCompletionClosure) { self.request = request self.completion = completion self.token = token ?? UUID().uuidString } func start(with behaviour: StubBehavior, sampleResponse: Data?) { switch behaviour { case .never: self.performRequest() case .immediate: self.perform(with: sampleResponse, after: 0) case let .delayed(seconds: seconds): self.perform(with: sampleResponse, after: seconds) } } // MARK: - Cancellable let token: CancellationToken func cancel() { self.task?.cancel() self.task = nil } // MARK: - // MARK: - Private private func performRequest() { let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 1 config.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData config.urlCache = nil let session = URLSession(configuration: config) let task = session.dataTask(with: self.request) { [weak self] (data, response, error) in guard let self = self else { return } guard let task = self.task else { self.completion(self.token, Result.failure(NetworkRouterError.cancelled)) return } task.cancel() self.task = nil let result = NetworkTask.convertResponseToResult(response as? HTTPURLResponse, data: data, error: error, request: self.request) self.completion(self.token, result) } self.task = task task.resume() } private func perform(with sampleData: Data?, after seconds: TimeInterval) { let completionHandler = { [weak self] in guard let self = self else { return } let result = NetworkTask.convertResponseToResult(nil, data: sampleData, error: nil, request: self.request) self.completion(self.token, result) } guard seconds > 0 else { completionHandler() return } DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + seconds, execute: completionHandler) } private static func convertResponseToResult(_ response: HTTPURLResponse?, data: Data?, error: Error?, request: URLRequest) -> Result { let taskResponse = Response(statusCode: response?.statusCode ?? 200, data: data ?? Data(), response: response, request: request) return Result.success(taskResponse) /* switch (data, error) { case (.some, .none): return self.errorFromServerResponse(tossResponse) >>- { self.fail(with: $0, response: tossResponse) } ?? .success(tossResponse) case let (_, error?): guard (error as NSError).domain != NSURLErrorDomain else { return self.fail(with: error, response: tossResponse) } guard let errorFromResponse = self.errorFromServerResponse(tossResponse) else { let generalError = NSError.tossSpecificError(with: (error as NSError).code, response: tossResponse, localizedDescription: (error as NSError).localizedDescription) return self.fail(with: generalError, response: tossResponse) } return self.fail(with: errorFromResponse, response: tossResponse) default: let error = NSError.tossSpecificError(with: .unknown, response: tossResponse) return self.fail(with: error, response: tossResponse) } */ } }