From d61d3325e3f06024b49d7dc007cb21b4e4f67926 Mon Sep 17 00:00:00 2001 From: Raghav Ahuja Date: Mon, 30 Dec 2019 00:21:45 +0530 Subject: [PATCH] Updated With Publisher Kit Support --- .../contents.xcworkspacedata | 2 +- Package.resolved | 16 ++ Package.swift | 3 +- .../Create Request/Create Request.swift | 16 +- .../Debug Loging/Debug Logging.swift | 42 ---- .../NetworkKit/Debug Loging/NKLogger.swift | 226 ++++++++++++++++++ .../NetworkKit/Errors/Business Error.swift | 29 --- Sources/NetworkKit/Errors/HTTP Error.swift | 135 ----------- .../NSError.swift} | 33 ++- .../Extensions/Dictionary+Extension.swift | 48 ---- .../Extensions/JSONDecoder+Extension.swift | 14 -- .../Extensions/Optional+Extension.swift | 23 -- ...ay+Extension.swift => Set+Extension.swift} | 6 +- .../NetworkKit/Extensions/URL+Extension.swift | 19 -- .../Extensions/URLRequest+Extension.swift | 27 --- .../NKAnyCancellable/NKAnyCancellable.swift | 33 --- Sources/NetworkKit/Kit/NKQueue/NKQueue.swift | 51 ---- .../NetworkKit/Kit/NKResult/NKResult.swift | 25 -- Sources/NetworkKit/Kit/Network Task.swift | 110 --------- .../Kit/Publishers/Catch/Catch.swift | 45 ---- .../Kit/Publishers/Catch/Try Catch.swift | 45 ---- .../Publishers/Compact Map/Compact Map.swift | 64 ----- .../Compact Map/Try Compact Map.swift | 71 ------ .../Kit/Publishers/Debounce/Debounce.swift | 44 ---- .../Kit/Publishers/Decode/Decode.swift | 76 ------ .../Kit/Publishers/Map/Error/Map Error.swift | 60 ----- .../Map/Keypath/Map Keypath 2.swift | 67 ------ .../Publishers/Map/Keypath/Map Keypath.swift | 56 ----- .../NetworkKit/Kit/Publishers/Map/Map.swift | 60 ----- .../Kit/Publishers/Map/Try Map.swift | 67 ------ .../Kit/Publishers/NKPublishers.swift | 12 - .../Replace Empty/Replace Empty.swift | 44 ---- .../Replace Error/Replace Error.swift | 44 ---- .../Kit/Publishers/Validate/Validate.swift | 156 ------------ Sources/NetworkKit/Models/API Analytics.swift | 18 -- Sources/NetworkKit/Models/Error Model.swift | 29 --- ...swift => Configuration+Notification.swift} | 6 +- ...onfiguration.swift => Configuration.swift} | 72 +++--- .../ImageSession+ImageType.swift} | 0 .../ImageSession.swift} | 42 +--- .../{NKSession.swift => Session.swift} | 40 +++- .../Operations/Assign Operation.swift | 40 ---- .../Operations/Asynchronous Operation.swift | 54 ----- .../Operations/Base Block Operation.swift | 28 --- .../Operations/Catch Operation.swift | 81 ------- .../Operations/Debouce Operation.swift | 42 ---- .../Operations/Fetch Operation.swift | 59 ----- .../Operations/Replace Empty Operation.swift | 43 ---- .../Operations/Replace Error Operation.swift | 46 ---- .../Operations/TryCatch Operation.swift | 91 ------- ...egate.swift => ImageSessionDelegate.swift} | 10 +- .../NKCancellable/NKCancellable.swift | 15 -- .../Protocols/Network Decoder.swift | 16 -- .../Publisher/NKPublisher+Methods.swift | 217 ----------------- .../Publisher/NKPublisher+Queue.swift | 31 --- .../Protocols/Publisher/NKPublisher.swift | 24 -- .../Protocols/Server/Environment.swift | 2 +- .../NetworkKit/Request/Network Request.swift | 146 ----------- Sources/NetworkKit/Request/Request.swift | 82 +++++++ Tests/NetworkKitTests/NetworkKitTests.swift | 2 +- 60 files changed, 439 insertions(+), 2566 deletions(-) create mode 100644 Package.resolved delete mode 100644 Sources/NetworkKit/Debug Loging/Debug Logging.swift create mode 100644 Sources/NetworkKit/Debug Loging/NKLogger.swift delete mode 100644 Sources/NetworkKit/Errors/Business Error.swift delete mode 100644 Sources/NetworkKit/Errors/HTTP Error.swift rename Sources/NetworkKit/{Extensions/NSError+Extension.swift => Errors/NSError.swift} (65%) delete mode 100644 Sources/NetworkKit/Extensions/Dictionary+Extension.swift delete mode 100644 Sources/NetworkKit/Extensions/JSONDecoder+Extension.swift delete mode 100644 Sources/NetworkKit/Extensions/Optional+Extension.swift rename Sources/NetworkKit/Extensions/{Array+Extension.swift => Set+Extension.swift} (83%) delete mode 100644 Sources/NetworkKit/Extensions/URL+Extension.swift delete mode 100644 Sources/NetworkKit/Extensions/URLRequest+Extension.swift delete mode 100644 Sources/NetworkKit/Kit/NKAnyCancellable/NKAnyCancellable.swift delete mode 100644 Sources/NetworkKit/Kit/NKQueue/NKQueue.swift delete mode 100644 Sources/NetworkKit/Kit/NKResult/NKResult.swift delete mode 100644 Sources/NetworkKit/Kit/Network Task.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Catch/Catch.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Catch/Try Catch.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Compact Map/Compact Map.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Compact Map/Try Compact Map.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Debounce/Debounce.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Decode/Decode.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Map/Error/Map Error.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Map/Keypath/Map Keypath 2.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Map/Keypath/Map Keypath.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Map/Map.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Map/Try Map.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/NKPublishers.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Replace Empty/Replace Empty.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Replace Error/Replace Error.swift delete mode 100644 Sources/NetworkKit/Kit/Publishers/Validate/Validate.swift delete mode 100644 Sources/NetworkKit/Models/API Analytics.swift delete mode 100644 Sources/NetworkKit/Models/Error Model.swift rename Sources/NetworkKit/Networking/Configuration/{Network Configuration+Notification.swift => Configuration+Notification.swift} (82%) rename Sources/NetworkKit/Networking/Configuration/{Network Configuration.swift => Configuration.swift} (74%) rename Sources/NetworkKit/Networking/{NKImageSession/NKImageSession+ImageType.swift => ImageSession/ImageSession+ImageType.swift} (100%) rename Sources/NetworkKit/Networking/{NKImageSession/NKImageSession.swift => ImageSession/ImageSession.swift} (73%) rename Sources/NetworkKit/Networking/{NKSession.swift => Session.swift} (57%) delete mode 100644 Sources/NetworkKit/Operations/Assign Operation.swift delete mode 100644 Sources/NetworkKit/Operations/Asynchronous Operation.swift delete mode 100644 Sources/NetworkKit/Operations/Base Block Operation.swift delete mode 100644 Sources/NetworkKit/Operations/Catch Operation.swift delete mode 100644 Sources/NetworkKit/Operations/Debouce Operation.swift delete mode 100644 Sources/NetworkKit/Operations/Fetch Operation.swift delete mode 100644 Sources/NetworkKit/Operations/Replace Empty Operation.swift delete mode 100644 Sources/NetworkKit/Operations/Replace Error Operation.swift delete mode 100644 Sources/NetworkKit/Operations/TryCatch Operation.swift rename Sources/NetworkKit/Protocols/{NKImageSessionDelegate.swift => ImageSessionDelegate.swift} (91%) delete mode 100644 Sources/NetworkKit/Protocols/NKCancellable/NKCancellable.swift delete mode 100644 Sources/NetworkKit/Protocols/Network Decoder.swift delete mode 100644 Sources/NetworkKit/Protocols/Publisher/NKPublisher+Methods.swift delete mode 100644 Sources/NetworkKit/Protocols/Publisher/NKPublisher+Queue.swift delete mode 100644 Sources/NetworkKit/Protocols/Publisher/NKPublisher.swift delete mode 100644 Sources/NetworkKit/Request/Network Request.swift create mode 100644 Sources/NetworkKit/Request/Request.swift diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata index 706eede..919434a 100644 --- a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..3cadc13 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "PublisherKit", + "repositoryURL": "https://github.com/ragzy15/PublisherKit", + "state": { + "branch": null, + "revision": "ad211780f0f532a473e3d5d1cef2a2644024d7c4", + "version": "1.0.0" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift index 20f668c..68f8439 100644 --- a/Package.swift +++ b/Package.swift @@ -18,6 +18,7 @@ let package = Package( targets: ["NetworkKit"]), ], dependencies: [ + .package(url: "https://github.com/ragzy15/PublisherKit", from: .init(1, 0, 0)) // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), ], @@ -26,7 +27,7 @@ let package = Package( // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "NetworkKit", - dependencies: []), + dependencies: ["PublisherKit"]), .testTarget( name: "NetworkKitTests", dependencies: ["NetworkKit"]), diff --git a/Sources/NetworkKit/Create Request/Create Request.swift b/Sources/NetworkKit/Create Request/Create Request.swift index c0df1ff..4a9841e 100644 --- a/Sources/NetworkKit/Create Request/Create Request.swift +++ b/Sources/NetworkKit/Create Request/Create Request.swift @@ -15,7 +15,7 @@ public struct CreateRequest { public let request: URLRequest - public init?(with connection: ConnectionRepresentable, query urlQuery: URLQuery?) { + public init?(with connection: ConnectionRepresentable, query urlQuery: Set, body: Data?, headers: HTTPHeaderParameters) { var components = URLComponents() components.scheme = connection.scheme.rawValue @@ -26,15 +26,15 @@ public struct CreateRequest { components.host = (subURL.isEmpty ? subURL : subURL + ".") + connection.host.host components.path = endPoint + connection.path - var queryItems: [URLQueryItem] = [] - queryItems.addURLQuery(query: urlQuery) + var queryItems = Set() queryItems.addURLQuery(query: connection.defaultQuery) queryItems.addURLQuery(query: connection.host.defaultUrlQuery) + queryItems = queryItems.union(urlQuery) let method = connection.method if !queryItems.isEmpty { - components.queryItems = queryItems + components.queryItems = Array(queryItems) } guard let url = components.url else { @@ -47,15 +47,15 @@ public struct CreateRequest { let defaultHeaderFields = connection.host.defaultHeaders let connectionHeaderFields = connection.httpHeaders - let headerFields = defaultHeaderFields.merging(connectionHeaderFields) { (_, new) in new } + var headerFields = defaultHeaderFields.merging(connectionHeaderFields) { (_, new) in new } + headerFields.merge(headers) { (_, new) in new } if !headerFields.isEmpty { urlRequest.allHTTPHeaderFields = headerFields } + + urlRequest.httpBody = body request = urlRequest - #if DEBUG - DebugPrint.logAPIRequest(request: request, apiName: connection.name) - #endif } } diff --git a/Sources/NetworkKit/Debug Loging/Debug Logging.swift b/Sources/NetworkKit/Debug Loging/Debug Logging.swift deleted file mode 100644 index dc32772..0000000 --- a/Sources/NetworkKit/Debug Loging/Debug Logging.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// DebugLogging.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -typealias DebugPrint = PilotDebugLogs - -/// Allows Logs to be Printed in Debug Console. -public struct PilotDebugLogs { - - /// Enabled Network debug logs to be printed in debug console. - /// Default value is `true` - public static var isEnabled = true - - // MARK: - DEBUG LOGS HANDLER - - /// Writes the textual representations of the given items into the standard output. - /// - Parameter value: String to be printed on console. - /// - Parameter flag: If the value should be printed on console - static func print(_ value: String, shouldPrint flag: Bool = true) { - if DebugPrint.isEnabled, flag { - Swift.print(value) - } - } - - static func logAPIRequest(request: URLRequest, apiName: String?) { - print( - """ - ------------------------------------------------------------ - API Call Request for: - Name: \(apiName ?? "nil") - \(request.debugDescription) - - """ - ) - } -} diff --git a/Sources/NetworkKit/Debug Loging/NKLogger.swift b/Sources/NetworkKit/Debug Loging/NKLogger.swift new file mode 100644 index 0000000..9bcb0e5 --- /dev/null +++ b/Sources/NetworkKit/Debug Loging/NKLogger.swift @@ -0,0 +1,226 @@ +// +// NKLogger.swift +// NetworkKit +// +// Created by Raghav Ahuja on 18/10/19. +// Copyright © 2019 Raghav Ahuja. All rights reserved. +// + +import Foundation + +final public class NKLogger { + + /// Allows Logs to be Printed in Debug Console. + /// Default value is `true` + public var isLoggingEnabled: Bool = true + + public static let `default` = NKLogger() + + /** + Creates a `NKLogger`. + */ + public init() { } + + /** + Writes the textual representations of the given items into the standard output. + + - parameter items: Zero or more items to print.. + - parameter separator: A string to print between each item. The default is a single space (" "). + - parameter terminator: The string to print after all items have been printed. The default is a newline ("\n"). + + */ + func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { + #if DEBUG + guard NKConfiguration.allowLoggingOnAllSessions, isLoggingEnabled else { return } + Swift.print(items, separator: separator, terminator: terminator) + #endif + } + + /** + Writes the textual representations of the given items most suitable for debugging into the standard output. + + - parameter items: Zero or more items to print. + - parameter separator: A string to print between each item. The default is a single space (" "). + - parameter terminator: The string to print after all items have been printed. The default is a newline ("\n"). + + */ + func debugPrint(_ items: Any..., separator: String = " ", terminator: String = "\n") { + #if DEBUG + guard NKConfiguration.allowLoggingOnAllSessions, isLoggingEnabled else { return } + Swift.debugPrint(items, separator: separator, terminator: terminator) + #endif + } + + /** + Handles APIRequest logging sent by the `NetworkKit`. + + - parameter request: URLRequest + - parameter apiName: API name. + */ + func logAPIRequest(request: URLRequest?, apiName: String?) { + #if DEBUG + guard NKConfiguration.allowLoggingOnAllSessions, isLoggingEnabled else { return } + + Swift.print( + """ + ------------------------------------------------------------ + API Call Request for: + Name: \(apiName ?? "nil") + \(request?.debugDescription ?? "") + + """ + ) + #endif + } + + /** + Print JSON sent by the `NetworkKit`. + + - parameter data: Input Type to be printed + - parameter apiName: API name. + */ + func printJSON(data: Input, apiName: String) { + #if DEBUG + guard NKConfiguration.allowLoggingOnAllSessions, isLoggingEnabled else { return } + guard let data = data as? Data else { + return + } + + do { + let object = try JSONSerialization.jsonObject(with: data, options: []) + let newData = try JSONSerialization.data(withJSONObject: object, options: .prettyPrinted) + +// Swift.print( +// """ +// ------------------------------------------------------------ +// Printing JSON for: +// API Name: \(apiName) +// JSON: +// + // """) + Swift.print(""" + ------------------------------------------------------------ + JSON: + + """) + Swift.print(String(data: newData, encoding: .utf8) ?? "nil") + Swift.print("------------------------------------------------------------") + + } catch { + + } + #endif + } + + /** + Handles errors sent by the `NetworkKit`. + + - parameter error: Error occurred. + - parameter file: Source file name. + - parameter line: Source line number. + - parameter function: Source function name. + */ + @inline(__always) + func log(error: Error, file: StaticString = #file, line: UInt = #line, function: StaticString = #function) { + #if DEBUG + guard NKConfiguration.allowLoggingOnAllSessions, isLoggingEnabled else { return } + + Swift.print("⚠️ [NetworkKit: Error] \((String(describing: file) as NSString).lastPathComponent):\(line) \(function)\n ↪︎ \(error as NSError)\n") + #endif + } + + /** + Handles assertions made throughout the `NetworkKit`. + + - parameter condition: Assertion condition. + - parameter message: Assertion failure message. + - parameter file: Source file name. + - parameter line: Source line number. + - parameter function: Source function name. + */ + @inline(__always) + func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line, function: StaticString = #function) { + + #if DEBUG + let condition = condition() + + if condition { return } + + let message = message() + + Swift.print("❗ [NetworkKit: Assertion Failure] \((String(describing: file) as NSString).lastPathComponent):\(line) \(function)\n ↪︎ \(message)\n") + Swift.assert(condition, message, file: file, line: line) + #endif + } + + /** + Handles assertion failures made throughout the `NetworkKit`. + + - parameter message: Assertion failure message. + - parameter file: Source file name. + - parameter line: Source line number. + - parameter function: Source function name. + */ + @inlinable public func assertionFailure(_ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line, function: StaticString = #function) { + let message = message() + Swift.print("❗ [NetworkKit: Assertion Failure] \((String(describing: file) as NSString).lastPathComponent):\(line) \(function)\n ↪︎ \(message)\n") + Swift.assertionFailure(message, file: file, line: line) + } + + /** + Handles precondition failures made throughout the `NetworkKit`. + + - parameter message: Assertion failure message. + - parameter file: Source file name. + - parameter line: Source line number. + - parameter function: Source function name. + */ + @inlinable public func preconditionFailure(_ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line, function: StaticString = #function) -> Never { + let message = message() + Swift.print("❗ [NetworkKit: Assertion Failure] \((String(describing: file) as NSString).lastPathComponent):\(line) \(function)\n ↪︎ \(message)\n") + Swift.preconditionFailure(message, file: file, line: line) + } + + /** + Handles preconditions made throughout the `NetworkKit`. + + - parameter condition: Precondition to be satisfied. + - parameter message: Precondition failure message. + - parameter file: Source file name. + - parameter line: Source line number. + - parameter function: Source function name. + */ + @inline(__always) + func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line, function: StaticString = #function) { + + #if DEBUG + let condition = condition() + + if condition { return } + + let message = message() + + Swift.print("❗ [NetworkKit: Precondition Failure] \((String(describing: file) as NSString).lastPathComponent):\(line) \(function)\n ↪︎ \(message)\n") + Swift.preconditionFailure(message, file: file, line: line) + #endif + } + + /** + Handles fatal errors made throughout the `NetworkKit`. + - Important: Implementers should guarantee that this function doesn't return, either by calling another `Never` function such as `fatalError()` or `abort()`, or by raising an exception. + + - parameter message: Fatal error message. + - parameter file: Source file name. + - parameter line: Source line number. + - parameter function: Source function name. + */ + @inline(__always) + func fatalError(_ message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line, function: StaticString = #function) -> Never { + + #if DEBUG + let message = message() + Swift.print("❗ [NetworkKit: Fatal Error] \((String(describing: file) as NSString).lastPathComponent):\(line) \(function)\n ↪︎ \(message)\n") + Swift.fatalError(message, file: file, line: line) + #endif + } +} diff --git a/Sources/NetworkKit/Errors/Business Error.swift b/Sources/NetworkKit/Errors/Business Error.swift deleted file mode 100644 index fd6f8d9..0000000 --- a/Sources/NetworkKit/Errors/Business Error.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// BusinessError.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -/// Personal / Business / Server Errors -enum BusinessError: LocalizedError { - - case errorModel(ErrorModel, Int) - - var localizedDescription: String { - switch self { - case .errorModel(let model, _): - return model.message ?? "" - } - } - - var errorCode: Int { - switch self { - case .errorModel(let model, let httpStatusCode): - return model.code ?? httpStatusCode - } - } -} diff --git a/Sources/NetworkKit/Errors/HTTP Error.swift b/Sources/NetworkKit/Errors/HTTP Error.swift deleted file mode 100644 index f5f8c85..0000000 --- a/Sources/NetworkKit/Errors/HTTP Error.swift +++ /dev/null @@ -1,135 +0,0 @@ -// -// HTTPStatusCodes.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -/// HTTP Status Codes, obtained in `HTTPURLResponse` -enum HTTPStatusCode: Int, LocalizedError { - - case `continue` = 100 - case switchingProtocols = 101 - case processing = 102 - case earlyHints = 103 - - - case ok = 200 - case created = 201 - case accepted = 202 - case nonAuthoritativeInformation = 203 - case noContent = 204 - case resetContent = 205 - case partialContent = 206 - case multiStatus = 207 - case alreadyReported = 208 - case imUsed = 226 - - - case multipleChoices = 300 - case movedPermanently = 301 - case found = 302 - case seeOther = 303 - case notModified = 304 - case useProxy = 305 - case temporaryRedirect = 307 - case permanentRedirect = 308 - - - case badRequest = 400 - case unauthorized = 401 - case paymentRequired = 402 - case forbidden = 403 - case notFound = 404 - case methodNotAllowed = 405 - case notAcceptable = 406 - case proxyAuthenticationRequired = 407 - case requestTimeout = 408 - case conflict = 409 - case gone = 410 - case lengthRequired = 411 - case preconditionFailed = 412 - case payloadTooLarge = 413 - case uriTooLong = 414 - case unsupportedMediaType = 415 - case rangeNotSatisfiable = 416 - case expectationFailed = 417 - - case imATeapot = 418 - case misdirectedRequest = 421 - case unprocessableEntity = 422 - case locked = 423 - case failedDependency = 424 - case tooEarly = 425 - case upgradeRequired = 426 - case preconditionRequired = 428 - case tooManyRequests = 429 - case requestHeaderFieldsTooLarge = 431 - case iisLoginTimeout = 440 - case nginxNoResponse = 444 - case iisRetryWith = 449 - case blockedByWindowsParentalControls = 450 - case unavailableForLegalReasons = 451 - case nginxSSLCertificateError = 495 - case nginxSSLCertificateRequired = 496 - - case nginxHTTPToHTTPS = 497 - case tokenExpired = 498 - case nginxClientClosedRequest = 499 - - - case internalServerError = 500 - case notImplemented = 501 - case badGateway = 502 - case serviceUnavailable = 503 - case gatewayTimeout = 504 - case httpVersionNotSupported = 505 - case variantAlsoNegotiates = 506 - case insufficientStorage = 507 - case loopDetected = 508 - case bandwidthLimitExceeded = 509 - case notExtended = 510 - case networkAuthenticationRequired = 511 - case siteIsFrozen = 530 - case networkConnectTimeoutError = 599 - - /// Retrieve the localized description for this error. - var localizedDescription: String { - return HTTPURLResponse.localizedString(forStatusCode: rawValue) - } - - var errorDescription: String? { - return HTTPURLResponse.localizedString(forStatusCode: rawValue) - } -} - -extension HTTPStatusCode { - /// Informational - Request received, continuing process. - var isInformational: Bool { - return isIn(range: 100...199) - } - /// Success - The action was successfully received, understood, and accepted. - var isSuccess: Bool { - return isIn(range: 200...299) - } - /// Redirection - Further action must be taken in order to complete the request. - var isRedirection: Bool { - return isIn(range: 300...399) - } - /// Client Error - The request contains bad syntax or cannot be fulfilled. - var isClientError: Bool { - return isIn(range: 400...499) - } - /// Server Error - The server failed to fulfill an apparently valid request. - var isServerError: Bool { - return isIn(range: 500...599) - } - - /// - returns: `true` if the status code is in the provided range, false otherwise. - private func isIn(range: ClosedRange) -> Bool { - return range.contains(rawValue) - } -} diff --git a/Sources/NetworkKit/Extensions/NSError+Extension.swift b/Sources/NetworkKit/Errors/NSError.swift similarity index 65% rename from Sources/NetworkKit/Extensions/NSError+Extension.swift rename to Sources/NetworkKit/Errors/NSError.swift index b970565..4708a1f 100644 --- a/Sources/NetworkKit/Extensions/NSError+Extension.swift +++ b/Sources/NetworkKit/Errors/NSError.swift @@ -1,8 +1,8 @@ // -// NSError+Extension.swift +// NSError.swift // NetworkKit // -// Created by Raghav Ahuja on 21/11/19. +// Created by Raghav Ahuja on 18/10/19. // Copyright © 2019 Raghav Ahuja. All rights reserved. // @@ -10,19 +10,21 @@ import Foundation extension NSError { + public static var networkKitErrorDomain: String { "NKErrorDomain" } + static func cancelled(for url: URL?) -> NSError { var userInfo: [String: Any] = [NSLocalizedDescriptionKey: "User cancelled the task for url: \(url?.absoluteString ?? "nil")."] if let url = url { userInfo[NSURLErrorFailingURLErrorKey] = url } - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled, userInfo: userInfo) + let error = NSError(domain: networkKitErrorDomain, code: NSURLErrorCancelled, userInfo: userInfo) return error } static func badServerResponse(for url: URL) -> NSError { - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorBadServerResponse, userInfo: [ + let error = NSError(domain: networkKitErrorDomain, code: NSURLErrorBadServerResponse, userInfo: [ NSURLErrorFailingURLErrorKey: url, NSLocalizedDescriptionKey: "Bad server response for request : \(url.absoluteString)" ]) @@ -30,8 +32,17 @@ extension NSError { return error } + static func badURL(for urlString: String?) -> NSError { + let error = NSError(domain: networkKitErrorDomain, code: NSURLErrorBadURL, userInfo: [ + NSURLErrorFailingURLStringErrorKey: urlString ?? "nil", + NSLocalizedDescriptionKey: "Invalid URL provied." + ]) + + return error + } + static func resourceUnavailable(for url: URL) -> NSError { - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorResourceUnavailable, userInfo: [ + let error = NSError(domain: networkKitErrorDomain, code: NSURLErrorResourceUnavailable, userInfo: [ NSURLErrorFailingURLErrorKey: url, NSLocalizedDescriptionKey: "A requested resource couldn’t be retrieved from url: \(url.absoluteString)." ]) @@ -45,13 +56,13 @@ extension NSError { userInfo[NSURLErrorFailingURLErrorKey] = url } - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorUnsupportedURL, userInfo: userInfo) + let error = NSError(domain: networkKitErrorDomain, code: NSURLErrorUnsupportedURL, userInfo: userInfo) return error } static func zeroByteResource(for url: URL) -> NSError { - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorZeroByteResource, userInfo: [ + let error = NSError(domain: networkKitErrorDomain, code: NSURLErrorZeroByteResource, userInfo: [ NSURLErrorFailingURLErrorKey: url, NSLocalizedDescriptionKey: "A server reported that a URL has a non-zero content length, but terminated the network connection gracefully without sending any data." ]) @@ -60,7 +71,7 @@ extension NSError { } static func cannotDecodeContentData(for url: URL) -> NSError { - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotDecodeContentData, userInfo: [ + let error = NSError(domain: networkKitErrorDomain, code: NSURLErrorCannotDecodeContentData, userInfo: [ NSURLErrorFailingURLErrorKey: url, NSLocalizedDescriptionKey: "Content data received during a connection request had an unknown content encoding." ]) @@ -69,7 +80,7 @@ extension NSError { } static func cannotDecodeRawData(for url: URL) -> NSError { - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotDecodeRawData, userInfo: [ + let error = NSError(domain: networkKitErrorDomain, code: NSURLErrorCannotDecodeRawData, userInfo: [ NSURLErrorFailingURLErrorKey: url, NSLocalizedDescriptionKey: "Content data received during a connection request had an unknown content encoding." ]) @@ -83,13 +94,13 @@ extension NSError { userInfo[NSURLErrorFailingURLErrorKey] = url } - let error = NSError(domain: NSURLErrorDomain, code: NSUserCancelledError, userInfo: userInfo) + let error = NSError(domain: networkKitErrorDomain, code: NSUserCancelledError, userInfo: userInfo) return error } static func unkown() -> NSError { - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorUnknown, userInfo: [ + let error = NSError(domain: networkKitErrorDomain, code: NSURLErrorUnknown, userInfo: [ NSLocalizedDescriptionKey: "An Unknown Occurred." ]) diff --git a/Sources/NetworkKit/Extensions/Dictionary+Extension.swift b/Sources/NetworkKit/Extensions/Dictionary+Extension.swift deleted file mode 100644 index 38bfeaf..0000000 --- a/Sources/NetworkKit/Extensions/Dictionary+Extension.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// Dictionary+Extension.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -extension Dictionary { - - var prettyPrint: String { - let prefix = isEmpty ? "" : "\n" - var printString = "\(prefix)[" - - for (key, value) in self { - let itemString = "\n\t\(key): \(value)," - printString.append(itemString) - } - - let postfix = isEmpty ? " " : "\n" - printString.append("\(postfix)]") - - return printString - } -} - -extension Dictionary where Value: OptionalDelegate { - - var prettyPrint: String { - let prefix = isEmpty ? "" : "\n" - var printString = "\(prefix)[" - - for (key, value) in self { - let optionalValue = value.unwrappedValue() - let value = optionalValue == nil ? "nil" : "\(optionalValue!)" - - let itemString = "\n\t\(key): \(value)," - printString.append(itemString) - } - - let postfix = isEmpty ? " " : "\n" - printString.append("\(postfix)]") - - return printString - } -} diff --git a/Sources/NetworkKit/Extensions/JSONDecoder+Extension.swift b/Sources/NetworkKit/Extensions/JSONDecoder+Extension.swift deleted file mode 100644 index 79b8a8d..0000000 --- a/Sources/NetworkKit/Extensions/JSONDecoder+Extension.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// JSONDecoder+Extension.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -extension JSONDecoder: NetworkDecoder { - - public typealias Input = Data -} diff --git a/Sources/NetworkKit/Extensions/Optional+Extension.swift b/Sources/NetworkKit/Extensions/Optional+Extension.swift deleted file mode 100644 index 07f1b81..0000000 --- a/Sources/NetworkKit/Extensions/Optional+Extension.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// Optional+Extension.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -protocol OptionalDelegate { - - associatedtype Wrapped - - func unwrappedValue() -> Wrapped? -} - -extension Optional: OptionalDelegate { - - func unwrappedValue() -> Wrapped? { - return self - } -} diff --git a/Sources/NetworkKit/Extensions/Array+Extension.swift b/Sources/NetworkKit/Extensions/Set+Extension.swift similarity index 83% rename from Sources/NetworkKit/Extensions/Array+Extension.swift rename to Sources/NetworkKit/Extensions/Set+Extension.swift index a420376..63b79dc 100644 --- a/Sources/NetworkKit/Extensions/Array+Extension.swift +++ b/Sources/NetworkKit/Extensions/Set+Extension.swift @@ -1,5 +1,5 @@ // -// Array+Extension.swift +// Set+Extension.swift // NetworkKit // // Created by Raghav Ahuja on 15/10/19. @@ -8,14 +8,14 @@ import Foundation -extension Array where Element == URLQueryItem { +extension Set where Element == URLQueryItem { mutating func addURLQuery(query urlQuery: URLQuery?) { if let urlQuery = urlQuery { for query in urlQuery { let queryItem = URLQueryItem(name: query.key, value: query.value) if !self.contains(queryItem) { - self.append(queryItem) + insert(queryItem) } } } diff --git a/Sources/NetworkKit/Extensions/URL+Extension.swift b/Sources/NetworkKit/Extensions/URL+Extension.swift deleted file mode 100644 index e20dad4..0000000 --- a/Sources/NetworkKit/Extensions/URL+Extension.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// URL+Extension.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -extension URL { - - var parameters: URLQuery { - guard let components = URLComponents(url: self, resolvingAgainstBaseURL: false) else { - return [:] - } - return components.queryItems?.toDictionary ?? [:] - } -} diff --git a/Sources/NetworkKit/Extensions/URLRequest+Extension.swift b/Sources/NetworkKit/Extensions/URLRequest+Extension.swift deleted file mode 100644 index b75db4f..0000000 --- a/Sources/NetworkKit/Extensions/URLRequest+Extension.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// URLRequest+Extension.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -extension URLRequest { - - var debugDescription: String { - """ - ------------------------------------------------------------ - Request Method: \(httpMethod ?? "nil") - Request URL: \(url?.absoluteString ?? "nil") - - Request Parameters: \((url?.parameters ?? [:]).prettyPrint) - - Request Headers: \((allHTTPHeaderFields ?? [:]).prettyPrint) - - Request HTTPBody: \(httpBody?.debugDescription ?? "nil") - ------------------------------------------------------------ - """ - } -} diff --git a/Sources/NetworkKit/Kit/NKAnyCancellable/NKAnyCancellable.swift b/Sources/NetworkKit/Kit/NKAnyCancellable/NKAnyCancellable.swift deleted file mode 100644 index 7984b62..0000000 --- a/Sources/NetworkKit/Kit/NKAnyCancellable/NKAnyCancellable.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// NKAnyCancellable.swift -// NetworkKit -// -// Created by Raghav Ahuja on 25/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public class NKAnyCancellable: NKCancellable { - - let block: () -> Void - - /// Initializes the cancellable object with the given cancel-time closure. - /// - /// - Parameter cancel: A closure that the `cancel()` method executes. - public init(cancel: @escaping () -> Void) { - block = cancel - } - - public init(_ canceller: C) { - block = canceller.cancel - } - - deinit { - cancel() - } - - public func cancel() { - block() - } -} diff --git a/Sources/NetworkKit/Kit/NKQueue/NKQueue.swift b/Sources/NetworkKit/Kit/NKQueue/NKQueue.swift deleted file mode 100644 index 297203a..0000000 --- a/Sources/NetworkKit/Kit/NKQueue/NKQueue.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// NKQueue.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public final class NKQueue { - - /// A queue that regulates the execution of operations. - /// It is a serial queue, that has `quality of service` of `utility`. - private let operationQueue: OperationQueue - - let request: URLRequest? - - let apiName: String - - var operations: [Operation] { - operationQueue.operations - } - - var isSuspended: Bool { - get { operationQueue.isSuspended } - set { operationQueue.isSuspended = newValue } - } - - init(operationQueue: OperationQueue, request: URLRequest?, apiName: String?) { - self.operationQueue = operationQueue - self.request = request - self.apiName = apiName ?? request?.url?.absoluteString ?? "nil" - } - - deinit { - operationQueue.cancelAllOperations() - } - - func addOperation(_ op: Operation) { - operationQueue.addOperation(op) - } - - func addOperation(_ block: @escaping () -> Void) { - operationQueue.addOperation(block) - } - - func cancelAllOperations() { - operationQueue.cancelAllOperations() - } -} diff --git a/Sources/NetworkKit/Kit/NKResult/NKResult.swift b/Sources/NetworkKit/Kit/NKResult/NKResult.swift deleted file mode 100644 index b78952c..0000000 --- a/Sources/NetworkKit/Kit/NKResult/NKResult.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// NKResult.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public final class NKResult { - - var result: Result? - - var operation: Operation? - - init(result: Result?) { - self.result = result - } - - init() { - let error = NSError.notStarted(for: nil) - result = .failure(error as! Failure) - } -} diff --git a/Sources/NetworkKit/Kit/Network Task.swift b/Sources/NetworkKit/Kit/Network Task.swift deleted file mode 100644 index 34314e9..0000000 --- a/Sources/NetworkKit/Kit/Network Task.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// NetworkTask.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public struct NetworkTask: NKPublisher { - - public var result: NKResult - - public var queue: NKQueue - - public typealias Output = (data: Data, response: HTTPURLResponse) - - public typealias Failure = NSError - - public let request: URLRequest? - - public let session: URLSession - - private let operationQueue: OperationQueue = { - let queue = OperationQueue() - queue.maxConcurrentOperationCount = 1 - queue.qualityOfService = .utility - queue.isSuspended = true - return queue - }() - - /// Creates a data task from the provided Network Request and URL session. - /// - Parameter session: The `URLSession` from `NetworkConfiguration` to create the data task. - /// - Parameter builder: The block which returns a `NetworkRequest` to create a URL session data task. - public init(session: NetworkConfiguration, _ builder: () -> NetworkRequest) { - self.init(session: session.session, builder) - } - - /// Creates a data task from the provided URL request and URL session. - /// - Parameter request: The `URLRequest` from which to create a URL session data task. - /// - Parameter session: The `URLSession` from `NetworkConfiguration` to create the data task. - /// - Parameter apiName: API Name for debug console logging. - public init(request: URLRequest, session: NetworkConfiguration, apiName: String? = nil) { - self.init(request: request, session: session.session, apiName: apiName) - } - - - /// Creates a data task from the provided URL and URL session. - /// - Parameter url: The `URL` from which to create a URL session data task. - /// - Parameter session: The `URLSession` from `NetworkConfiguration` to create the data task. - /// - Parameter apiName: API Name for debug console logging. - public init(url: URL, session: NetworkConfiguration, apiName: String? = nil) { - self.init(url: url, session: session.session, apiName: apiName) - } - - /// Creates a data task from the provided Network Request and URL session. - /// - Parameter session: The `URLSession` to create the data task. - /// - Parameter builder: The block which returns a `NetworkRequest` to create a URL session data task. - public init(session: URLSession, _ builder: () -> NetworkRequest) { - let requestBuilder = DispatchQueue.global(qos: .utility).sync { builder() } - - self.session = session - - request = requestBuilder.request - - result = .init() - queue = .init(operationQueue: operationQueue, - request: requestBuilder.request, - apiName: requestBuilder.apiName) - resume() - } - - /// Creates a data task from the provided URL request and URL session. - /// - Parameter request: The `URLRequest` from which to create a URL session data task. - /// - Parameter session: The `URLSession` to create the data task. - /// - Parameter apiName: API Name for debug console logging. - public init(request: URLRequest, session: URLSession, apiName: String? = nil) { - self.request = request - - self.session = session - - result = .init() - queue = .init(operationQueue: operationQueue, - request: request, - apiName: apiName) - resume() - } - - /// Creates a data task from the provided URL and URL session. - /// - Parameter url: The `URL` from which to create a URL session data task. - /// - Parameter session: The `URLSession` to create the data task. - /// - Parameter apiName: API Name for debug console logging. - public init(url: URL, session: URLSession, apiName: String? = nil) { - request = URLRequest(url: url) - - self.session = session - - result = .init() - queue = .init(operationQueue: operationQueue, - request: request, - apiName: apiName) - resume() - } - - private func resume() { - let fetchOperation = FetchOperation(session: session, request: request, result: result) - addToQueue(isSuspended: true, fetchOperation) - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Catch/Catch.swift b/Sources/NetworkKit/Kit/Publishers/Catch/Catch.swift deleted file mode 100644 index d395e3d..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Catch/Catch.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// Catch.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - struct Catch: NKPublisher where Upstream.Output == NewPublisher.Output { - - public var result: NKResult - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = Upstream.Output - - public typealias Failure = NewPublisher.Failure - - /// The publisher that this publisher receives elements from. - public let upstream: Upstream - - /// A closure that accepts the upstream failure as input and returns a publisher to replace the upstream publisher. - public let handler: (Upstream.Failure) -> NewPublisher - - /// Creates a publisher that handles errors from an upstream publisher by replacing the failed publisher with another publisher. - /// - /// - Parameters: - /// - upstream: The publisher that this publisher receives elements from. - /// - handler: A closure that accepts the upstream failure as input and returns a publisher to replace the upstream publisher. - public init(upstream: Upstream, handler: @escaping (Upstream.Failure) -> NewPublisher) { - self.upstream = upstream - self.handler = handler - result = .init() - - let operation = CatchOperation(upstream: upstream, handler: handler, result: result) - addToQueue(operation) - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Catch/Try Catch.swift b/Sources/NetworkKit/Kit/Publishers/Catch/Try Catch.swift deleted file mode 100644 index ba716d2..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Catch/Try Catch.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// Try Catch.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - struct TryCatch: NKPublisher where Upstream.Output == NewPublisher.Output { - - public var result: NKResult - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = Upstream.Output - - public typealias Failure = NewPublisher.Failure - - /// The publisher that this publisher receives elements from. - public let upstream: Upstream - - /// A closure that accepts the upstream failure as input and returns a publisher to replace the upstream publisher. - public let handler: (Upstream.Failure) throws -> NewPublisher - - /// Creates a publisher that handles errors from an upstream publisher by replacing the failed publisher with another publisher. - /// - /// - Parameters: - /// - upstream: The publisher that this publisher receives elements from. - /// - handler: A closure that accepts the upstream failure as input and returns a publisher to replace the upstream publisher. - public init(upstream: Upstream, handler: @escaping (Upstream.Failure) throws -> NewPublisher) { - self.upstream = upstream - self.handler = handler - result = .init() - - let operation = TryCatchOperation(upstream: upstream, handler: handler, result: result) - addToQueue(operation) - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Compact Map/Compact Map.swift b/Sources/NetworkKit/Kit/Publishers/Compact Map/Compact Map.swift deleted file mode 100644 index 408b02f..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Compact Map/Compact Map.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// Compact Map.swift -// NetworkKit -// -// Created by Raghav Ahuja on 26/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - /// A publisher that republishes all non-`nil` results of calling a closure with each received element. - struct CompactMap: NKPublisher { - - public var result: NKResult - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = MapOutput - - public typealias Failure = Upstream.Failure - - /// The publisher that this publisher receives elements from. - public let upstream: Upstream - - /// The closure that transforms elements from the upstream publisher. - public let transform: (Upstream.Output) -> Output? - - public init(upstream: Upstream, transform: @escaping (Upstream.Output) -> Output?) { - self.upstream = upstream - self.transform = transform - result = .init() - perform() - } - - private func perform() { - addToQueue { - self.doTransform() - } - } - - private func doTransform() { - let upstreamResult = upstream.result.result - - switch upstreamResult { - case .success(let output): - guard let newOutput = transform(output) else { - result.result = .none - return - } - result.result = .success(newOutput) - - case .failure(let error): - result.result = .failure(error) - - case .none: - result.result = .none - } - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Compact Map/Try Compact Map.swift b/Sources/NetworkKit/Kit/Publishers/Compact Map/Try Compact Map.swift deleted file mode 100644 index ec490f2..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Compact Map/Try Compact Map.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// Compact Map.swift -// NetworkKit -// -// Created by Raghav Ahuja on 26/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - /// A publisher that republishes all non-`nil` results of calling an error-throwing closure with each received element. - struct TryCompactMap: NKPublisher { - - public var result: NKResult - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = MapOutput - - public typealias Failure = Upstream.Failure - - /// The publisher that this publisher receives elements from. - public let upstream: Upstream - - /// The closure that transforms elements from the upstream publisher. - public let transform: (Upstream.Output) throws -> Output? - - public init(upstream: Upstream, transform: @escaping (Upstream.Output) throws -> Output?) { - self.upstream = upstream - self.transform = transform - result = .init() - perform() - } - - private func perform() { - addToQueue { - self.doTransform() - } - } - - private func doTransform() { - let upstreamResult = upstream.result.result - - switch upstreamResult { - case .success(let output): - do { - guard let newOutput = try transform(output) else { - result.result = .none - return - } - - result.result = .success(newOutput) - - } catch { - let error = error as NSError - result.result = .failure(error as! Failure) - } - - case .failure(let error): - result.result = .failure(error) - - case .none: - result.result = .none - } - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Debounce/Debounce.swift b/Sources/NetworkKit/Kit/Publishers/Debounce/Debounce.swift deleted file mode 100644 index 5573b05..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Debounce/Debounce.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// Debounce.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - struct Debounce: NKPublisher { - - public var result: NKResult { - upstream.result - } - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = Upstream.Output - - public typealias Failure = Upstream.Failure - - /// The publisher that this publisher receives elements from. - public let upstream: Upstream - - public let dueTime: DispatchTime - - public init(upstream: Upstream, dueTime: DispatchTime) { - self.upstream = upstream - self.dueTime = dueTime - perform() - } - - private func perform() { - let operation = DebounceOperation(result: result, url: queue.request?.url, dueTime: dueTime) - queue.operations.first?.addDependency(operation) - addToQueue(operation) - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Decode/Decode.swift b/Sources/NetworkKit/Kit/Publishers/Decode/Decode.swift deleted file mode 100644 index d7020f1..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Decode/Decode.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// Decode.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - struct Decode: NKPublisher where Upstream.Output == Decoder.Input { - - public var result: NKResult - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = Item - - public typealias Failure = NSError - - /// The publisher that this publisher receives elements from. - public let upstream: Upstream - - public let decoder: Decoder - - public init(upstream: Upstream, decoder: Decoder) { - self.upstream = upstream - self.decoder = decoder - result = .init() - perform() - } - - public init(upstream: Upstream, jsonKeyDecodingStrategy: JSONDecoder.KeyDecodingStrategy) { - self.upstream = upstream - - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = jsonKeyDecodingStrategy - self.decoder = decoder as! Decoder - - result = .init() - perform() - } - - private func perform() { - addToQueue { - self.doDecoding() - } - } - - private func doDecoding() { - let upstreamResult = upstream.result.result - - switch upstreamResult { - case .success(let data): - - do { - let output = try decoder.decode(Item.self, from: data) - self.result.result = .success(output) - - } catch { - result.result = .failure(error as NSError) - } - - case .failure(let error): - result.result = .failure(error as NSError) - - case .none: - result.result = .none - } - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Map/Error/Map Error.swift b/Sources/NetworkKit/Kit/Publishers/Map/Error/Map Error.swift deleted file mode 100644 index d266aac..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Map/Error/Map Error.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// Map Error.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - struct MapError: NKPublisher { - - public var result: NKResult - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = Upstream.Output - - public typealias Failure = MapFailure - - /// The publisher that this publisher receives elements from. - public let upstream: Upstream - - /// The closure that transforms elements from the upstream publisher. - public let transform: (Upstream.Failure) -> MapFailure - - public init(upstream: Upstream, transform: @escaping (Upstream.Failure) -> MapFailure) { - self.upstream = upstream - self.transform = transform - result = .init(result: .none) - perform() - } - - private func perform() { - addToQueue { - self.doTransform() - } - } - - private func doTransform() { - let upstreamResult = upstream.result.result - - switch upstreamResult { - case .success(let output): - result.result = .success(output) - - case .failure(let error): - let newError = transform(error) - result.result = .failure(newError) - - case .none: - result.result = .none - } - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Map/Keypath/Map Keypath 2.swift b/Sources/NetworkKit/Kit/Publishers/Map/Keypath/Map Keypath 2.swift deleted file mode 100644 index f05e589..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Map/Keypath/Map Keypath 2.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// Map KeyPath 2.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -extension NKPublishers { - - /// A publisher that publishes the values of two key paths as a tuple. - public struct MapKeyPath2: NKPublisher { - - public var result: NKResult<(Output0, Output1), Upstream.Failure> - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = (Output0, Output1) - - public typealias Failure = Upstream.Failure - - /// The publisher from which this publisher receives elements. - public let upstream: Upstream - - /// The key path of a property to publish. - public let keyPath0: KeyPath - - /// The key path of a second property to publish. - public let keyPath1: KeyPath - - public init(upstream: Upstream, keyPath0: KeyPath, keyPath1: KeyPath) { - self.upstream = upstream - self.keyPath0 = keyPath0 - self.keyPath1 = keyPath1 - - result = .init() - perform() - } - - private func perform() { - addToQueue { - self.map() - } - } - - private func map() { - switch upstream.result.result { - case .success(let output): - let output1 = output[keyPath: keyPath0] - let output2 = output[keyPath: keyPath1] - - result.result = .success((output1, output2)) - - case .failure(let error): - result.result = .failure(error) - - case .none: - result.result = .none - } - } - } - -} diff --git a/Sources/NetworkKit/Kit/Publishers/Map/Keypath/Map Keypath.swift b/Sources/NetworkKit/Kit/Publishers/Map/Keypath/Map Keypath.swift deleted file mode 100644 index decd805..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Map/Keypath/Map Keypath.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// Map KeyPath.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - /// A publisher that publishes the value of a key path. - struct MapKeyPath: NKPublisher { - - public var result: NKResult - - public var queue: NKQueue { - upstream.queue - } - - public typealias Failure = Upstream.Failure - - /// The publisher from which this publisher receives elements. - public let upstream: Upstream - - /// The key path of a property to publish. - public let keyPath: KeyPath - - public init(upstream: Upstream, keyPath: KeyPath) { - self.upstream = upstream - self.keyPath = keyPath - result = .init() - perform() - } - - private func perform() { - addToQueue { - self.map() - } - } - - private func map() { - switch upstream.result.result { - case .success(let output): - result.result = .success(output[keyPath: keyPath]) - - case .failure(let error): - result.result = .failure(error) - - case .none: - result.result = .none - } - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Map/Map.swift b/Sources/NetworkKit/Kit/Publishers/Map/Map.swift deleted file mode 100644 index e6ab7d9..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Map/Map.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// Map.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - struct Map: NKPublisher { - - public var result: NKResult - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = MapOutput - - public typealias Failure = Upstream.Failure - - /// The publisher that this publisher receives elements from. - public let upstream: Upstream - - /// The closure that transforms elements from the upstream publisher. - public let transform: (Upstream.Output) -> Output - - public init(upstream: Upstream, transform: @escaping (Upstream.Output) -> Output) { - self.upstream = upstream - self.transform = transform - result = .init() - perform() - } - - private func perform() { - addToQueue { - self.doTransform() - } - } - - private func doTransform() { - let upstreamResult = upstream.result.result - - switch upstreamResult { - case .success(let output): - let newOutput = transform(output) - result.result = .success(newOutput) - - case .failure(let error): - result.result = .failure(error) - - case .none: - result.result = .none - } - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Map/Try Map.swift b/Sources/NetworkKit/Kit/Publishers/Map/Try Map.swift deleted file mode 100644 index a929cae..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Map/Try Map.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// Try Map.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - struct TryMap: NKPublisher { - - public var result: NKResult - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = MapOutput - - public typealias Failure = Upstream.Failure - - /// The publisher that this publisher receives elements from. - public let upstream: Upstream - - /// The closure that transforms elements from the upstream publisher. - public let transform: (Upstream.Output) throws -> Output - - public init(upstream: Upstream, transform: @escaping (Upstream.Output) throws -> Output) { - self.upstream = upstream - self.transform = transform - result = .init() - perform() - } - - private func perform() { - addToQueue { - self.doTransform() - } - } - - private func doTransform() { - let upstreamResult = upstream.result.result - - switch upstreamResult { - case .success(let output): - - do { - let newOutput = try transform(output) - result.result = .success(newOutput) - - } catch { - let error = error as NSError - result.result = .failure(error as! Failure) - } - - case .failure(let error): - result.result = .failure(error) - - case .none: - result.result = .none - } - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/NKPublishers.swift b/Sources/NetworkKit/Kit/Publishers/NKPublishers.swift deleted file mode 100644 index ab99513..0000000 --- a/Sources/NetworkKit/Kit/Publishers/NKPublishers.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// NKPublishers.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public enum NKPublishers { -} diff --git a/Sources/NetworkKit/Kit/Publishers/Replace Empty/Replace Empty.swift b/Sources/NetworkKit/Kit/Publishers/Replace Empty/Replace Empty.swift deleted file mode 100644 index d948035..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Replace Empty/Replace Empty.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// Replace Empty.swift -// NetworkKit -// -// Created by Raghav Ahuja on 26/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - /// A publisher that replaces an empty stream with a provided element. - struct ReplaceEmpty: NKPublisher { - - public var result: NKResult - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = Upstream.Output - - public typealias Failure = Never - - /// The element with which to replace errors from the upstream publisher. - public let output: Upstream.Output - - /// The publisher from which this publisher receives elements. - public let upstream: Upstream - - public init(upstream: Upstream, output: Output) { - self.upstream = upstream - self.output = output - result = .init(result: .success(output)) - perform() - } - - private func perform() { - let operation = ReplaceEmptyOperation(upstream: upstream, output: output, result: result) - addToQueue(operation) - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Replace Error/Replace Error.swift b/Sources/NetworkKit/Kit/Publishers/Replace Error/Replace Error.swift deleted file mode 100644 index ff1cd13..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Replace Error/Replace Error.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// Replace Error.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - /// A publisher that replaces any errors in the stream with a provided element. - struct ReplaceError: NKPublisher { - - public var result: NKResult - - public var queue: NKQueue { - upstream.queue - } - - public typealias Output = Upstream.Output - - public typealias Failure = Never - - /// The element with which to replace errors from the upstream publisher. - public let output: Upstream.Output - - /// The publisher from which this publisher receives elements. - public let upstream: Upstream - - public init(upstream: Upstream, output: Output) { - self.upstream = upstream - self.output = output - result = .init(result: .success(output)) - perform() - } - - private func perform() { - let operation = ReplaceErrorOperation(upstream: upstream, output: output, result: result) - addToQueue(operation) - } - } -} diff --git a/Sources/NetworkKit/Kit/Publishers/Validate/Validate.swift b/Sources/NetworkKit/Kit/Publishers/Validate/Validate.swift deleted file mode 100644 index 44598a5..0000000 --- a/Sources/NetworkKit/Kit/Publishers/Validate/Validate.swift +++ /dev/null @@ -1,156 +0,0 @@ -// -// Validate.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublishers { - - struct Validate: NKPublisher where Upstream.Output == NetworkTask.Output, Upstream.Failure == NetworkTask.Failure { - - public var queue: NKQueue { - upstream.queue - } - - public var result: NKResult - - public typealias Output = NetworkTask.Output - - public typealias Failure = NetworkTask.Failure - - /// The publisher from which this publisher receives elements. - public let upstream: Upstream - - /// Check for any business error model on failure. - public let shouldCheckForErrorModel: Bool - - /// Acceptable HTTP Status codes for the network call. - public let acceptableStatusCodes: [Int] - - public init(upstream: Upstream, shouldCheckForErrorModel: Bool, acceptableStatusCodes: [Int]) { - self.upstream = upstream - self.shouldCheckForErrorModel = shouldCheckForErrorModel - - self.acceptableStatusCodes = acceptableStatusCodes - - result = .init() - perform() - } - - private func perform() { - addToQueue(isSuspended: true) { - self.doValidation() - } - } - - private func doValidation() { - guard let url = queue.request?.url else { - result.result = .failure(.unsupportedURL(for: nil)) - return - } - - guard let (data, response) = try? upstream.result.result?.get() else { - result.result = upstream.result.result - return - } - - if acceptableStatusCodes.contains(response.statusCode) { - - guard !data.isEmpty else { - result.result = .failure(.zeroByteResource(for: url)) - return - } - - var acceptableContentTypes: [String] { - if let accept = queue.request?.value(forHTTPHeaderField: "Accept") { - return accept.components(separatedBy: ",") - } - - return ["*/*"] - } - - guard let responseContentType = response.mimeType, let responseMIMEType = MIMEType(responseContentType) else { - for contentType in acceptableContentTypes { - if let mimeType = MIMEType(contentType), mimeType.isWildcard { - result.result = .success((data, response)) - return - } - } - - result.result = .failure(.cannotDecodeContentData(for: url)) - return - } - - for contentType in acceptableContentTypes { - if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) { - result.result = .success((data, response)) - return - } - } - - result.result = .failure(.cannotDecodeContentData(for: url)) - - } else { - // On Failure it checks if it a business error. - - if shouldCheckForErrorModel, !data.isEmpty { - - let model = try? JSONDecoder().decode(ErrorModel.self, from: data) - if let errorModel = model { - let error = BusinessError.errorModel(errorModel, response.statusCode) - result.result = .failure(error as NSError) - - return - } - - // else throw http or url error - if let httpError = HTTPStatusCode(rawValue: response.statusCode) { - result.result = .failure(httpError as NSError) - } else { - result.result = .failure(.badServerResponse(for: url)) - } - } - } - } - } -} - - -private extension NKPublishers.Validate { - - /// ACCEPTABLE CONTENT TYPE CHECK - struct MIMEType { - let type: String - let subtype: String - - var isWildcard: Bool { return type == "*" && subtype == "*" } - - init?(_ string: String) { - let components: [String] = { - let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines) - let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)] - return split.components(separatedBy: "/") - }() - - if let type = components.first, let subtype = components.last { - self.type = type - self.subtype = subtype - } else { - return nil - } - } - - func matches(_ mime: MIMEType) -> Bool { - switch (type, subtype) { - case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"): - return true - default: - return false - } - } - } -} diff --git a/Sources/NetworkKit/Models/API Analytics.swift b/Sources/NetworkKit/Models/API Analytics.swift deleted file mode 100644 index e350326..0000000 --- a/Sources/NetworkKit/Models/API Analytics.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// APIAnalytics.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public struct APIAnalytics { - - public let apiName: String - public let urlString: String - public let totalTime: TimeInterval - public let errorCode: Int? - public let errorMessage: String? -} diff --git a/Sources/NetworkKit/Models/Error Model.swift b/Sources/NetworkKit/Models/Error Model.swift deleted file mode 100644 index 265ba29..0000000 --- a/Sources/NetworkKit/Models/Error Model.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// ErrorModel.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -// MARK: - ErrorModel -public struct ErrorModel: Codable { - - public let businessCode: Int? - public let errorCode: Int? - public let message: String? - public let status: String? - - public var code: Int? { - return businessCode ?? errorCode - } - - public enum CodingKeys: String, CodingKey { - case businessCode = "business_error_code" - case errorCode = "error_code" - case message - case status - } -} diff --git a/Sources/NetworkKit/Networking/Configuration/Network Configuration+Notification.swift b/Sources/NetworkKit/Networking/Configuration/Configuration+Notification.swift similarity index 82% rename from Sources/NetworkKit/Networking/Configuration/Network Configuration+Notification.swift rename to Sources/NetworkKit/Networking/Configuration/Configuration+Notification.swift index c3eec3c..df088ad 100644 --- a/Sources/NetworkKit/Networking/Configuration/Network Configuration+Notification.swift +++ b/Sources/NetworkKit/Networking/Configuration/Configuration+Notification.swift @@ -1,5 +1,5 @@ // -// Network Configuration+Notification.swift +// NKConfiguration+Notification.swift // NetworkKit // // Created by Raghav Ahuja on 15/10/19. @@ -10,7 +10,7 @@ import AppKit.NSApplication // MARK: - NOTIFICATION OBSERVERS -extension NetworkConfiguration { +extension NKConfiguration { var notification: Notification.Name { NSApplication.willTerminateNotification } } @@ -21,7 +21,7 @@ extension NetworkConfiguration { import UIKit.UIApplication // MARK: - NOTIFICATION OBSERVERS -extension NetworkConfiguration { +extension NKConfiguration { var notification: Notification.Name { UIApplication.willTerminateNotification } } diff --git a/Sources/NetworkKit/Networking/Configuration/Network Configuration.swift b/Sources/NetworkKit/Networking/Configuration/Configuration.swift similarity index 74% rename from Sources/NetworkKit/Networking/Configuration/Network Configuration.swift rename to Sources/NetworkKit/Networking/Configuration/Configuration.swift index 90648f2..65db5a8 100644 --- a/Sources/NetworkKit/Networking/Configuration/Network Configuration.swift +++ b/Sources/NetworkKit/Networking/Configuration/Configuration.swift @@ -1,5 +1,5 @@ // -// Network Configuration.swift +// NKConfiguration.swift // NetworkKit // // Created by Raghav Ahuja on 15/10/19. @@ -8,20 +8,32 @@ import Foundation -// MARK: - NETWORK CONFIGURATION +// MARK: - NKCONFIGURATION /// A Class that coordinates a group of related network data transfer tasks. -open class NetworkConfiguration { +open class NKConfiguration { - /// Allows Logs to be Printed in Debug Console. - /// Default value is `true` - open var debugPrint: Bool + /// Enables Logging for this session. + /// Default value is `true`. + open var enableLogs: Bool { + get { + logger.isLoggingEnabled + } set { + logger.isLoggingEnabled = newValue + } + } + + var logger = NKLogger() + + /// Should allow logging for all sessions. + /// Default value is `false`. + public static var allowLoggingOnAllSessions: Bool = true - /// Should Empty URL Cache Before Application Terminates. + /// Should empty cache before application terminates. /// Default value is `true`. open var emptyCacheOnAppTerminate: Bool - /// Should Empty URL Cache Before Application Terminates. + /// Should empty cache before application terminates. /// Default value is `false`. public static var emptyCacheOnAppTerminateOnAllSessions: Bool = false @@ -51,7 +63,7 @@ open class NetworkConfiguration { /// Inititalises Manager with `defaultURLSessionConfiguration` Configuration. public init(useDefaultCache: Bool, requestCachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy, cacheDiskPath diskPath: String? = nil) { - let configuration = NetworkConfiguration.defaultConfiguration + let configuration = NKConfiguration.defaultConfiguration configuration.requestCachePolicy = requestCachePolicy if useDefaultCache { @@ -68,7 +80,7 @@ open class NetworkConfiguration { } session = URLSession(configuration: configuration) - debugPrint = true +// session.nkLogger = .init() emptyCacheOnAppTerminate = true setNotificationObservers() } @@ -76,7 +88,7 @@ open class NetworkConfiguration { public init(urlCache cache: URLCache? = nil, configuration config: URLSessionConfiguration) { urlCache = cache session = URLSession(configuration: config) - debugPrint = true +// session.nkLogger = .init() emptyCacheOnAppTerminate = true setNotificationObservers() } @@ -90,7 +102,7 @@ open class NetworkConfiguration { private func setNotificationObservers() { #if !canImport(WatchKit) NotificationCenter.default.addObserver(forName: notification, object: nil, queue: .init()) { [weak self] (_) in - if let `self` = self, self.emptyCacheOnAppTerminate || NetworkConfiguration.emptyCacheOnAppTerminateOnAllSessions { + if let `self` = self, self.emptyCacheOnAppTerminate || NKConfiguration.emptyCacheOnAppTerminateOnAllSessions { self.removeAllCachedResponses() } } @@ -100,7 +112,7 @@ open class NetworkConfiguration { // MARK: - URL CACHE MANAGER -extension NetworkConfiguration { +extension NKConfiguration { /// Remove All Cached Responses from this session. open func removeAllCachedResponses() { @@ -110,16 +122,9 @@ extension NetworkConfiguration { #if DEBUG public func printURLCacheDetails() { guard let cache = session.configuration.urlCache else { - print( - """ - - --------------------------------------------- - Cannot Print Cache Memory And Disk Capacity And Usage - Error - No URL Cache Found - --------------------------------------------- - - """ - ) + logger.print( + "Cannot Print Cache Memory And Disk Capacity And Usage", + "Error - No URL Cache Found") return } let byteToMb: Double = 1048576 @@ -130,26 +135,19 @@ extension NetworkConfiguration { let diskCapacity = Double(cache.diskCapacity) / byteToMb let diskUsage = Double(cache.currentDiskUsage) / byteToMb - print( - """ - - --------------------------------------------- - Current URL Cache Memory And Disk Capacity And Usage - Memory Capacity: \(String(format: "%.2f", memoryCapacity)) Mb - Memory Usage: \(String(format: "%.3f", memoryUsage)) Mb - - Disk Capacity: \(String(format: "%.2f", diskCapacity)) Mb - Disk Usage: \(String(format: "%.3f", diskUsage)) Mb - --------------------------------------------- - - """ + logger.print( + "Current URL Cache Memory And Disk Capacity And Usage", + "Memory Capacity: \(String(format: "%.2f", memoryCapacity)) Mb", + "Memory Usage: \(String(format: "%.3f", memoryUsage)) Mb", + "Disk Capacity: \(String(format: "%.2f", diskCapacity)) Mb", + "Disk Usage: \(String(format: "%.3f", diskUsage)) Mb" ) } #endif } // MARK: - SERVER ENVIRONMENT -public extension NetworkConfiguration { +public extension NKConfiguration { /// Updates the current environment. /// - Parameter newEnvironment: New Server Environment to be set. diff --git a/Sources/NetworkKit/Networking/NKImageSession/NKImageSession+ImageType.swift b/Sources/NetworkKit/Networking/ImageSession/ImageSession+ImageType.swift similarity index 100% rename from Sources/NetworkKit/Networking/NKImageSession/NKImageSession+ImageType.swift rename to Sources/NetworkKit/Networking/ImageSession/ImageSession+ImageType.swift diff --git a/Sources/NetworkKit/Networking/NKImageSession/NKImageSession.swift b/Sources/NetworkKit/Networking/ImageSession/ImageSession.swift similarity index 73% rename from Sources/NetworkKit/Networking/NKImageSession/NKImageSession.swift rename to Sources/NetworkKit/Networking/ImageSession/ImageSession.swift index 5d88903..f039708 100644 --- a/Sources/NetworkKit/Networking/NKImageSession/NKImageSession.swift +++ b/Sources/NetworkKit/Networking/ImageSession/ImageSession.swift @@ -8,12 +8,12 @@ import Foundation -public final class NKImageSession: NetworkConfiguration { +public final class NKImageSession: NKConfiguration { private typealias ImageValidationResult = (response: HTTPURLResponse, data: Data, image: ImageType) public static let shared = NKImageSession() - + public init(useCache: Bool = true, cacheDiskPath: String? = "cachedImages") { let requestCachePolicy: NSURLRequest.CachePolicy = useCache ? .returnCacheDataElseLoad : .useProtocolCachePolicy super.init(useDefaultCache: useCache, requestCachePolicy: requestCachePolicy, cacheDiskPath: cacheDiskPath) @@ -37,27 +37,11 @@ public extension NKImageSession { let task = session.dataTask(with: request) { [weak self] (data, response, error) in guard let `self` = self else { - let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorUnknown, userInfo: nil) - completion(.failure(error)) return } if let error = error as NSError? { - - #if DEBUG - DebugPrint.print( - """ - - --------------------------------------------- - Cannot Fetch Image From: - URL: \(url.absoluteString) - Error: \(error) - --------------------------------------------- - - """ - , shouldPrint: self.debugPrint) - #endif - + self.logger.log(error: error) DispatchQueue.main.async { completion(.failure(error)) } @@ -72,24 +56,10 @@ public extension NKImageSession { completion(.success(value.image)) } - case .failure(let networkError): - - #if DEBUG - DebugPrint.print( - """ - - --------------------------------------------- - Cannot Fetch Image From: - URL: \(url.absoluteString) - Error: \(networkError.localizedDescription) - --------------------------------------------- - - """ - , shouldPrint: self.debugPrint) - #endif - + case .failure(let error): + self.logger.log(error: error) DispatchQueue.main.async { - completion(.failure(networkError)) + completion(.failure(error)) } } } diff --git a/Sources/NetworkKit/Networking/NKSession.swift b/Sources/NetworkKit/Networking/Session.swift similarity index 57% rename from Sources/NetworkKit/Networking/NKSession.swift rename to Sources/NetworkKit/Networking/Session.swift index 0f56e99..1136c22 100644 --- a/Sources/NetworkKit/Networking/NKSession.swift +++ b/Sources/NetworkKit/Networking/Session.swift @@ -7,13 +7,27 @@ // import Foundation +import PublisherKit -final public class NKSession: NetworkConfiguration { +extension URLSession.NKDataTaskPublisher { + + public func validate() -> NKPublishers.Validate { + NKPublishers.Validate(upstream: self, shouldCheckForErrorModel: true, acceptableStatusCodes: NKSession.shared.acceptableStatusCodes) + } +} + +public typealias NKTask = URLSession.NKDataTaskPublisher +public typealias NKAnyCancellable = PublisherKit.NKAnyCancellable +public typealias NKPublishers = PublisherKit.NKPublishers + +final public class NKSession: NKConfiguration { public static let shared = NKSession() + private let queue = DispatchQueue(label: "com.networkkit.task-thread", qos: .utility, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil) + private init() { - super.init(configuration: NetworkConfiguration.defaultConfiguration) + super.init(configuration: NKConfiguration.defaultConfiguration) #if DEBUG if let environmentValue = UserDefaults.standard.value(forKey: "api_environment") as? String, !environmentValue.isEmpty { @@ -32,8 +46,18 @@ final public class NKSession: NetworkConfiguration { /// - Parameter builder: The block which returns a `NetworkRequest` to create a URL session data task. /// - Parameter apiName: API Name for debug console logging. /// - Returns: A publisher that wraps a data task for the URL request. - public func dataTask(_ builder: () -> NetworkRequest) -> NetworkTask { - NetworkTask(session: session, builder) + public func dataTask(file: StaticString = #file, line: UInt = #line, function: StaticString = #function, _ builder: () -> NKRequest) -> URLSession.NKDataTaskPublisher { + let creator = builder() + let urlRequest: URLRequest? = queue.sync { + creator.create() + return creator.request + } + + guard let request = urlRequest else { + NKLogger.default.preconditionFailure("Invalid Request Created", file: file, line: line, function: function) + } + + return session.nkTaskPublisher(for: request, apiName: creator.apiName) } /// Returns a publisher that wraps a URL session data task for a given URL request. @@ -42,8 +66,8 @@ final public class NKSession: NetworkConfiguration { /// - Parameter request: The URL request for which to create a data task. /// - Parameter apiName: API Name for debug console logging. /// - Returns: A publisher that wraps a data task for the URL request. - public func dataTask(for request: URLRequest, apiName: String? = nil) -> NetworkTask { - NetworkTask(request: request, session: session, apiName: apiName) + public func dataTask(for request: URLRequest, apiName: String? = nil) -> URLSession.NKDataTaskPublisher { + session.nkTaskPublisher(for: request) } /// Returns a publisher that wraps a URL session data task for a given URL. @@ -52,7 +76,7 @@ final public class NKSession: NetworkConfiguration { /// - Parameter url: The URL for which to create a data task. /// - Parameter apiName: API Name for debug console logging. /// - Returns: A publisher that wraps a data task for the URL. - public func dataTask(for url: URL, apiName: String? = nil) -> NetworkTask { - NetworkTask(url: url, session: session, apiName: apiName) + public func dataTask(for url: URL, apiName: String? = nil) -> URLSession.NKDataTaskPublisher { + session.nkTaskPublisher(for: url) } } diff --git a/Sources/NetworkKit/Operations/Assign Operation.swift b/Sources/NetworkKit/Operations/Assign Operation.swift deleted file mode 100644 index 5bf5096..0000000 --- a/Sources/NetworkKit/Operations/Assign Operation.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// Assign Operation.swift -// NetworkKit -// -// Created by Raghav Ahuja on 25/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -final class AssignOperation: Operation where Upstream.Failure == Never { - - let upstrem: Upstream - let keypath: ReferenceWritableKeyPath - let object: Root - - var updatedValue = false - - init(upstrem: Upstream, keypath: ReferenceWritableKeyPath, object: Root) { - self.upstrem = upstrem - self.keypath = keypath - self.object = object - } - - override func main() { - guard let output = try? upstrem.result.result?.get() else { - updatedValue = true - return - } - object[keyPath: keypath] = output - updatedValue = true - } - - override func cancel() { - super.cancel() - if !updatedValue { - main() - } - } -} diff --git a/Sources/NetworkKit/Operations/Asynchronous Operation.swift b/Sources/NetworkKit/Operations/Asynchronous Operation.swift deleted file mode 100644 index 908b9e8..0000000 --- a/Sources/NetworkKit/Operations/Asynchronous Operation.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// Asynchronous Operation.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -class AsynchronousOperation: Operation { - override var isAsynchronous: Bool { - return true - } - - private var _isFinished: Bool = false - override var isFinished: Bool { - get { - return _isFinished - } set { - willChangeValue(forKey: "isFinished") - _isFinished = newValue - didChangeValue(forKey: "isFinished") - } - } - - private var _isExecuting: Bool = false - override var isExecuting: Bool { - get { - return _isExecuting - } set { - willChangeValue(forKey: "isExecuting") - _isExecuting = newValue - didChangeValue(forKey: "isExecuting") - } - } - - override func start() { - guard !isCancelled else { - return - } - isExecuting = true - main() - } - - override func main() { - fatalError("Implement in sublcass to perform task") - } - - func finish() { - isExecuting = false - isFinished = true - } -} diff --git a/Sources/NetworkKit/Operations/Base Block Operation.swift b/Sources/NetworkKit/Operations/Base Block Operation.swift deleted file mode 100644 index f86ce7b..0000000 --- a/Sources/NetworkKit/Operations/Base Block Operation.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// Base Block Operation.swift -// NetworkKit -// -// Created by Raghav Ahuja on 23/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -class BaseBlockOperation: BlockOperation { - - var result: NKResult - var request: URLRequest? - - init(request: URLRequest?, result: NKResult, block: @escaping () -> Void) { - self.request = request - self.result = result - super.init() - addExecutionBlock(block) - } - - override func cancel() { - let error = NSError.cancelled(for: request?.url) - result.result = .failure(error as! Failure) - super.cancel() - } -} diff --git a/Sources/NetworkKit/Operations/Catch Operation.swift b/Sources/NetworkKit/Operations/Catch Operation.swift deleted file mode 100644 index 33422ef..0000000 --- a/Sources/NetworkKit/Operations/Catch Operation.swift +++ /dev/null @@ -1,81 +0,0 @@ -// -// Catch Operation.swift -// NetworkKit -// -// Created by Raghav Ahuja on 21/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -final class CatchOperation: AsynchronousOperation where Upstream.Output == NewPublisher.Output { - - typealias Output = Upstream.Output - - typealias Failure = NewPublisher.Failure - - /// The publisher that this publisher receives elements from. - private let upstream: Upstream - - private var result: NKResult - - /// A closure that accepts the upstream failure as input and returns a publisher to replace the upstream publisher. - private let handler: (Upstream.Failure) -> NewPublisher - - private var newOperation: Operation? - - init(upstream: Upstream, handler: @escaping (Upstream.Failure) -> NewPublisher, result: NKResult) { - self.upstream = upstream - self.handler = handler - self.result = result - } - - override func main() { - guard !isCancelled else { - return - } - - switch upstream.result.result { - case .success(let output): - result.result = .success(output) - - case .failure(let error): - let newPublisher = handler(error) - - guard let newOperation = newPublisher.result.operation else { - return - } - - self.newOperation = newOperation - - newOperation.completionBlock = { [weak self] in - guard !(self?.isCancelled ?? true) else { - return - } - - switch newPublisher.result.result! { - case .success(let output): - self?.result.result = .success(output) - - case .failure(let error): - self?.result.result = .failure(error) - } - - newPublisher.queue.cancelAllOperations() - self?.finish() - } - - newOperation.main() - - case .none: - result.result = .none - } - } - - override func cancel() { - newOperation?.cancel() - let error = NSError.cancelled(for: upstream.queue.request?.url) - result.result = .failure(error as! Failure) - super.cancel() - } -} diff --git a/Sources/NetworkKit/Operations/Debouce Operation.swift b/Sources/NetworkKit/Operations/Debouce Operation.swift deleted file mode 100644 index f0fb097..0000000 --- a/Sources/NetworkKit/Operations/Debouce Operation.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// Debounce Operation.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -final class DebounceOperation: AsynchronousOperation { - - private let dueTime: DispatchTime - - private let result: NKResult - - private let url: URL? - - init(result: NKResult, url: URL?, dueTime: DispatchTime) { - self.result = result - self.url = url - self.dueTime = dueTime - super.init() - queuePriority = .veryHigh - } - - override func main() { - guard !isCancelled else { - return - } - - DispatchQueue.global(qos: .utility).asyncAfter(deadline: dueTime) { [weak self] in - self?.finish() - } - } - - override func cancel() { - let error = NSError.cancelled(for: url) - result.result = .failure(error as! Failure) - super.cancel() - } -} diff --git a/Sources/NetworkKit/Operations/Fetch Operation.swift b/Sources/NetworkKit/Operations/Fetch Operation.swift deleted file mode 100644 index 02bd341..0000000 --- a/Sources/NetworkKit/Operations/Fetch Operation.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// Fetch Operation.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -final class FetchOperation: AsynchronousOperation { - - private let result: NKResult? - - private let request: URLRequest? - - private let session: URLSession - - var task: URLSessionDataTask? - - init(session: URLSession, request: URLRequest?, result: NKResult?) { - self.session = session - self.request = request - self.result = result - super.init() - - queuePriority = .high - } - - override func main() { - guard !isCancelled else { - return - } - - guard let request = request else { - result?.result = .failure(NSError.unsupportedURL(for: nil)) - finish() - return - } - - task = session.dataTask(with: request) { [weak self] (data, response, error) in - if let error = error as NSError? { - self?.result?.result = .failure(error) - } else if let response = response as? HTTPURLResponse, let data = data { - self?.result?.result = .success((data, response)) - } - - self?.finish() - } - - task?.resume() - } - - override func cancel() { - result?.result = .failure(NSError.cancelled(for: request?.url)) - task?.cancel() - super.cancel() - } -} diff --git a/Sources/NetworkKit/Operations/Replace Empty Operation.swift b/Sources/NetworkKit/Operations/Replace Empty Operation.swift deleted file mode 100644 index f2711b5..0000000 --- a/Sources/NetworkKit/Operations/Replace Empty Operation.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// Replace Empty Operation.swift -// NetworkKit -// -// Created by Raghav Ahuja on 26/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -final class ReplaceEmptyOperation: Operation { - - typealias Output = Upstream.Output - - private let upstream: Upstream - - private let output: Output - - private let result: NKResult - - init(upstream: Upstream, output: Output, result: NKResult) { - self.upstream = upstream - self.output = output - self.result = result - } - - override func main() { - let upstreamResult = upstream.result.result - - switch upstreamResult { - case .success(let output): - result.result = .success(output) - - case .failure, .none: - result.result = .success(output) - } - } - - override func cancel() { - result.result = .success(output) - super.cancel() - } -} diff --git a/Sources/NetworkKit/Operations/Replace Error Operation.swift b/Sources/NetworkKit/Operations/Replace Error Operation.swift deleted file mode 100644 index c0f7a75..0000000 --- a/Sources/NetworkKit/Operations/Replace Error Operation.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// Replace Error Operation.swift -// NetworkKit -// -// Created by Raghav Ahuja on 25/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -final class ReplaceErrorOperation: Operation { - - typealias Output = Upstream.Output - - private let upstream: Upstream - - private let output: Output - - private let result: NKResult - - init(upstream: Upstream, output: Output, result: NKResult) { - self.upstream = upstream - self.output = output - self.result = result - } - - override func main() { - let upstreamResult = upstream.result.result - - switch upstreamResult { - case .success(let output): - result.result = .success(output) - - case .failure: - result.result = .success(output) - - case .none: - result.result = .none - } - } - - override func cancel() { - result.result = .success(output) - super.cancel() - } -} diff --git a/Sources/NetworkKit/Operations/TryCatch Operation.swift b/Sources/NetworkKit/Operations/TryCatch Operation.swift deleted file mode 100644 index 40fc45c..0000000 --- a/Sources/NetworkKit/Operations/TryCatch Operation.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// TryCatch Operation.swift -// NetworkKit -// -// Created by Raghav Ahuja on 21/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -final class TryCatchOperation: AsynchronousOperation where Upstream.Output == NewPublisher.Output { - - typealias Output = Upstream.Output - - typealias Failure = NewPublisher.Failure - - /// The publisher that this publisher receives elements from. - private let upstream: Upstream - - private var result: NKResult - - private var newOperation: Operation? - - /// A closure that accepts the upstream failure as input and returns a publisher to replace the upstream publisher. - private let handler: (Upstream.Failure) throws -> NewPublisher - - init(upstream: Upstream, handler: @escaping (Upstream.Failure) throws -> NewPublisher, result: NKResult) { - self.upstream = upstream - self.handler = handler - self.result = result - } - - override func main() { - guard !isCancelled else { - return - } - - switch upstream.result.result { - case .success(let output): - result.result = .success(output) - finish() - - case .failure(let error): - do { - let newPublisher = try handler(error) - - guard let newOperation = newPublisher.result.operation else { - let failError = NSError.unkown() - result.result = .failure(failError as! Failure) - return - } - - self.newOperation = newOperation - - newOperation.completionBlock = { [weak self] in - guard !(self?.isCancelled ?? true) else { - return - } - - switch newPublisher.result.result! { - case .success(let output): - self?.result.result = .success(output) - - case .failure(let error): - self?.result.result = .failure(error) - } - - newPublisher.queue.cancelAllOperations() - self?.finish() - } - - newOperation.main() - - } catch { - let error = error as NSError - result.result = .failure(error as! Failure) - finish() - } - - case .none: - result.result = .none - } - } - - override func cancel() { - newOperation?.cancel() - let error = NSError.cancelled(for: upstream.queue.request?.url) - result.result = .failure(error as! Failure) - super.cancel() - } -} diff --git a/Sources/NetworkKit/Protocols/NKImageSessionDelegate.swift b/Sources/NetworkKit/Protocols/ImageSessionDelegate.swift similarity index 91% rename from Sources/NetworkKit/Protocols/NKImageSessionDelegate.swift rename to Sources/NetworkKit/Protocols/ImageSessionDelegate.swift index 684ef86..639df1e 100644 --- a/Sources/NetworkKit/Protocols/NKImageSessionDelegate.swift +++ b/Sources/NetworkKit/Protocols/ImageSessionDelegate.swift @@ -63,15 +63,7 @@ private extension NKImageSessionDelegate { guard let urlStringValue = urlString, let url = URL(string: urlStringValue) else { #if DEBUG - print(""" - - --------------------------------------------- - Cannot Fetch Image From: - URL: \(String(describing: urlString)) - Error: \(URLError(.badURL).localizedDescription) - --------------------------------------------- - - """) + NKImageSession.shared.logger.log(error: NSError.badURL(for: urlString)) #endif if flag { diff --git a/Sources/NetworkKit/Protocols/NKCancellable/NKCancellable.swift b/Sources/NetworkKit/Protocols/NKCancellable/NKCancellable.swift deleted file mode 100644 index f7eb82f..0000000 --- a/Sources/NetworkKit/Protocols/NKCancellable/NKCancellable.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// NKCancellable.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public protocol NKCancellable { - - /// Cancel the activity. - func cancel() -} diff --git a/Sources/NetworkKit/Protocols/Network Decoder.swift b/Sources/NetworkKit/Protocols/Network Decoder.swift deleted file mode 100644 index 36d4301..0000000 --- a/Sources/NetworkKit/Protocols/Network Decoder.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// Network Decoder.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public protocol NetworkDecoder { - - associatedtype Input - - func decode(_ type: T.Type, from: Self.Input) throws -> T -} diff --git a/Sources/NetworkKit/Protocols/Publisher/NKPublisher+Methods.swift b/Sources/NetworkKit/Protocols/Publisher/NKPublisher+Methods.swift deleted file mode 100644 index 0d11bd0..0000000 --- a/Sources/NetworkKit/Protocols/Publisher/NKPublisher+Methods.swift +++ /dev/null @@ -1,217 +0,0 @@ -// -// NKPublisher+Methods.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public extension NKPublisher { - - /// Decodes the output from upstream using a specified `NetworkDecoder`. - /// For example, use `JSONDecoder`. - /// - Parameter type: Type to decode into. - /// - Parameter decoder: `NetworkDecoder` for decoding output. - func decode(type: Item.Type, decoder: Decoder) -> NKPublishers.Decode { - NKPublishers.Decode(upstream: self, decoder: decoder) - } - - /// Decodes the output from upstream using a specified `JSONDecoder`. - /// - Parameter type: Type to decode into. - /// - Parameter jsonKeyDecodingStrategy: JSON Key Decoding Strategy. Default value is `.useDefaultKeys`. - func decode(type: Item.Type, jsonKeyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> NKPublishers.Decode { - NKPublishers.Decode(upstream: self, jsonKeyDecodingStrategy: jsonKeyDecodingStrategy) - } - - /// Transforms all elements from the upstream publisher with a provided closure. - /// - /// - Parameter transform: A closure that takes one element as its parameter and returns a new element. - /// - Returns: A publisher that uses the provided closure to map elements from the upstream publisher to new elements that it then publishes. - func map(_ transform: @escaping (Output) -> T) -> NKPublishers.Map { - NKPublishers.Map(upstream: self, transform: transform) - } - - /// Transforms all elements from the upstream publisher with a provided error-throwing closure. - /// - /// If the `transform` closure throws an error, the publisher fails with the thrown error. - /// - Parameter transform: A closure that takes one element as its parameter and returns a new element. - /// - Returns: A publisher that uses the provided closure to map elements from the upstream publisher to new elements that it then publishes. - func tryMap(_ transform: @escaping (Output) throws -> T) -> NKPublishers.TryMap { - NKPublishers.TryMap(upstream: self, transform: transform) - } - - /// Replaces any errors in the stream with the provided element. - /// - /// If the upstream publisher fails with an error, this publisher emits the provided element, then finishes normally. - /// - Parameter output: An element to emit when the upstream publisher fails. - /// - Returns: A publisher that replaces an error from the upstream publisher with the provided output element. - func replaceError(with output: Output) -> NKPublishers.ReplaceError { - NKPublishers.ReplaceError(upstream: self, output: output) - } - - /// Publishes elements only after a specified time interval elapses between events. - /// - /// Use this operator when you want to wait for a pause in the delivery of events from the upstream publisher. For example, call `debounce` on the publisher from a text field to only receive elements when the user pauses or stops typing. When they start typing again, the `debounce` holds event delivery until the next pause. - /// - Parameters: - /// - dueTime: The time the publisher should wait before publishing an element. - /// - Returns: A publisher that publishes events only after a specified time elapses. - func debounce(_ dueTime: DispatchTimeInterval) -> NKPublishers.Debounce { - NKPublishers.Debounce(upstream: self, dueTime: .now() + dueTime) - } - - /// Publishes elements only after a specified time interval elapses between events. - /// - /// Use this operator when you want to wait for a pause in the delivery of events from the upstream publisher. For example, call `debounce` on the publisher from a text field to only receive elements when the user pauses or stops typing. When they start typing again, the `debounce` holds event delivery until the next pause. - /// - Parameters: - /// - dueTime: The time the publisher should wait before publishing an element. - func debounce(_ dueTime: DispatchTime) -> NKPublishers.Debounce { - NKPublishers.Debounce(upstream: self, dueTime: dueTime) - } - - /// Returns a publisher that publishes the value of a key path. - /// - /// - Parameter keyPath: The key path of a property on `Output` - /// - Returns: A publisher that publishes the value of the key path. - func map(_ keyPath: KeyPath) -> NKPublishers.MapKeyPath { - NKPublishers.MapKeyPath(upstream: self, keyPath: keyPath) - } - - /// Returns a publisher that publishes the values of two key paths as a tuple. - /// - /// - Parameters: - /// - keyPath0: The key path of a property on `Output` - /// - keyPath1: The key path of another property on `Output` - /// - Returns: A publisher that publishes the values of two key paths as a tuple. - func map(_ keyPath0: KeyPath, _ keyPath1: KeyPath) -> NKPublishers.MapKeyPath2 { - NKPublishers.MapKeyPath2(upstream: self, keyPath0: keyPath0, keyPath1: keyPath1) - } - - /// Handles errors from an upstream publisher by replacing it with another publisher. - /// - /// The following example replaces any error from the upstream publisher and replaces the upstream with a `Just` publisher. This continues the stream by publishing a single value and completing normally. - /// ``` - /// enum SimpleError: Error { case error } - /// let errorPublisher = (0..<10).publisher.tryMap { v -> Int in - /// if v < 5 { - /// return v - /// } else { - /// throw SimpleError.error - /// } - /// } - /// - /// let noErrorPublisher = errorPublisher.catch { _ in - /// return Just(100) - /// } - /// ``` - /// Backpressure note: This publisher passes through `request` and `cancel` to the upstream. After receiving an error, the publisher sends sends any unfulfilled demand to the new `Publisher`. - /// - Parameter handler: A closure that accepts the upstream failure as input and returns a publisher to replace the upstream publisher. - /// - Returns: A publisher that handles errors from an upstream publisher by replacing the failed publisher with another publisher. - func `catch`(_ handler: @escaping (Self.Failure) -> P) -> NKPublishers.Catch where Self.Output == P.Output { - NKPublishers.Catch(upstream: self, handler: handler) - } - - /// Handles errors from an upstream publisher by either replacing it with another publisher or `throw`ing a new error. - /// - /// - Parameter handler: A `throw`ing closure that accepts the upstream failure as input and returns a publisher to replace the upstream publisher or if an error is thrown will send the error downstream. - /// - Returns: A publisher that handles errors from an upstream publisher by replacing the failed publisher with another publisher. - func tryCatch(_ handler: @escaping (Self.Failure) throws -> P) -> NKPublishers.TryCatch where Self.Output == P.Output { - NKPublishers.TryCatch(upstream: self, handler: handler) - } - - /// Converts any failure from the upstream publisher into a new error. - /// - /// Until the upstream publisher finishes normally or fails with an error, the returned publisher republishes all the elements it receives. - /// - /// - Parameter transform: A closure that takes the upstream failure as a parameter and returns a new error for the publisher to terminate with. - /// - Returns: A publisher that replaces any upstream failure with a new error produced by the `transform` closure. - func mapError(_ transform: @escaping (Self.Failure) -> E) -> NKPublishers.MapError { - NKPublishers.MapError(upstream: self, transform: transform) - } - - /// Calls a closure with each received element and publishes any returned optional that has a value. - /// - /// - Parameter transform: A closure that receives a value and returns an optional value. - /// - Returns: A publisher that republishes all non-`nil` results of calling the transform closure. - func compactMap(_ transform: @escaping (Output) -> T?) -> NKPublishers.CompactMap { - NKPublishers.CompactMap(upstream: self, transform: transform) - } - - /// Calls an error-throwing closure with each received element and publishes any returned optional that has a value. - /// - /// If the closure throws an error, the publisher cancels the upstream and sends the thrown error to the downstream receiver as a `Failure`. - /// - Parameter transform: an error-throwing closure that receives a value and returns an optional value. - /// - Returns: A publisher that republishes all non-`nil` results of calling the transform closure. - func tryCompactMap(_ transform: @escaping (Self.Output) throws -> T?) -> NKPublishers.TryCompactMap { - NKPublishers.TryCompactMap(upstream: self, transform: transform) - } - - /// Replaces nil elements in the stream with the proviced element. - /// - /// - Parameter output: The element to use when replacing `nil`. - /// - Returns: A publisher that replaces `nil` elements from the upstream publisher with the provided element. - func replaceNil(with output: T) -> NKPublishers.Map where Output == T? { - NKPublishers.Map(upstream: self) { _ in output } - } - - /// Replaces an empty stream with the provided element. - /// - /// If the upstream publisher finishes without producing any elements, this publisher emits the provided element, then finishes normally. - /// - Parameter output: An element to emit when the upstream publisher finishes without emitting any elements. - /// - Returns: A publisher that replaces an empty stream with the provided output element. - func replaceEmpty(with output: Output) -> NKPublishers.ReplaceEmpty { - NKPublishers.ReplaceEmpty(upstream: self, output: output) - } -} - -public extension NKPublisher where Self.Output == NetworkTask.Output, Self.Failure == NetworkTask.Failure { - - /// Validates Network call response with provided acceptable status codes. - /// - Parameter acceptableStatusCodes: Acceptable HTTP Status codes for the network call. Default value is `Array(200 ..< 300)`. - /// - Parameter checkForErrorModel: If publisher should check for custom error model to decode. - /// - Returns: A publisher that validates response from an upstream publisher. - func validate(acceptableStatusCodes: [Int] = NKSession.shared.acceptableStatusCodes, checkForErrorModel: Bool = true) -> NKPublishers.Validate { - NKPublishers.Validate(upstream: self, shouldCheckForErrorModel: checkForErrorModel, acceptableStatusCodes: acceptableStatusCodes) - } -} - -public extension NKPublisher { - - /// Attaches a subscriber with closure-based behavior. - /// - /// This method creates the subscriber and immediately requests an unlimited number of values, prior to returning the subscriber. - /// - parameter block: The closure to execute on completion. - /// - Returns: A cancellable instance; used when you end assignment of the received value. Deallocation of the result will tear down the subscription stream. - func completion(_ block: @escaping (Result) -> Void) -> NKAnyCancellable { - queue.addOperation { - guard let output = self.result.result else { - return - } - - block(output) - } - - return NKAnyCancellable { - self.queue.cancelAllOperations() - } - } -} - -public extension NKPublisher where Self.Failure == Never { - - /// Assigns each element from a Publisher to a property on an object. - /// - /// - Parameters: - /// - keyPath: The key path of the property to assign. - /// - object: The object on which to assign the value. - /// - Returns: A cancellable instance; used when you end assignment of the received value. Deallocation of the result will tear down the subscription stream. - func assign(to keyPath: ReferenceWritableKeyPath, on object: Root) -> NKAnyCancellable { - let operation = AssignOperation(upstrem: self, keypath: keyPath, object: object) - queue.addOperation(operation) - - return NKAnyCancellable { - self.queue.cancelAllOperations() - } - } -} diff --git a/Sources/NetworkKit/Protocols/Publisher/NKPublisher+Queue.swift b/Sources/NetworkKit/Protocols/Publisher/NKPublisher+Queue.swift deleted file mode 100644 index f8ea9f1..0000000 --- a/Sources/NetworkKit/Protocols/Publisher/NKPublisher+Queue.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// NKPublisher+Queue.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -extension NKPublisher { - - func addToQueue(isSuspended: Bool = false, _ block: @escaping () -> Void) { - let op = BaseBlockOperation(request: queue.request, result: result, block: block) - result.operation = op - queue.addOperation(op) - queue(isSuspended: isSuspended) - } - - func addToQueue(isSuspended: Bool = false, _ op: Operation) { - result.operation = op - queue.addOperation(op) - queue(isSuspended: isSuspended) - } - - func queue(isSuspended: Bool) { - if queue.isSuspended != isSuspended { - queue.isSuspended = isSuspended - } - } -} diff --git a/Sources/NetworkKit/Protocols/Publisher/NKPublisher.swift b/Sources/NetworkKit/Protocols/Publisher/NKPublisher.swift deleted file mode 100644 index 4c25486..0000000 --- a/Sources/NetworkKit/Protocols/Publisher/NKPublisher.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// NKPublisher.swift -// NetworkKit -// -// Created by Raghav Ahuja on 18/11/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public protocol NKPublisher { - - /// The kind of values published by this publisher. - associatedtype Output - - /// The kind of errors this publisher might publish. - /// - /// Use `Never` if this `Publisher` does not publish errors. - associatedtype Failure: Error - - var result: NKResult { get } - - var queue: NKQueue { get } -} diff --git a/Sources/NetworkKit/Protocols/Server/Environment.swift b/Sources/NetworkKit/Protocols/Server/Environment.swift index 016dcb5..4bd4f0c 100644 --- a/Sources/NetworkKit/Protocols/Server/Environment.swift +++ b/Sources/NetworkKit/Protocols/Server/Environment.swift @@ -18,7 +18,7 @@ import Foundation It has a `current` property for maintaining the server environment. - To update the `current` environment, use `NetworkConfiguration.updateEnvironment(:_)`. + To update the `current` environment, use `NKConfiguration.updateEnvironment(:_)`. In `DEBUG` mode, it persists the `current` value in `UserDefaults`. */ diff --git a/Sources/NetworkKit/Request/Network Request.swift b/Sources/NetworkKit/Request/Network Request.swift deleted file mode 100644 index 50a76fe..0000000 --- a/Sources/NetworkKit/Request/Network Request.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// NetworkRequest.swift -// NetworkKit -// -// Created by Raghav Ahuja on 15/10/19. -// Copyright © 2019 Raghav Ahuja. All rights reserved. -// - -import Foundation - -public final class NetworkRequest { - - public private(set) var request: URLRequest? - - public let apiName: String - - public init(to endPoint: ConnectionRepresentable) { - request = CreateRequest(with: endPoint, query: nil)?.request - apiName = endPoint.name ?? "nil" - } - - public func urlQuery(_ query: URLQuery) -> Self { - guard let url = request?.url?.absoluteURL, - var components = URLComponents(string: url.absoluteString) else { - return self - } - - var items = components.queryItems ?? [] - items.addURLQuery(query: query) - components.queryItems = items - - self.request?.url = components.url - - #if DEBUG - DebugPrint.print( - """ - Request Dynamic URLQuery: - \(items.toDictionary.prettyPrint) - - --------------------------------------------- - """ - ) - #endif - return self - } - - public func requestBody(_ body: T, strategy: JSONEncoder.KeyEncodingStrategy = .useDefaultKeys) -> Self { - guard request != nil else { - return self - } - - var data: Data? = nil - - let encoder = JSONEncoder() - encoder.keyEncodingStrategy = strategy - encoder.outputFormatting = .prettyPrinted - - do { - let bodyData = try encoder.encode(body) - data = bodyData - - #if DEBUG - DebugPrint.print( - """ - Request Body: - \(String(data: bodyData, encoding: .utf8) ?? "nil") - - --------------------------------------------- - """ - ) - #endif - } catch { - #if DEBUG - DebugPrint.print( - """ - Request Body: nil - Error Encoding: - - \(error) - - --------------------------------------------- - """ - ) - #endif - - data = nil - } - - request?.httpBody = data - - var headers = request?.allHTTPHeaderFields ?? [:] - headers = headers.merging(HTTPBodyEncodingType.json.headers) { (_, new) in new } - request?.allHTTPHeaderFields = headers - - return self - } - - public func requestBody(_ body: [String: Any], encoding: HTTPBodyEncodingType) -> Self { - guard request != nil else { - return self - } - - #if DEBUG - DebugPrint.print(""" - Request Body: - \(body.prettyPrint) - - --------------------------------------------- - """) - #endif - - request?.httpBody = encoding.encode(body: body) - - var headers = request?.allHTTPHeaderFields ?? [:] - headers = headers.merging(encoding.headers) { (_, new) in new } - request?.allHTTPHeaderFields = headers - - return self - } - - public func requestBody(_ body: Data?, httpHeaders: HTTPHeaderParameters) -> Self { - guard request != nil else { - return self - } - - request?.httpBody = body - - var headers = request?.allHTTPHeaderFields ?? [:] - headers = headers.merging(httpHeaders) { (_, new) in new } - request?.allHTTPHeaderFields = headers - - return self - } - - public func httpHeaders(_ httpHeaders: HTTPHeaderParameters) -> Self { - guard request != nil else { - return self - } - - var headers = request?.allHTTPHeaderFields ?? [:] - headers = headers.merging(httpHeaders) { (_, new) in new } - request?.allHTTPHeaderFields = headers - - return self - } -} - diff --git a/Sources/NetworkKit/Request/Request.swift b/Sources/NetworkKit/Request/Request.swift new file mode 100644 index 0000000..b22d261 --- /dev/null +++ b/Sources/NetworkKit/Request/Request.swift @@ -0,0 +1,82 @@ +// +// NetworkRequest.swift +// NetworkKit +// +// Created by Raghav Ahuja on 15/10/19. +// Copyright © 2019 Raghav Ahuja. All rights reserved. +// + +import Foundation + +public typealias NetworkRequest = NKRequest + +public class NKRequest { + + var bodyData: Data? = nil + var headers: HTTPHeaderParameters = [:] + + var queryItems = Set() + + private var _request: URLRequest? = nil + + public var request: URLRequest? { + _request + } + + public let apiName: String + + public let connection: ConnectionRepresentable + + public init(to endPoint: ConnectionRepresentable) { + connection = endPoint + apiName = endPoint.name ?? "Nil" + } + + public func create() { + let creator = CreateRequest(with: connection, query: queryItems, body: bodyData, headers: headers) + _request = creator?.request + } + + public func urlQuery(_ query: URLQuery) -> Self { + queryItems.addURLQuery(query: query) + return self + } + + public func requestBody(_ body: T, strategy: JSONEncoder.KeyEncodingStrategy = .useDefaultKeys) -> Self { + let encoder = JSONEncoder() + encoder.keyEncodingStrategy = strategy + encoder.outputFormatting = .prettyPrinted + + do { + let bodyData = try encoder.encode(body) + self.bodyData = bodyData + } catch { + bodyData = nil + } + + headers = headers.merging(HTTPBodyEncodingType.json.headers) { (_, new) in new } + + return self + } + + public func requestBody(_ body: [String: Any], encoding: HTTPBodyEncodingType) -> Self { + bodyData = encoding.encode(body: body) + + headers = headers.merging(encoding.headers) { (_, new) in new } + + return self + } + + public func requestBody(_ body: Data?, httpHeaders: HTTPHeaderParameters) -> Self { + bodyData = body + headers = headers.merging(httpHeaders) { (_, new) in new } + + return self + } + + public func httpHeaders(_ httpHeaders: HTTPHeaderParameters) -> Self { + headers = headers.merging(httpHeaders) { (_, new) in new } + + return self + } +} diff --git a/Tests/NetworkKitTests/NetworkKitTests.swift b/Tests/NetworkKitTests/NetworkKitTests.swift index b9f5e2f..2e6af01 100644 --- a/Tests/NetworkKitTests/NetworkKitTests.swift +++ b/Tests/NetworkKitTests/NetworkKitTests.swift @@ -129,7 +129,7 @@ final class NetworkKitTests: XCTestCase { let expecatation = XCTestExpectation() func testExample() { - cancellable = NetworkTask(session: NKSession.shared) { + cancellable = NKSession.shared.dataTask { NetworkRequest(to: MockPoint.allUsers) } .map(\.data)