From 544f7126206506279ca18f63b5a541d63b63ef7f Mon Sep 17 00:00:00 2001 From: Jasper Blues Date: Fri, 6 Aug 2021 10:54:16 +0800 Subject: [PATCH 1/2] Allow overridden parameter types on GET/DELETE --- 2 | 18 ++++++++++++++++ Sources/Networking+HTTPRequests.swift | 30 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 2 diff --git a/2 b/2 new file mode 100644 index 00000000..f7effbe5 --- /dev/null +++ b/2 @@ -0,0 +1,18 @@ +Allow overridden parameter types on GET/DELETE + +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# +# Date: Fri Aug 6 10:54:16 2021 +0800 +# +# On branch master +# Your branch and 'origin/master' have diverged, +# and have 1 and 1 different commits each, respectively. +# (use "git pull" to merge the remote branch into yours) +# +# Changes to be committed: +# modified: Sources/Networking+HTTPRequests.swift +# +# Untracked files: +# .idea/ +# diff --git a/Sources/Networking+HTTPRequests.swift b/Sources/Networking+HTTPRequests.swift index 6a759eec..c47b16fe 100644 --- a/Sources/Networking+HTTPRequests.swift +++ b/Sources/Networking+HTTPRequests.swift @@ -16,6 +16,21 @@ public extension Networking { return handleJSONRequest(.get, path: path, cacheName: nil, parameterType: parameterType, parameters: parameters, responseType: .json, cachingLevel: cachingLevel, completion: completion) } + /// GET request to the specified path, allowing overridden parameter types + /// + /// Overridden parameter types can be useful, for example some REST-ful APIs do not strictly adhere to spec. + /// + /// - Parameters: + /// - path: The path for the GET request. + /// - parameterType: The parameters type to be used. + /// - parameters: The parameters to be used, they will be serialized using Percent-encoding and appended to the URL. + /// - completion: The result of the operation, it's an enum with two cases: success and failure. + /// - Returns: The request identifier. + @discardableResult + func get(_ path: String, parameterType: ParameterType, parameters: Any? = nil, completion: @escaping (_ result: JSONResult) -> Void) -> String { + handleJSONRequest(.get, path: path, cacheName: nil, parameterType: parameterType, parameters: parameters, responseType: .json, cachingLevel: .none, completion: completion) + } + /// Registers a fake GET request for the specified path. After registering this, every GET request to the path, will return the registered response. /// /// - Parameters: @@ -205,6 +220,21 @@ public extension Networking { return handleJSONRequest(.delete, path: path, cacheName: nil, parameterType: parameterType, parameters: parameters, responseType: .json, cachingLevel: .none, completion: completion) } + /// DELETE request to the specified path, allowing overridden parameter types + /// + /// Overridden parameter types can be useful, for example some REST-ful APIs do not strictly adhere to spec. + /// + /// - Parameters: + /// - path: The path for the DELETE request. + /// - parameterType: The parameters type to be used. + /// - parameters: The parameters to be used, they will be serialized using Percent-encoding and appended to the URL. + /// - completion: The result of the operation, it's an enum with two cases: success and failure. + /// - Returns: The request identifier. + @discardableResult + func delete(_ path: String, parameterType: ParameterType, parameters: Any? = nil, completion: @escaping (_ result: JSONResult) -> Void) -> String { + handleJSONRequest(.delete, path: path, cacheName: nil, parameterType: parameterType, parameters: parameters, responseType: .json, cachingLevel: .none, completion: completion) + } + /// Registers a fake DELETE request for the specified path. After registering this, every DELETE request to the path, will return the registered response. /// /// - Parameters: From 203bf640975e6165d84f484a277bc80c92c26cd0 Mon Sep 17 00:00:00 2001 From: Jasper Blues Date: Tue, 28 Sep 2021 12:21:28 +0800 Subject: [PATCH 2/2] Add error logger/provider. Default impl prints, as before. Custom can for eg log to sentry. --- Demo.xcodeproj/project.pbxproj | 18 ++++++++++ Sources/ErrorLogger.swift | 44 +++++++++++++++++++++++ Sources/Networking+Private.swift | 62 ++++++++++++++++---------------- Sources/Networking.swift | 2 ++ 4 files changed, 96 insertions(+), 30 deletions(-) create mode 100644 Sources/ErrorLogger.swift diff --git a/Demo.xcodeproj/project.pbxproj b/Demo.xcodeproj/project.pbxproj index 2e00632b..9fed5a90 100755 --- a/Demo.xcodeproj/project.pbxproj +++ b/Demo.xcodeproj/project.pbxproj @@ -183,6 +183,14 @@ 44DFD3B01D9A34540014E9F2 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 148341701CF1CF0500E91F01 /* Image.swift */; }; 44DFD3B11D9A3FC80014E9F2 /* pig.png in Resources */ = {isa = PBXBuildFile; fileRef = 14B0CEE31CF1D0D700049AD6 /* pig.png */; }; 44DFD3B21D9A3FC90014E9F2 /* pig.png in Resources */ = {isa = PBXBuildFile; fileRef = 14B0CEE31CF1D0D700049AD6 /* pig.png */; }; + BA79824C3FD10818DA976F8A /* ErrorLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7983C1B85B8AE9BAF59E3E /* ErrorLogger.swift */; }; + BA7985EE02F8B15CAE1C86DD /* ErrorLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7983C1B85B8AE9BAF59E3E /* ErrorLogger.swift */; }; + BA79866331D2E147C9A9EE21 /* ErrorLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7983C1B85B8AE9BAF59E3E /* ErrorLogger.swift */; }; + BA79878BCE133FA5429E6DAA /* ErrorLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7983C1B85B8AE9BAF59E3E /* ErrorLogger.swift */; }; + BA798837CAF824FACE6DDC91 /* ErrorLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7983C1B85B8AE9BAF59E3E /* ErrorLogger.swift */; }; + BA7988926836681D75A2D44C /* ErrorLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7983C1B85B8AE9BAF59E3E /* ErrorLogger.swift */; }; + BA798D222B6284F771465C87 /* ErrorLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7983C1B85B8AE9BAF59E3E /* ErrorLogger.swift */; }; + BA798DDD639F9AAFCF98EF39 /* ErrorLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7983C1B85B8AE9BAF59E3E /* ErrorLogger.swift */; }; E6ED61311E27CD630058ACE9 /* UnauthorizedCallbackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6ED61301E27CD630058ACE9 /* UnauthorizedCallbackTests.swift */; }; E6ED61321E27CD630058ACE9 /* UnauthorizedCallbackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6ED61301E27CD630058ACE9 /* UnauthorizedCallbackTests.swift */; }; E6ED61331E27CD630058ACE9 /* UnauthorizedCallbackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6ED61301E27CD630058ACE9 /* UnauthorizedCallbackTests.swift */; }; @@ -247,6 +255,7 @@ 44DFD39B1D9A2D5A0014E9F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44DFD39F1D9A2EA70014E9F2 /* CellData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellData.swift; sourceTree = ""; }; 44DFD3A11D9A33610014E9F2 /* FakeImageController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FakeImageController.swift; sourceTree = ""; }; + BA7983C1B85B8AE9BAF59E3E /* ErrorLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorLogger.swift; sourceTree = ""; }; E6ED61301E27CD630058ACE9 /* UnauthorizedCallbackTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnauthorizedCallbackTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -380,6 +389,7 @@ 4488DBC01D9BBFCF007D7A14 /* FakeRequest.swift */, 1474C1C21CA2988900E15D84 /* TestCheck.swift */, 446C24621DFEE964006FA6CC /* Helpers.swift */, + BA7983C1B85B8AE9BAF59E3E /* ErrorLogger.swift */, ); path = Sources; sourceTree = ""; @@ -815,6 +825,7 @@ 1413F4771CE7968E00482096 /* JSON.swift in Sources */, 4488DBCC1D9BCA0E007D7A14 /* FakeRequestTests.swift in Sources */, 445F053321007512003906C2 /* NetworkingTests.swift in Sources */, + BA798837CAF824FACE6DDC91 /* ErrorLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -834,6 +845,7 @@ 14638D0A1CC64629002B9433 /* Networking.swift in Sources */, 4488DBC51D9BBFCF007D7A14 /* FakeRequest.swift in Sources */, 14638D0B1CC64629002B9433 /* TestCheck.swift in Sources */, + BA7985EE02F8B15CAE1C86DD /* ErrorLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -853,6 +865,7 @@ 14638D201CC64733002B9433 /* Networking.swift in Sources */, 4488DBC61D9BBFCF007D7A14 /* FakeRequest.swift in Sources */, 14638D211CC64733002B9433 /* TestCheck.swift in Sources */, + BA79866331D2E147C9A9EE21 /* ErrorLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -872,6 +885,7 @@ 14638D361CC647B8002B9433 /* Networking.swift in Sources */, 4488DBC71D9BBFCF007D7A14 /* FakeRequest.swift in Sources */, 14638D371CC647B8002B9433 /* TestCheck.swift in Sources */, + BA798D222B6284F771465C87 /* ErrorLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -891,6 +905,7 @@ 14638D4C1CC64829002B9433 /* Networking.swift in Sources */, 4488DBC81D9BBFCF007D7A14 /* FakeRequest.swift in Sources */, 14638D4D1CC64829002B9433 /* TestCheck.swift in Sources */, + BA79878BCE133FA5429E6DAA /* ErrorLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -926,6 +941,7 @@ 1474C1C81CA2988900E15D84 /* Networking.swift in Sources */, 4488DBCA1D9BCA0E007D7A14 /* FakeRequestTests.swift in Sources */, 445F053121007512003906C2 /* NetworkingTests.swift in Sources */, + BA7988926836681D75A2D44C /* ErrorLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -961,6 +977,7 @@ 44B5D3A01D19B9E5004378B9 /* Networking.swift in Sources */, 4488DBCB1D9BCA0E007D7A14 /* FakeRequestTests.swift in Sources */, 445F053221007512003906C2 /* NetworkingTests.swift in Sources */, + BA79824C3FD10818DA976F8A /* ErrorLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -984,6 +1001,7 @@ 446C24631DFEE964006FA6CC /* Helpers.swift in Sources */, 44DFD3A01D9A2EA70014E9F2 /* CellData.swift in Sources */, 44A6D5481E4122DE00405A7E /* Networking+Private.swift in Sources */, + BA798DDD639F9AAFCF98EF39 /* ErrorLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Sources/ErrorLogger.swift b/Sources/ErrorLogger.swift new file mode 100644 index 00000000..8cb016e6 --- /dev/null +++ b/Sources/ErrorLogger.swift @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// SYMBIOSE +// Copyright 2020 Symbiose Inc +// All Rights Reserved. +// +// NOTICE: This software is proprietary information. +// Unauthorized use is prohibited. +// +//////////////////////////////////////////////////////////////////////////////// + +import Foundation + +public protocol ErrorLoggerProvider { + + func provide(error: Error?) -> ErrorLogger + +} + +public class ConsoleLogProvider: ErrorLoggerProvider { + + public func provide(error: Error?) -> ErrorLogger { + ConsoleErrorLogger() + } + +} + +public protocol ErrorLogger { + + func log(_ message: String) + + func flush() + +} + +class ConsoleErrorLogger: ErrorLogger { + + func log(_ message: String) { + print(message) + } + + func flush() {} // Not required for console logger + +} diff --git a/Sources/Networking+Private.swift b/Sources/Networking+Private.swift index 065e46cb..5af61694 100644 --- a/Sources/Networking+Private.swift +++ b/Sources/Networking+Private.swift @@ -347,32 +347,33 @@ extension Networking { func logError(parameterType: ParameterType?, parameters: Any? = nil, data: Data?, request: URLRequest?, response: URLResponse?, error: NSError?) { guard isErrorLoggingEnabled else { return } guard let error = error else { return } + let logger = logProvider.provide(error: error) - print(" ") - print("========== Networking Error ==========") - print(" ") + logger.log(" ") + logger.log("========== Networking Error ==========") + logger.log(" ") let isCancelled = error.code == NSURLErrorCancelled if isCancelled { if let request = request, let url = request.url { - print("Cancelled request: \(url.absoluteString)") - print(" ") + logger.log("Cancelled request: \(url.absoluteString)") + logger.log(" ") } } else { - print("*** Request ***") - print(" ") + logger.log("*** Request ***") + logger.log(" ") - print("Error \(error.code): \(error.description)") - print(" ") + logger.log("Error \(error.code): \(error.description)") + logger.log(" ") if let request = request, let url = request.url { - print("URL: \(url.absoluteString)") - print(" ") + logger.log("URL: \(url.absoluteString)") + logger.log(" ") } if let headers = request?.allHTTPHeaderFields { - print("Headers: \(headers)") - print(" ") + logger.log("Headers: \(headers)") + logger.log(" ") } if let parameterType = parameterType, let parameters = parameters { @@ -382,44 +383,45 @@ extension Networking { let data = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) let string = String(data: data, encoding: .utf8) if let string = string { - print("Parameters: \(string)") - print(" ") + logger.log("Parameters: \(string)") + logger.log(" ") } } catch let error as NSError { - print("Failed pretty printing parameters: \(parameters), error: \(error)") - print(" ") + logger.log("Failed pretty printing parameters: \(parameters), error: \(error)") + logger.log(" ") } case .formURLEncoded: guard let parametersDictionary = parameters as? [String: Any] else { fatalError("Couldn't cast parameters as dictionary: \(parameters)") } do { let formattedParameters = try parametersDictionary.urlEncodedString() - print("Parameters: \(formattedParameters)") + logger.log("Parameters: \(formattedParameters)") } catch let error as NSError { - print("Failed parsing Parameters: \(parametersDictionary) — \(error)") + logger.log("Failed parsing Parameters: \(parametersDictionary) — \(error)") } - print(" ") + logger.log(" ") default: break } } if let data = data, let stringData = String(data: data, encoding: .utf8) { - print("Data: \(stringData)") - print(" ") + logger.log("Data: \(stringData)") + logger.log(" ") } if let response = response as? HTTPURLResponse { - print("*** Response ***") - print(" ") + logger.log("*** Response ***") + logger.log(" ") - print("Headers: \(response.allHeaderFields)") - print(" ") + logger.log("Headers: \(response.allHeaderFields)") + logger.log(" ") - print("Status code: \(response.statusCode) — \(HTTPURLResponse.localizedString(forStatusCode: response.statusCode))") - print(" ") + logger.log("Status code: \(response.statusCode) — \(HTTPURLResponse.localizedString(forStatusCode: response.statusCode))") + logger.log(" ") } } - print("================= ~ ==================") - print(" ") + logger.log("================= ~ ==================") + logger.log(" ") + logger.flush() } func cacheOrPurgeJSON(object: Any?, path: String, cacheName: String?, cachingLevel: CachingLevel) throws { diff --git a/Sources/Networking.swift b/Sources/Networking.swift index 5daa118f..c6499f1e 100644 --- a/Sources/Networking.swift +++ b/Sources/Networking.swift @@ -111,6 +111,8 @@ open class Networking { URLSession(configuration: self.configuration) }() + public var logProvider: ErrorLoggerProvider = ConsoleLogProvider() + /// Caching options public enum CachingLevel { case memory