From 9828284c207b417429edb3d23c3a1c7645fa00e2 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Mon, 27 Jun 2022 18:13:57 +0300 Subject: [PATCH] feat: Asynchronous request preprocessing --- CHANGELOG.md | 4 + LeadKit.podspec | 2 +- TIAppleMapUtils/TIAppleMapUtils.podspec | 2 +- TIAuth/TIAuth.podspec | 2 +- .../Cancellables/Sources/Cancellables.swift | 17 +++ TIFoundationUtils/TIFoundationUtils.podspec | 2 +- TIGoogleMapUtils/TIGoogleMapUtils.podspec | 2 +- TIKeychainUtils/TIKeychainUtils.podspec | 2 +- TIMapUtils/TIMapUtils.podspec | 2 +- .../DefaultJsonNetworkService.swift | 68 ++++++--- TIMoyaNetworking/TIMoyaNetworking.podspec | 2 +- .../Sources/ApiInteractor/ApiInteractor.swift | 22 +-- .../DefaultTokenInterceptor.swift | 12 +- .../Interceptors/EndpointRequestRetrier.swift | 15 +- .../EndpointResponseTokenInterceptor.swift | 2 +- ...tEndpointSecurityRequestPreprocessor.swift | 67 +++++---- ...ltSecuritySchemesRequestPreprocessor.swift | 138 ++++++++++++++---- .../EndpointRequestPreprocessor.swift | 16 +- .../EndpointSecurityRequestPreprocessor.swift | 17 ++- TINetworking/TINetworking.podspec | 2 +- TINetworkingCache/TINetworkingCache.podspec | 2 +- TIPagination/TIPagination.podspec | 2 +- TISwiftUICore/TISwiftUICore.podspec | 2 +- TISwiftUtils/TISwiftUtils.podspec | 2 +- TITableKitUtils/TITableKitUtils.podspec | 2 +- TITransitions/TITransitions.podspec | 2 +- TIUIElements/TIUIElements.podspec | 2 +- TIUIKitCore/TIUIKitCore.podspec | 2 +- TIYandexMapUtils/TIYandexMapUtils.podspec | 2 +- 29 files changed, 283 insertions(+), 131 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbcffada..86e1929f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### 1.22.0 + +- **Update**: Asynchronous request preprocessing + ### 1.21.0 - **Update**: `AsyncEventHandler` was replaced with `EndpointRequestRetrier` diff --git a/LeadKit.podspec b/LeadKit.podspec index e83503e0..4d42224b 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKit" - s.version = "1.21.0" + s.version = "1.22.0" 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/TIAppleMapUtils/TIAppleMapUtils.podspec b/TIAppleMapUtils/TIAppleMapUtils.podspec index 0e1baab4..59ba157b 100644 --- a/TIAppleMapUtils/TIAppleMapUtils.podspec +++ b/TIAppleMapUtils/TIAppleMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIAppleMapUtils' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Set of helpers for map objects clustering and interacting using Apple MapKit.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIAuth/TIAuth.podspec b/TIAuth/TIAuth.podspec index 42f082b1..3266d6e5 100644 --- a/TIAuth/TIAuth.podspec +++ b/TIAuth/TIAuth.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIAuth' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Login, registration, confirmation and other related actions' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIFoundationUtils/Cancellables/Sources/Cancellables.swift b/TIFoundationUtils/Cancellables/Sources/Cancellables.swift index 7d805493..cf662966 100644 --- a/TIFoundationUtils/Cancellables/Sources/Cancellables.swift +++ b/TIFoundationUtils/Cancellables/Sources/Cancellables.swift @@ -29,3 +29,20 @@ public struct Cancellables { ScopeCancellable(scopeCancellableClosure: scopeCancellableClosure) } } + +@available(iOS 13.0.0, *) +public func withTaskCancellableClosure(closure: (@escaping (T) -> Void) -> Cancellable) async -> T { + let cancellableBag = BaseCancellableBag() + + return await withTaskCancellationHandler(handler: { + cancellableBag.cancel() + }, operation: { + await withCheckedContinuation { continuation in + closure { + continuation.resume(returning: $0) + } + .add(to: cancellableBag) + } + }) +} + diff --git a/TIFoundationUtils/TIFoundationUtils.podspec b/TIFoundationUtils/TIFoundationUtils.podspec index 0ad14462..07b02a63 100644 --- a/TIFoundationUtils/TIFoundationUtils.podspec +++ b/TIFoundationUtils/TIFoundationUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIFoundationUtils' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Set of helpers for Foundation framework classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIGoogleMapUtils/TIGoogleMapUtils.podspec b/TIGoogleMapUtils/TIGoogleMapUtils.podspec index 7c37ceed..ea5456a5 100644 --- a/TIGoogleMapUtils/TIGoogleMapUtils.podspec +++ b/TIGoogleMapUtils/TIGoogleMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIGoogleMapUtils' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Set of helpers for map objects clustering and interacting using Google Maps SDK.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIKeychainUtils/TIKeychainUtils.podspec b/TIKeychainUtils/TIKeychainUtils.podspec index cd3afa39..d099e240 100644 --- a/TIKeychainUtils/TIKeychainUtils.podspec +++ b/TIKeychainUtils/TIKeychainUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIKeychainUtils' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Set of helpers for Keychain classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIMapUtils/TIMapUtils.podspec b/TIMapUtils/TIMapUtils.podspec index b9fefb90..ef157380 100644 --- a/TIMapUtils/TIMapUtils.podspec +++ b/TIMapUtils/TIMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIMapUtils' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Set of helpers for map objects clustering and interacting.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIMoyaNetworking/Sources/NetworkService/DefaultJsonNetworkService.swift b/TIMoyaNetworking/Sources/NetworkService/DefaultJsonNetworkService.swift index 4c45f1be..f35136ab 100644 --- a/TIMoyaNetworking/Sources/NetworkService/DefaultJsonNetworkService.swift +++ b/TIMoyaNetworking/Sources/NetworkService/DefaultJsonNetworkService.swift @@ -75,33 +75,40 @@ open class DefaultJsonNetworkService: ApiInteractor { completion: @escaping ParameterClosure) -> TIFoundationUtils.Cancellable { ScopeCancellable { [serializationQueue, callbackQueue, preprocessors] scope in - let workItem = DispatchWorkItem { - guard !scope.isCancelled else { - return - } + Self.preprocess(request: request, + preprocessors: preprocessors) { + switch $0 { + case let .success(preprocessedRequest): + let workItem = DispatchWorkItem { + guard !scope.isCancelled else { + return + } - do { - let preprocessedRequest = try preprocessors.reduce(request) { - try $1.preprocess(request: $0) + do { + let serializedRequest = try self.serialize(request: preprocessedRequest) + + self.process(request: serializedRequest, + mapSuccess: mapSuccess, + mapFailure: mapFailure, + mapNetworkError: mapNetworkError, + completion: completion) + .add(to: scope) + } catch { + callbackQueue.async { + completion(mapNetworkError(.encodableMapping(error))) + } + } } - let serializedRequest = try self.serialize(request: preprocessedRequest) + workItem.add(to: scope) - scope.add(cancellable: self.process(request: serializedRequest, - mapSuccess: mapSuccess, - mapFailure: mapFailure, - mapNetworkError: mapNetworkError, - completion: completion)) - } catch { + serializationQueue.async(execute: workItem) + case let .failure(error): callbackQueue.async { completion(mapNetworkError(.encodableMapping(error))) } } } - - serializationQueue.async(execute: workItem) - - return workItem } } @@ -181,4 +188,29 @@ open class DefaultJsonNetworkService: ApiInteractor { preprocessors.append(securityPreprocessor) } + + private static func preprocess(request: EndpointRequest, + preprocessors: P, + completion: @escaping (Result, Error>) -> Void) -> TIFoundationUtils.Cancellable + where P.Element == EndpointRequestPreprocessor { + + guard let preprocessor = preprocessors.first else { + completion(.success(request)) + return Cancellables.nonCancellable() + } + + return ScopeCancellable { scope in + preprocessor.preprocess(request: request) { + switch $0 { + case let .success(modifiedRequest): + preprocess(request: modifiedRequest, + preprocessors: preprocessors.dropFirst(), + completion: completion) + .add(to: scope) + case let .failure(error): + completion(.failure(error)) + } + } + } + } } diff --git a/TIMoyaNetworking/TIMoyaNetworking.podspec b/TIMoyaNetworking/TIMoyaNetworking.podspec index 78b60e31..d4061461 100644 --- a/TIMoyaNetworking/TIMoyaNetworking.podspec +++ b/TIMoyaNetworking/TIMoyaNetworking.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIMoyaNetworking' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Moya + Swagger network service.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TINetworking/Sources/ApiInteractor/ApiInteractor.swift b/TINetworking/Sources/ApiInteractor/ApiInteractor.swift index c0874d3a..453b3d0d 100644 --- a/TINetworking/Sources/ApiInteractor/ApiInteractor.swift +++ b/TINetworking/Sources/ApiInteractor/ApiInteractor.swift @@ -49,21 +49,13 @@ public extension ApiInteractor { mapFailure: @escaping Closure, mapNetworkError: @escaping Closure) async -> R { - let cancellableBag = BaseCancellableBag() - - return await withTaskCancellationHandler(handler: { - cancellableBag.cancel() - }, operation: { - await withCheckedContinuation { continuation in - process(request: request, - mapSuccess: mapSuccess, - mapFailure: mapFailure, - mapNetworkError: mapNetworkError) { - - continuation.resume(returning: $0) - } - .add(to: cancellableBag) + await withTaskCancellableClosure { completion in + process(request: request, + mapSuccess: mapSuccess, + mapFailure: mapFailure, + mapNetworkError: mapNetworkError) { + completion($0) } - }) + } } } diff --git a/TINetworking/Sources/Interceptors/DefaultTokenInterceptor.swift b/TINetworking/Sources/Interceptors/DefaultTokenInterceptor.swift index ddf6b3a5..3cc8f0bc 100644 --- a/TINetworking/Sources/Interceptors/DefaultTokenInterceptor.swift +++ b/TINetworking/Sources/Interceptors/DefaultTokenInterceptor.swift @@ -25,24 +25,24 @@ import Foundation import TIFoundationUtils open class DefaultTokenInterceptor: RequestInterceptor { - public typealias IsTokenInvalidClosure = (HTTPURLResponse?, Error?) -> Bool + public typealias ShouldRefreshTokenClosure = (URLRequest?, HTTPURLResponse?, Error?) -> Bool public typealias RefreshTokenClosure = (@escaping (RefreshError?) -> Void) -> Cancellable public typealias RequestModificationClosure = (URLRequest) -> URLRequest let refreshLock = NSLock() - let isTokenInvalidClosure: IsTokenInvalidClosure + let shouldRefreshToken: ShouldRefreshTokenClosure let refreshTokenClosure: RefreshTokenClosure public var defaultRetryStrategy: RetryResult = .doNotRetry public var requestModificationClosure: RequestModificationClosure? - public init(isTokenInvalidClosure: @escaping IsTokenInvalidClosure, + public init(isTokenInvalidClosure: @escaping ShouldRefreshTokenClosure, refreshTokenClosure: @escaping RefreshTokenClosure, requestModificationClosure: RequestModificationClosure? = nil) { - self.isTokenInvalidClosure = isTokenInvalidClosure + self.shouldRefreshToken = isTokenInvalidClosure self.refreshTokenClosure = refreshTokenClosure self.requestModificationClosure = requestModificationClosure } @@ -62,7 +62,7 @@ open class DefaultTokenInterceptor: RequestInterceptor { let modifiedRequest = requestModificationClosure?(urlRequest) ?? urlRequest - validateAndRepair(validationClosure: { true }, + validateAndRepair(validationClosure: { shouldRefreshToken(urlRequest, nil, nil) }, completion: adaptCompletion, defaultCompletionResult: modifiedRequest, recoveredCompletionResult: modifiedRequest) @@ -88,7 +88,7 @@ open class DefaultTokenInterceptor: RequestInterceptor { } } - validateAndRepair(validationClosure: { isTokenInvalidClosure(request.response, error) }, + validateAndRepair(validationClosure: { shouldRefreshToken(request.request, request.response, error) }, completion: retryCompletion, defaultCompletionResult: defaultRetryStrategy, recoveredCompletionResult: .retry) diff --git a/TINetworking/Sources/Interceptors/EndpointRequestRetrier.swift b/TINetworking/Sources/Interceptors/EndpointRequestRetrier.swift index 89d1a06a..7c089f54 100644 --- a/TINetworking/Sources/Interceptors/EndpointRequestRetrier.swift +++ b/TINetworking/Sources/Interceptors/EndpointRequestRetrier.swift @@ -35,17 +35,10 @@ public protocol EndpointRequestRetrier { @available(iOS 13.0.0, *) public extension EndpointRequestRetrier { func validateAndRepair(errorResults: [ErrorResult]) async -> EndpointRetryResult { - let cancellableBag = BaseCancellableBag() - - return await withTaskCancellationHandler(handler: { - cancellableBag.cancel() - }, operation: { - await withCheckedContinuation { continuation in - validateAndRepair(errorResults: errorResults) { - continuation.resume(returning: $0) - } - .add(to: cancellableBag) + await withTaskCancellableClosure { completion in + validateAndRepair(errorResults: errorResults) { + completion($0) } - }) + } } } diff --git a/TINetworking/Sources/Interceptors/EndpointResponseTokenInterceptor.swift b/TINetworking/Sources/Interceptors/EndpointResponseTokenInterceptor.swift index b3897bb7..41434978 100644 --- a/TINetworking/Sources/Interceptors/EndpointResponseTokenInterceptor.swift +++ b/TINetworking/Sources/Interceptors/EndpointResponseTokenInterceptor.swift @@ -29,7 +29,7 @@ open class EndpointResponseTokenInterceptor: DefaultTokenInterceptor String? + public typealias ValueProvider = (@escaping (String?) -> Void) -> Cancellable private let valueProvider: ValueProvider @@ -32,43 +34,52 @@ open class DefaultSecuritySchemePreprocessor: SecuritySchemePreprocessor { } public init(staticValue: String?) { - self.valueProvider = { staticValue } + self.valueProvider = { + $0(staticValue) + return Cancellables.nonCancellable() + } } // MARK: - EndpointSecurityRequestPreprocessor - public func preprocess(request: EndpointRequest, using security: SecurityScheme) throws -> EndpointRequest { + public func preprocess(request: EndpointRequest, + using security: SecurityScheme, + completion: @escaping (Result, Error>) -> Void) -> Cancellable { + var modifiedRequest = request - guard let value = valueProvider() else { - throw ValueNotProvidedError() - } + return valueProvider { + guard let value = $0 else { + completion(.failure(ValueNotProvidedError())) + return + } - switch security { - case let .http(authenticationScheme): - let headerValue = "\(authenticationScheme.rawValue) \(value)" - var headerParameters = modifiedRequest.headerParameters ?? [:] - headerParameters.updateValue(.init(value: headerValue), - forKey: "Authorization") - - modifiedRequest.headerParameters = headerParameters - case let .apiKey(parameterLocation, parameterName): - switch parameterLocation { - case .header: + switch security { + case let .http(authenticationScheme): + let headerValue = "\(authenticationScheme.rawValue) \(value)" var headerParameters = modifiedRequest.headerParameters ?? [:] - headerParameters.updateValue(.init(value: value), - forKey: parameterName) + headerParameters.updateValue(.init(value: headerValue), + forKey: "Authorization") modifiedRequest.headerParameters = headerParameters - case .query: - modifiedRequest.queryParameters.updateValue(.init(value: value), - forKey: parameterName) - case .cookie: - modifiedRequest.cookieParameters.updateValue(.init(value: value), - forKey: parameterName) - } - } + case let .apiKey(parameterLocation, parameterName): + switch parameterLocation { + case .header: + var headerParameters = modifiedRequest.headerParameters ?? [:] + headerParameters.updateValue(.init(value: value), + forKey: parameterName) - return modifiedRequest + modifiedRequest.headerParameters = headerParameters + case .query: + modifiedRequest.queryParameters.updateValue(.init(value: value), + forKey: parameterName) + case .cookie: + modifiedRequest.cookieParameters.updateValue(.init(value: value), + forKey: parameterName) + } + } + + completion(.success(modifiedRequest)) + } } } diff --git a/TINetworking/Sources/RequestPreprocessors/DefaultSecuritySchemesRequestPreprocessor.swift b/TINetworking/Sources/RequestPreprocessors/DefaultSecuritySchemesRequestPreprocessor.swift index 6e40adb1..f1a0b995 100644 --- a/TINetworking/Sources/RequestPreprocessors/DefaultSecuritySchemesRequestPreprocessor.swift +++ b/TINetworking/Sources/RequestPreprocessors/DefaultSecuritySchemesRequestPreprocessor.swift @@ -20,6 +20,8 @@ // THE SOFTWARE. // +import TIFoundationUtils + open class DefaultEndpointSecurityPreprocessor: EndpointRequestPreprocessor { enum PreprocessError: Error { case missingSecurityScheme(String, [String: SecurityScheme]) @@ -36,46 +38,120 @@ open class DefaultEndpointSecurityPreprocessor: EndpointRequestPreprocessor { self.schemePreprocessors = schemePreprocessors } - public func preprocess(request: EndpointRequest) throws -> EndpointRequest { + public func preprocess(request: EndpointRequest, + completion: @escaping (Result, Error>) -> Void) -> Cancellable { + guard !request.security.compactMap({ $0 }).isEmpty else { - return request + completion(.success(request)) + return Cancellables.nonCancellable() } - let endpointSchemes: [[KeyValueTuple]] = try request.security.map { - try $0.map { - guard let securityScheme = openApi.security[$0] else { - throw PreprocessError.missingSecurityScheme($0, openApi.security) - } + do { + let endpointSchemes: [[KeyValueTuple]] = try request.security.map { + try $0.map { + guard let securityScheme = openApi.security[$0] else { + throw PreprocessError.missingSecurityScheme($0, openApi.security) + } - return ($0, securityScheme) + return ($0, securityScheme) + } } + + return Self.preprocess(request: request, + using: endpointSchemes, + schemePreprocessors: schemePreprocessors) { [schemePreprocessors] in + switch $0 { + case let .success(modifiedRequest): + completion(.success(modifiedRequest)) + case let .failure(error): + completion(.failure(PreprocessError.unableToSatisfyRequirements(anyOfRequired: request.security, + registeredPreprocessors: schemePreprocessors))) + } + } + } catch { + completion(.failure(error)) + return Cancellables.nonCancellable() } - - for schemeGroup in endpointSchemes { - let preprocessorsGroup: [KeyValueTuple] = schemeGroup.compactMap { - guard let preprocessor = schemePreprocessors[$0.key] else { - return nil - } - - return ($0.value, preprocessor) - } - - guard preprocessorsGroup.count == schemeGroup.count else { - continue // unable to satisfy group requirement - } - - do { - return try preprocessorsGroup.reduce(request) { - try $1.value.preprocess(request: $0, using: $1.key) - } - } - } - - throw PreprocessError.unableToSatisfyRequirements(anyOfRequired: request.security, - registeredPreprocessors: schemePreprocessors) } public func register(preprocessor: SecuritySchemePreprocessor, for scheme: String) { schemePreprocessors[scheme] = preprocessor } + + private static func preprocess(request: EndpointRequest, + using schemes: SC, + schemePreprocessors: [String: SecuritySchemePreprocessor], + completion: @escaping (Result, Error>) -> Void) -> Cancellable + where SC.Element == [KeyValueTuple] { + + guard let schemeGroup = schemes.first else { + completion(.success(request)) + return Cancellables.nonCancellable() + } + + let preprocessorsGroup: [KeyValueTuple] = schemeGroup.compactMap { + guard let preprocessor = schemePreprocessors[$0.key] else { + return nil + } + + return ($0.value, preprocessor) + } + + guard preprocessorsGroup.count == schemeGroup.count else { + // unable to satisfy group requirement + // try next scheme group + return preprocess(request: request, + using: schemes.dropFirst(), + schemePreprocessors: schemePreprocessors, + completion: completion) + } + + return ScopeCancellable { scope in + preprocess(request: request, + with: preprocessorsGroup) { + + switch $0 { + case let .success(modifiedRequest): + completion(.success(modifiedRequest)) + case let .failure(error): + guard !schemes.isEmpty else { + completion(.failure(error)) + return + } + + preprocess(request: request, + using: schemes.dropFirst(), + schemePreprocessors: schemePreprocessors, + completion: completion) + .add(to: scope) + } + } + } + } + + private static func preprocess(request: EndpointRequest, + with groups: G, + completion: @escaping (Result, Error>) -> Void) -> Cancellable + where G.Element == KeyValueTuple { + + guard let group = groups.first else { + completion(.success(request)) + return Cancellables.nonCancellable() + } + + return ScopeCancellable { scope in + group.value.preprocess(request: request, + using: group.key) { + switch $0 { + case let .success(modifiedRequest): + preprocess(request: modifiedRequest, + with: groups.dropFirst(), + completion: completion) + .add(to: scope) + case let .failure(error): + completion(.failure(error)) + } + } + } + } } diff --git a/TINetworking/Sources/RequestPreprocessors/EndpointRequestPreprocessor.swift b/TINetworking/Sources/RequestPreprocessors/EndpointRequestPreprocessor.swift index 2a1a3764..ed1c7d16 100644 --- a/TINetworking/Sources/RequestPreprocessors/EndpointRequestPreprocessor.swift +++ b/TINetworking/Sources/RequestPreprocessors/EndpointRequestPreprocessor.swift @@ -20,6 +20,20 @@ // THE SOFTWARE. // +import TIFoundationUtils + public protocol EndpointRequestPreprocessor { - func preprocess(request: EndpointRequest) throws -> EndpointRequest + func preprocess(request: EndpointRequest, + completion: @escaping (Result, Error>) -> Void) -> Cancellable +} + +@available(iOS 13.0.0, *) +public extension EndpointRequestPreprocessor { + func preprocess(request: EndpointRequest) async -> Result, Error> { + await withTaskCancellableClosure { completion in + preprocess(request: request) { + completion($0) + } + } + } } diff --git a/TINetworking/Sources/RequestPreprocessors/EndpointSecurityRequestPreprocessor.swift b/TINetworking/Sources/RequestPreprocessors/EndpointSecurityRequestPreprocessor.swift index 3ba79ad1..8dcf2e56 100644 --- a/TINetworking/Sources/RequestPreprocessors/EndpointSecurityRequestPreprocessor.swift +++ b/TINetworking/Sources/RequestPreprocessors/EndpointSecurityRequestPreprocessor.swift @@ -20,8 +20,21 @@ // THE SOFTWARE. // +import TIFoundationUtils + public protocol SecuritySchemePreprocessor { - func preprocess(request: EndpointRequest, using security: SecurityScheme) throws -> EndpointRequest + func preprocess(request: EndpointRequest, + using security: SecurityScheme, + completion: @escaping (Result, Error>) -> Void) -> Cancellable } - +@available(iOS 13.0.0, *) +public extension SecuritySchemePreprocessor { + func preprocess(request: EndpointRequest, using security: SecurityScheme) async -> Result, Error> { + await withTaskCancellableClosure { completion in + preprocess(request: request, using: security) { + completion($0) + } + } + } +} diff --git a/TINetworking/TINetworking.podspec b/TINetworking/TINetworking.podspec index abe56038..1749684d 100644 --- a/TINetworking/TINetworking.podspec +++ b/TINetworking/TINetworking.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TINetworking' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Swagger-frendly networking layer helpers.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TINetworkingCache/TINetworkingCache.podspec b/TINetworkingCache/TINetworkingCache.podspec index 8f87a5f7..fa086f5a 100644 --- a/TINetworkingCache/TINetworkingCache.podspec +++ b/TINetworkingCache/TINetworkingCache.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TINetworkingCache' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Caching results of EndpointRequests.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIPagination/TIPagination.podspec b/TIPagination/TIPagination.podspec index 6c4f81cd..be2e9143 100644 --- a/TIPagination/TIPagination.podspec +++ b/TIPagination/TIPagination.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIPagination' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Generic pagination component.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TISwiftUICore/TISwiftUICore.podspec b/TISwiftUICore/TISwiftUICore.podspec index b67348a0..7c8dde9f 100644 --- a/TISwiftUICore/TISwiftUICore.podspec +++ b/TISwiftUICore/TISwiftUICore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TISwiftUICore' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Core UI elements: protocols, views and helpers..' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TISwiftUtils/TISwiftUtils.podspec b/TISwiftUtils/TISwiftUtils.podspec index d0812a96..f22e5a1e 100644 --- a/TISwiftUtils/TISwiftUtils.podspec +++ b/TISwiftUtils/TISwiftUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TISwiftUtils' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Bunch of useful helpers for Swift development.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TITableKitUtils/TITableKitUtils.podspec b/TITableKitUtils/TITableKitUtils.podspec index 438966cb..d808aba8 100644 --- a/TITableKitUtils/TITableKitUtils.podspec +++ b/TITableKitUtils/TITableKitUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITableKitUtils' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Set of helpers for TableKit classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TITransitions/TITransitions.podspec b/TITransitions/TITransitions.podspec index f054a296..e1dcf910 100644 --- a/TITransitions/TITransitions.podspec +++ b/TITransitions/TITransitions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITransitions' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Set of custom transitions to present controller. ' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIUIElements/TIUIElements.podspec b/TIUIElements/TIUIElements.podspec index 00576842..93d0709f 100644 --- a/TIUIElements/TIUIElements.podspec +++ b/TIUIElements/TIUIElements.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIElements' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Bunch of useful protocols and views.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIUIKitCore/TIUIKitCore.podspec b/TIUIKitCore/TIUIKitCore.podspec index 9f979669..32675f8d 100644 --- a/TIUIKitCore/TIUIKitCore.podspec +++ b/TIUIKitCore/TIUIKitCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIKitCore' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Core UI elements: protocols, views and helpers.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIYandexMapUtils/TIYandexMapUtils.podspec b/TIYandexMapUtils/TIYandexMapUtils.podspec index b12086f8..851f7167 100644 --- a/TIYandexMapUtils/TIYandexMapUtils.podspec +++ b/TIYandexMapUtils/TIYandexMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIYandexMapUtils' - s.version = '1.21.0' + s.version = '1.22.0' s.summary = 'Set of helpers for map objects clustering and interacting using Yandex Maps SDK.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' }