From a27ec284ac52c5c55728d9b24daec6bff1e4c6dc Mon Sep 17 00:00:00 2001 From: Ivan Babkin Date: Mon, 17 Dec 2018 18:00:45 +0300 Subject: [PATCH 1/7] Added ability to pass an array parameter in request --- CHANGELOG.md | 4 ++ LeadKit.podspec | 2 +- .../Alamofire/SessionManager+Extensions.swift | 58 +++++++++++++++++-- .../Structures/Api/ApiRequestParameters.swift | 25 +++++++- .../NetworkServiceConfiguration.swift | 21 +++++++ 5 files changed, 101 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ac41b0b..5260e83b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ # Changelog +### 0.9.3 +- **Add**: `SessionManager.request` method, that takes an array as a request parameter. +- **Add**: `ParametersType` enum to pass array parameter to request body. + ### 0.9.3 - **Add**: `Insert`/`Remove` section with animation functions to `TableKit`. Also make new function `Replace` that uses new `Insert`/`Remove` to animate section replace. diff --git a/LeadKit.podspec b/LeadKit.podspec index f0a18701..5aa0ebbc 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKit" - s.version = "0.9.3" + s.version = "0.9.4" s.summary = "iOS framework with a bunch of tools for rapid development" s.homepage = "https://github.com/TouchInstinct/LeadKit" s.license = "Apache License, Version 2.0" diff --git a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift index fcc1d52b..07eb3ee7 100644 --- a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift +++ b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift @@ -26,18 +26,64 @@ import RxAlamofire public extension Reactive where Base: SessionManager { + /** + Creates an observable of the `Request`. + + - parameter method: Alamofire method object + - parameter url: An object adopting `URLConvertible` + - parameter parameters: An array of dictionaries containing all necessary options + - parameter encoding: The kind of encoding used to process parameters + - parameter header: A dictionary containing all the additional headers + + - returns: An observable of the `Request` + */ + public func request(_ method: Alamofire.HTTPMethod, + _ url: URLConvertible, + parameters: [Parameters]? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: [String: String]? = nil + ) + -> Observable + { + return Observable.deferred { + var urlRequest = URLRequest(url: try url.asURL()) + urlRequest.httpMethod = method.rawValue + urlRequest.allHTTPHeaderFields = headers + urlRequest.httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) + + if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { + urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") + } + + return self.request(urlRequest: urlRequest) + } + } + /// Method which executes request with given api parameters /// /// - Parameter requestParameters: api parameters to pass Alamofire /// - Returns: Observable with request func apiRequest(requestParameters: ApiRequestParameters) -> Observable { - - return request(requestParameters.method, - requestParameters.url, - parameters: requestParameters.parameters, - encoding: requestParameters.encoding, - headers: requestParameters.headers) + + let requestObservable: Observable + + switch requestParameters.parameters { + case .dictionary(let parameters): + requestObservable = request(requestParameters.method, + requestParameters.url, + parameters: parameters, + encoding: requestParameters.encoding, + headers: requestParameters.headers) + case .array(let parameters): + requestObservable = request(requestParameters.method, + requestParameters.url, + parameters: parameters, + encoding: requestParameters.encoding, + headers: requestParameters.headers) + } + + return requestObservable .map { $0.validate(statusCode: self.base.acceptableStatusCodes) } } diff --git a/Sources/Structures/Api/ApiRequestParameters.swift b/Sources/Structures/Api/ApiRequestParameters.swift index c9ccafd0..99c0c2ca 100644 --- a/Sources/Structures/Api/ApiRequestParameters.swift +++ b/Sources/Structures/Api/ApiRequestParameters.swift @@ -22,6 +22,14 @@ import Alamofire +/** + * Enum which keeps parameters type for request body + */ +public enum ParametersType { + case dictionary(_ parameter: Parameters?) + case array(_ array: [Parameters]?) +} + /** * Struct which keeps base parameters required for api request */ @@ -29,7 +37,7 @@ public struct ApiRequestParameters { let method: HTTPMethod let url: URLConvertible - let parameters: Parameters? + let parameters: ParametersType let encoding: ParameterEncoding let headers: HTTPHeaders? @@ -41,7 +49,20 @@ public struct ApiRequestParameters { self.method = method self.url = url - self.parameters = parameters + self.parameters = .dictionary(parameters) + self.encoding = encoding + self.headers = headers + } + + public init(url: URLConvertible, + method: HTTPMethod = .get, + parameters: [Parameters]? = nil, + encoding: ParameterEncoding = URLEncoding.default, + headers: HTTPHeaders? = nil) { + + self.method = method + self.url = url + self.parameters = .array(parameters) self.encoding = encoding self.headers = headers } diff --git a/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift b/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift index 494072d6..7070f4e4 100644 --- a/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift +++ b/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift @@ -95,5 +95,26 @@ public extension NetworkServiceConfiguration { encoding: requestEncoding ?? encoding, headers: requestHeaders) } + + /// Convenient method to create ApiRequestParameters. + /// + /// - Parameters: + /// - relativeUrl: Url that will be concatenated with base url. + /// - method: HTTP method to use for request. + /// - parameters: An array of dictionaries of parameters to apply to a URLRequest. + /// - requestEncoding: Encoding type to use. If passed nil, configuration encoding will be used. + /// - requestHeaders: Dictionary of headers to apply to a URLRequest. + /// - Returns: Initialized instance of ApiRequestParameters with given parameters. + func apiRequestParameters(relativeUrl: String, + method: HTTPMethod = .get, + parameters: [Parameters]? = nil, + requestEncoding: ParameterEncoding? = nil, + requestHeaders: HTTPHeaders? = nil) -> ApiRequestParameters { + return ApiRequestParameters(url: baseUrl + relativeUrl, + method: method, + parameters: parameters, + encoding: requestEncoding ?? encoding, + headers: requestHeaders) + } } From c49fce14a21d94b07913be8ab67efaaf6e8c3c60 Mon Sep 17 00:00:00 2001 From: Ivan Babkin Date: Mon, 17 Dec 2018 18:29:23 +0300 Subject: [PATCH 2/7] Changelog fix --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5260e83b..eef804a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ # Changelog -### 0.9.3 +### 0.9.4 - **Add**: `SessionManager.request` method, that takes an array as a request parameter. - **Add**: `ParametersType` enum to pass array parameter to request body. From bd47336cfb68140c8126c4896602b5ab833f0098 Mon Sep 17 00:00:00 2001 From: Ivan Babkin Date: Tue, 18 Dec 2018 16:10:46 +0300 Subject: [PATCH 3/7] Refactoring --- .../Alamofire/SessionManager+Extensions.swift | 62 +++++++++++-------- .../Structures/Api/ApiRequestParameters.swift | 30 ++++++--- .../NetworkServiceConfiguration.swift | 2 +- 3 files changed, 56 insertions(+), 38 deletions(-) diff --git a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift index 07eb3ee7..1ac27e84 100644 --- a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift +++ b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift @@ -26,35 +26,37 @@ import RxAlamofire public extension Reactive where Base: SessionManager { - /** - Creates an observable of the `Request`. - - - parameter method: Alamofire method object - - parameter url: An object adopting `URLConvertible` - - parameter parameters: An array of dictionaries containing all necessary options - - parameter encoding: The kind of encoding used to process parameters - - parameter header: A dictionary containing all the additional headers - - - returns: An observable of the `Request` - */ + + /// Creates an observable of the `Request`. + /// + /// - Parameters: + /// - method: Alamofire method object + /// - url: An object adopting `URLConvertible` + /// - parameters: An array of dictionaries containing all necessary options + /// - encoding: The kind of encoding used to process parameters + /// - headers: A dictionary containing all the additional headers + /// - Returns: An observable of the `Request` public func request(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, - parameters: [Parameters]? = nil, - encoding: ParameterEncoding = URLEncoding.default, - headers: [String: String]? = nil - ) - -> Observable - { + parameters: [Any]? = nil, + encoding: ParameterEncoding = JSONEncoding.default, + headers: [String: String]? = nil) + -> Observable { + return Observable.deferred { - var urlRequest = URLRequest(url: try url.asURL()) - urlRequest.httpMethod = method.rawValue - urlRequest.allHTTPHeaderFields = headers - urlRequest.httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) - - if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { - urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") + let urlRequest = try URLRequest(url: try url.asURL(), method: method, headers: headers) + + if let encoding = encoding as? JSONEncoding { + let encodedUrlRequest = try encoding.encode(urlRequest, withJSONObject: parameters) + self.request(urlRequest: encodedUrlRequest) + } else { + assertionFailure("Invalid encoding type") } - + + if method == .get { + assertionFailure("Unable to pass array in get request") + } + return self.request(urlRequest: urlRequest) } } @@ -69,18 +71,24 @@ public extension Reactive where Base: SessionManager { let requestObservable: Observable switch requestParameters.parameters { - case .dictionary(let parameters): + case .dictionary(let parameters)?: requestObservable = request(requestParameters.method, requestParameters.url, parameters: parameters, encoding: requestParameters.encoding, headers: requestParameters.headers) - case .array(let parameters): + case .array(let parameters)?: requestObservable = request(requestParameters.method, requestParameters.url, parameters: parameters, encoding: requestParameters.encoding, headers: requestParameters.headers) + case .none: + requestObservable = request(requestParameters.method, + requestParameters.url, + parameters: nil as Parameters?, + encoding: requestParameters.encoding, + headers: requestParameters.headers) } return requestObservable diff --git a/Sources/Structures/Api/ApiRequestParameters.swift b/Sources/Structures/Api/ApiRequestParameters.swift index 99c0c2ca..1a065c2a 100644 --- a/Sources/Structures/Api/ApiRequestParameters.swift +++ b/Sources/Structures/Api/ApiRequestParameters.swift @@ -22,12 +22,14 @@ import Alamofire -/** - * Enum which keeps parameters type for request body - */ -public enum ParametersType { - case dictionary(_ parameter: Parameters?) - case array(_ array: [Parameters]?) + +/// Enum which keeps parameters type for request body +/// +/// - dictionary: dictionary parameter +/// - array: array parameter +public enum RequestParameters { + case dictionary(Parameters) + case array([Any]) } /** @@ -37,7 +39,7 @@ public struct ApiRequestParameters { let method: HTTPMethod let url: URLConvertible - let parameters: ParametersType + let parameters: RequestParameters? let encoding: ParameterEncoding let headers: HTTPHeaders? @@ -49,22 +51,30 @@ public struct ApiRequestParameters { self.method = method self.url = url - self.parameters = .dictionary(parameters) self.encoding = encoding self.headers = headers + if let parameters = parameters { + self.parameters = .dictionary(parameters) + } else { + self.parameters = nil + } } public init(url: URLConvertible, method: HTTPMethod = .get, - parameters: [Parameters]? = nil, + parameters: [Any]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: HTTPHeaders? = nil) { self.method = method self.url = url - self.parameters = .array(parameters) self.encoding = encoding self.headers = headers + if let parameters = parameters { + self.parameters = .array(parameters) + } else { + self.parameters = nil + } } } diff --git a/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift b/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift index 7070f4e4..77d53424 100644 --- a/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift +++ b/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift @@ -107,7 +107,7 @@ public extension NetworkServiceConfiguration { /// - Returns: Initialized instance of ApiRequestParameters with given parameters. func apiRequestParameters(relativeUrl: String, method: HTTPMethod = .get, - parameters: [Parameters]? = nil, + parameters: [Any]? = nil, requestEncoding: ParameterEncoding? = nil, requestHeaders: HTTPHeaders? = nil) -> ApiRequestParameters { return ApiRequestParameters(url: baseUrl + relativeUrl, From e01861af805470e7800e7e3d4bd4ddd5c059416b Mon Sep 17 00:00:00 2001 From: Ivan Babkin Date: Tue, 18 Dec 2018 17:21:26 +0300 Subject: [PATCH 4/7] Documentation fix --- CHANGELOG.md | 4 +++- .../Alamofire/SessionManager+Extensions.swift | 2 +- .../Structures/Api/ApiRequestParameters.swift | 19 +++++++++---------- .../NetworkServiceConfiguration.swift | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eef804a6..4677cafa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # Changelog ### 0.9.4 +- **Add**: initialization of `ApiRequestParameters`, that takes an array as a request parameter. +- **Add**: `NetworkServiceConfiguration.apiRequestParameters` method, that creates `ApiRequestParameters` with array request parameter. - **Add**: `SessionManager.request` method, that takes an array as a request parameter. -- **Add**: `ParametersType` enum to pass array parameter to request body. +- **Add**: `RequestParameters` enum to pass array parameter to request body. ### 0.9.3 - **Add**: `Insert`/`Remove` section with animation functions to `TableKit`. Also make new function `Replace` that uses new `Insert`/`Remove` to animate section replace. diff --git a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift index 1ac27e84..d098971c 100644 --- a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift +++ b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift @@ -32,7 +32,7 @@ public extension Reactive where Base: SessionManager { /// - Parameters: /// - method: Alamofire method object /// - url: An object adopting `URLConvertible` - /// - parameters: An array of dictionaries containing all necessary options + /// - parameters: An array of JSON objects containing all necessary options /// - encoding: The kind of encoding used to process parameters /// - headers: A dictionary containing all the additional headers /// - Returns: An observable of the `Request` diff --git a/Sources/Structures/Api/ApiRequestParameters.swift b/Sources/Structures/Api/ApiRequestParameters.swift index 1a065c2a..411ff4e8 100644 --- a/Sources/Structures/Api/ApiRequestParameters.swift +++ b/Sources/Structures/Api/ApiRequestParameters.swift @@ -22,21 +22,20 @@ import Alamofire - -/// Enum which keeps parameters type for request body -/// -/// - dictionary: dictionary parameter -/// - array: array parameter -public enum RequestParameters { - case dictionary(Parameters) - case array([Any]) -} - /** * Struct which keeps base parameters required for api request */ public struct ApiRequestParameters { + /// Enum which keeps parameters type for request body + /// + /// - dictionary: dictionary parameter + /// - array: array parameter + enum RequestParameters { + case dictionary(Parameters) + case array([Any]) + } + let method: HTTPMethod let url: URLConvertible let parameters: RequestParameters? diff --git a/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift b/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift index 77d53424..231dd924 100644 --- a/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift +++ b/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift @@ -101,7 +101,7 @@ public extension NetworkServiceConfiguration { /// - Parameters: /// - relativeUrl: Url that will be concatenated with base url. /// - method: HTTP method to use for request. - /// - parameters: An array of dictionaries of parameters to apply to a URLRequest. + /// - parameters: An array of JSON objects to apply to a URLRequest. /// - requestEncoding: Encoding type to use. If passed nil, configuration encoding will be used. /// - requestHeaders: Dictionary of headers to apply to a URLRequest. /// - Returns: Initialized instance of ApiRequestParameters with given parameters. From 598ae1adadd82e72ad54d1feffe713c630ffe297 Mon Sep 17 00:00:00 2001 From: Ivan Babkin Date: Wed, 19 Dec 2018 12:58:05 +0300 Subject: [PATCH 5/7] Throwing error when using invalid parameters --- .../Alamofire/SessionManager+Extensions.swift | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift index d098971c..7190913f 100644 --- a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift +++ b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift @@ -24,6 +24,17 @@ import Alamofire import RxSwift import RxAlamofire +/// Enum that represents wrong usage of requset parameters +/// +/// - getMethodForbidden: invalid usage of get method +/// - urlEncodingForbidden: invalid usage of URLEncoding +enum RequestUsageError: Error { + + case getMethodForbidden + case urlEncodingForbidden + +} + public extension Reactive where Base: SessionManager { @@ -39,25 +50,21 @@ public extension Reactive where Base: SessionManager { public func request(_ method: Alamofire.HTTPMethod, _ url: URLConvertible, parameters: [Any]? = nil, - encoding: ParameterEncoding = JSONEncoding.default, + encoding: JSONEncoding = .default, headers: [String: String]? = nil) -> Observable { return Observable.deferred { - let urlRequest = try URLRequest(url: try url.asURL(), method: method, headers: headers) - - if let encoding = encoding as? JSONEncoding { - let encodedUrlRequest = try encoding.encode(urlRequest, withJSONObject: parameters) - self.request(urlRequest: encodedUrlRequest) - } else { - assertionFailure("Invalid encoding type") - } - - if method == .get { + + guard method != .get else { assertionFailure("Unable to pass array in get request") + throw RequestUsageError.getMethodForbidden } - - return self.request(urlRequest: urlRequest) + + let urlRequest = try URLRequest(url: try url.asURL(), method: method, headers: headers) + let encodedUrlRequest = try encoding.encode(urlRequest, withJSONObject: parameters) + + return self.request(urlRequest: encodedUrlRequest) } } @@ -78,11 +85,16 @@ public extension Reactive where Base: SessionManager { encoding: requestParameters.encoding, headers: requestParameters.headers) case .array(let parameters)?: - requestObservable = request(requestParameters.method, - requestParameters.url, - parameters: parameters, - encoding: requestParameters.encoding, - headers: requestParameters.headers) + if let encoding = requestParameters.encoding as? JSONEncoding { + requestObservable = request(requestParameters.method, + requestParameters.url, + parameters: parameters, + encoding: encoding, + headers: requestParameters.headers) + } else { + assertionFailure("Invalid encoding type with array parameter") + requestObservable = .error(RequestUsageError.urlEncodingForbidden) + } case .none: requestObservable = request(requestParameters.method, requestParameters.url, From 373fee95d051f64a8495e70da35b0b8f14afdbbd Mon Sep 17 00:00:00 2001 From: Ivan Babkin Date: Wed, 19 Dec 2018 13:06:12 +0300 Subject: [PATCH 6/7] Changelog fix --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4677cafa..50f44e39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ - **Add**: initialization of `ApiRequestParameters`, that takes an array as a request parameter. - **Add**: `NetworkServiceConfiguration.apiRequestParameters` method, that creates `ApiRequestParameters` with array request parameter. - **Add**: `SessionManager.request` method, that takes an array as a request parameter. -- **Add**: `RequestParameters` enum to pass array parameter to request body. +- **Add**: `RequestUsageError` error, that represents wrong usage of requset parameters. ### 0.9.3 - **Add**: `Insert`/`Remove` section with animation functions to `TableKit`. Also make new function `Replace` that uses new `Insert`/`Remove` to animate section replace. From e1f79f330414d03dc61d2fedc0f4cb77e6ae4f9d Mon Sep 17 00:00:00 2001 From: Ivan Babkin Date: Wed, 19 Dec 2018 13:14:28 +0300 Subject: [PATCH 7/7] Using guard instead of if --- .../Alamofire/SessionManager+Extensions.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift index 7190913f..94e39703 100644 --- a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift +++ b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift @@ -85,16 +85,16 @@ public extension Reactive where Base: SessionManager { encoding: requestParameters.encoding, headers: requestParameters.headers) case .array(let parameters)?: - if let encoding = requestParameters.encoding as? JSONEncoding { - requestObservable = request(requestParameters.method, - requestParameters.url, - parameters: parameters, - encoding: encoding, - headers: requestParameters.headers) - } else { + guard let encoding = requestParameters.encoding as? JSONEncoding else { assertionFailure("Invalid encoding type with array parameter") - requestObservable = .error(RequestUsageError.urlEncodingForbidden) + return .error(RequestUsageError.urlEncodingForbidden) } + + requestObservable = request(requestParameters.method, + requestParameters.url, + parameters: parameters, + encoding: encoding, + headers: requestParameters.headers) case .none: requestObservable = request(requestParameters.method, requestParameters.url,