From 3ed9d949a4a13b09cf7cdae97166555624fe4fd2 Mon Sep 17 00:00:00 2001 From: Ivan Babkin Date: Fri, 28 Jun 2019 15:42:51 +0300 Subject: [PATCH] Add possibility to pass query items to ApiRequestParameters --- CHANGELOG.md | 4 ++ LeadKit.podspec | 2 +- Sources/Enums/LeadKitError.swift | 3 + .../Alamofire/SessionManager+Extensions.swift | 70 +++++++++++-------- .../Codable/Encodable+Extensions.swift | 17 +++++ Sources/Info-iOS.plist | 2 +- Sources/Info-tvOS.plist | 2 +- Sources/Info-watchOS.plist | 2 +- .../Structures/Api/ApiRequestParameters.swift | 5 ++ .../NetworkServiceConfiguration.swift | 6 ++ 10 files changed, 80 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0685227a..82810b01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### 0.9.24 +- **Add**: `queryItems` parameter for `ApiRequestParameters`. +- **Add**: `asQueryItems` method for `Encodable` that converts model to query items array. + ### 0.9.23 - **Add**: Rounding for `Decimal`. - **Add**: `doubleValue` property for `Decimal`. diff --git a/LeadKit.podspec b/LeadKit.podspec index ae7a0d9a..04619f0e 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKit" - s.version = "0.9.23" + s.version = "0.9.24" 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/Enums/LeadKitError.swift b/Sources/Enums/LeadKitError.swift index 90e2324f..7f917551 100644 --- a/Sources/Enums/LeadKitError.swift +++ b/Sources/Enums/LeadKitError.swift @@ -25,8 +25,11 @@ import Foundation /// Enum that represents common errors in LeadKit framework /// /// - failedToCastValue: attempt to cast was failed +/// - failedToDecode: attempt to decoding was failed +/// - failedToEncodeQueryItems: attempt to encoding to query items was failed public enum LeadKitError: Error { case failedToCastValue(expectedType: Any.Type, givenType: Any.Type) case failedToDecode(reason: String) + case failedToEncodeQueryItems } diff --git a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift index a8afc976..cc4816d8 100644 --- a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift +++ b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift @@ -32,6 +32,7 @@ enum RequestUsageError: Error { case getMethodForbidden case urlEncodingForbidden + case unableToHandleQueryParams } public extension Reactive where Base: SessionManager { @@ -72,41 +73,52 @@ public extension Reactive where Base: SessionManager { /// - requestParameters: api parameters to pass Alamofire /// - additionalValidStatusCodes: set of additional valid status codes /// - Returns: Observable with request - func apiRequest(requestParameters: ApiRequestParameters, additionalValidStatusCodes: Set) - -> Observable { + func apiRequest(requestParameters: ApiRequestParameters, additionalValidStatusCodes: Set) -> Observable { + return .deferred { + var url = try requestParameters.url.asURL() - let requestObservable: Observable + if let queryItems = requestParameters.queryItems { + guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true) else { + return .error(RequestUsageError.unableToHandleQueryParams) + } - 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)?: - guard let encoding = requestParameters.encoding as? JSONEncoding else { - assertionFailure("Invalid encoding type with array parameter") - return .error(RequestUsageError.urlEncodingForbidden) + urlComponents.queryItems = queryItems + url = try urlComponents.asURL() } - requestObservable = request(requestParameters.method, - requestParameters.url, - parameters: parameters, - encoding: encoding, - headers: requestParameters.headers) + let requestObservable: Observable - case .none: - requestObservable = request(requestParameters.method, - requestParameters.url, - parameters: nil as Parameters?, - encoding: requestParameters.encoding, - headers: requestParameters.headers) + switch requestParameters.parameters { + case .dictionary(let parameters)?: + requestObservable = self.request(requestParameters.method, + url, + parameters: parameters, + encoding: requestParameters.encoding, + headers: requestParameters.headers) + + case .array(let parameters)?: + guard let encoding = requestParameters.encoding as? JSONEncoding else { + assertionFailure("Invalid encoding type with array parameter") + return .error(RequestUsageError.urlEncodingForbidden) + } + + requestObservable = self.request(requestParameters.method, + url, + parameters: parameters, + encoding: encoding, + headers: requestParameters.headers) + + case .none: + requestObservable = self.request(requestParameters.method, + url, + parameters: nil as Parameters?, + encoding: requestParameters.encoding, + headers: requestParameters.headers) + } + + return requestObservable + .validate(statusCodes: self.base.acceptableStatusCodes.union(additionalValidStatusCodes)) } - - return requestObservable - .validate(statusCodes: self.base.acceptableStatusCodes.union(additionalValidStatusCodes)) } /// Method that executes request and serializes response into target object diff --git a/Sources/Extensions/Codable/Encodable+Extensions.swift b/Sources/Extensions/Codable/Encodable+Extensions.swift index ced45817..6d2304ed 100644 --- a/Sources/Extensions/Codable/Encodable+Extensions.swift +++ b/Sources/Extensions/Codable/Encodable+Extensions.swift @@ -21,6 +21,7 @@ // import Foundation +import Alamofire public extension Encodable { @@ -35,4 +36,20 @@ public extension Encodable { return json } + + /// Method that converts encodable model to URLQueryItems array + /// - Returns: URLQueryItems array + func asUrlQueryItems() throws -> [URLQueryItem] { + return try toJSON().map { + if let value = $1 as? Encodable, + let jsonData = try? JSONSerialization.data(withJSONObject: value.toJSON(), options: []), + let jsonString = String(data: jsonData, encoding: .utf8) { + return URLQueryItem(name: $0, value: jsonString) + } else if let value = $1 as? CustomStringConvertible { + return URLQueryItem(name: $0, value: "\(value)") + } else { + throw LeadKitError.failedToEncodeQueryItems + } + } + } } diff --git a/Sources/Info-iOS.plist b/Sources/Info-iOS.plist index 4b8f7be2..c1c8f16b 100644 --- a/Sources/Info-iOS.plist +++ b/Sources/Info-iOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.9.23 + 0.9.24 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/Sources/Info-tvOS.plist b/Sources/Info-tvOS.plist index 4b8f7be2..c1c8f16b 100644 --- a/Sources/Info-tvOS.plist +++ b/Sources/Info-tvOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.9.23 + 0.9.24 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/Sources/Info-watchOS.plist b/Sources/Info-watchOS.plist index 4b8f7be2..c1c8f16b 100644 --- a/Sources/Info-watchOS.plist +++ b/Sources/Info-watchOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.9.23 + 0.9.24 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/Sources/Structures/Api/ApiRequestParameters.swift b/Sources/Structures/Api/ApiRequestParameters.swift index 5da64760..effced3d 100644 --- a/Sources/Structures/Api/ApiRequestParameters.swift +++ b/Sources/Structures/Api/ApiRequestParameters.swift @@ -39,17 +39,20 @@ public struct ApiRequestParameters { let method: HTTPMethod let url: URLConvertible let parameters: RequestParameters? + let queryItems: [URLQueryItem]? let encoding: ParameterEncoding let headers: HTTPHeaders? public init(url: URLConvertible, method: HTTPMethod = .get, parameters: Parameters? = nil, + queryItems: [URLQueryItem]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: HTTPHeaders? = nil) { self.method = method self.url = url + self.queryItems = queryItems self.encoding = encoding self.headers = headers if let parameters = parameters { @@ -62,11 +65,13 @@ public struct ApiRequestParameters { public init(url: URLConvertible, method: HTTPMethod = .get, parameters: [Any]? = nil, + queryItems: [URLQueryItem]? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: HTTPHeaders? = nil) { self.method = method self.url = url + self.queryItems = queryItems self.encoding = encoding self.headers = headers if let parameters = parameters { diff --git a/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift b/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift index afcc36fc..50c68e75 100644 --- a/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift +++ b/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift @@ -81,17 +81,20 @@ public extension NetworkServiceConfiguration { /// - relativeUrl: Url that will be concatenated with base url. /// - method: HTTP method to use for request. /// - parameters: Dictionary of parameters to apply to a URLRequest. + /// - queryItems: An array of query items to configure URL with them. /// - 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, + queryItems: [URLQueryItem]? = nil, requestEncoding: ParameterEncoding? = nil, requestHeaders: HTTPHeaders? = nil) -> ApiRequestParameters { return ApiRequestParameters(url: baseUrl + relativeUrl, method: method, parameters: parameters, + queryItems: queryItems, encoding: requestEncoding ?? encoding, headers: requestHeaders) } @@ -102,17 +105,20 @@ public extension NetworkServiceConfiguration { /// - relativeUrl: Url that will be concatenated with base url. /// - method: HTTP method to use for request. /// - parameters: An array of JSON objects to apply to a URLRequest. + /// - queryItems: An array of query items to configure URL with them. /// - 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: [Any]? = nil, + queryItems: [URLQueryItem]? = nil, requestEncoding: ParameterEncoding? = nil, requestHeaders: HTTPHeaders? = nil) -> ApiRequestParameters { return ApiRequestParameters(url: baseUrl + relativeUrl, method: method, parameters: parameters, + queryItems: queryItems, encoding: requestEncoding ?? encoding, headers: requestHeaders) }