feature/http_status_codes_for_error_responses #5
|
|
@ -1,5 +1,10 @@
|
|||
# Changelog
|
||||
|
||||
### 1.44.0
|
||||
|
||||
- **Added**: HTTP status codes to `EndpointErrorResult.apiError` responses
|
||||
- **Added**: SwiftLint pre-build SPM step to TINetworking module
|
||||
|
||||
### 1.43.1
|
||||
|
||||
- **Fixed**: build scripts submodule url
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
// swift-tools-version:5.7
|
||||
|
||||
#if canImport(PackageDescription)
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
|
|
@ -71,7 +74,12 @@ let package = Package(
|
|||
.target(name: "TIDeveloperUtils", dependencies: ["TISwiftUtils", "TIUIKitCore", "TIUIElements"], path: "TIDeveloperUtils/Sources"),
|
||||
|
||||
// MARK: - Networking
|
||||
.target(name: "TINetworking", dependencies: ["TIFoundationUtils", "Alamofire"], path: "TINetworking/Sources"),
|
||||
|
||||
.target(name: "TINetworking",
|
||||
dependencies: ["TIFoundationUtils", "Alamofire"],
|
||||
path: "TINetworking/Sources",
|
||||
plugins: [.plugin(name: "TISwiftLintPlugin")]),
|
||||
|
||||
.target(name: "TIMoyaNetworking", dependencies: ["TINetworking", "TIFoundationUtils", "Moya"], path: "TIMoyaNetworking"),
|
||||
.target(name: "TINetworkingCache", dependencies: ["TIFoundationUtils", "TINetworking", "Cache"], path: "TINetworkingCache/Sources"),
|
||||
|
||||
|
|
@ -89,7 +97,15 @@ let package = Package(
|
|||
dependencies: [.product(name: "Antlr4", package: "antlr4")],
|
||||
path: "TITextProcessing/Sources",
|
||||
exclude: ["TITextProcessing.app"]),
|
||||
|
||||
|
||||
.binaryTarget(name: "SwiftLintBinary",
|
||||
url: "https://github.com/realm/SwiftLint/releases/download/0.52.2/SwiftLintBinary-macos.artifactbundle.zip",
|
||||
checksum: "89651e1c87fb62faf076ef785a5b1af7f43570b2b74c6773526e0d5114e0578e"),
|
||||
|
||||
.plugin(name: "TISwiftLintPlugin",
|
||||
capability: .buildTool(),
|
||||
dependencies: ["SwiftLintBinary"]),
|
||||
|
||||
// MARK: - Tests
|
||||
|
||||
.testTarget(
|
||||
|
|
@ -102,3 +118,5 @@ let package = Package(
|
|||
path: "Tests/TITextProcessingTests")
|
||||
]
|
||||
)
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import PackagePlugin
|
||||
|
||||
@main
|
||||
struct SwiftLintPlugin: BuildToolPlugin {
|
||||
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
|
||||
let swiftlintScriptPath = context.package.directory.appending(["build-scripts", "xcode", "build_phases", "swiftlint.sh"])
|
||||
|
||||
let swiftlintExecutablePath = try context.tool(named: "swiftlint").path
|
||||
|
||||
return [
|
||||
.prebuildCommand(displayName: "SwiftLint linting...",
|
||||
executable: swiftlintScriptPath,
|
||||
arguments: [
|
||||
swiftlintExecutablePath,
|
||||
context.package.directory.appending(subpath: "swiftlint_base.yml")
|
||||
],
|
||||
environment: [
|
||||
"SCRIPT_DIR": swiftlintScriptPath.removingLastComponent().string,
|
||||
"SRCROOT": context.package.directory.string,
|
||||
"SCRIPT_INPUT_FILE_COUNT": "1",
|
||||
"SCRIPT_INPUT_FILE_0": target.directory.removingLastComponent().lastComponent,
|
||||
// "FORCE_LINT": "1", // Lint all files in target (not only modified)
|
||||
// "AUTOCORRECT": "1"
|
||||
|
nikita.semenov marked this conversation as resolved
Outdated
|
||||
],
|
||||
outputFilesDirectory: context.package.directory)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIAppleMapUtils'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Apple MapKit.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIAuth'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Login, registration, confirmation and other related actions'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '13.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIDeeplink'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Deeplink service API'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -9,7 +9,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
sources = '/Sources/**/*'
|
||||
if ENV["DEVELOPMENT_INSTALL"] # installing using :path =>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIDeveloperUtils'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Universal web view API'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -9,7 +9,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIEcommerce'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Cart, products, promocodes, bonuses and other related actions'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -40,8 +40,10 @@ public extension Operation {
|
|||
leafDependency.addDependency(startOperation)
|
||||
}
|
||||
}
|
||||
|
||||
func add(to operationQueue: OperationQueue, waitUntilFinished: Bool = false) {
|
||||
|
||||
@discardableResult
|
||||
func add(to operationQueue: OperationQueue, waitUntilFinished: Bool = false) -> Self {
|
||||
operationQueue.addOperations(flattenDependencies + [self], waitUntilFinished: waitUntilFinished)
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
open class BaseCancellable: Cancellable {
|
||||
private(set) public var isCancelled = false
|
||||
|
||||
public init() {}
|
||||
|
||||
open func cancel() {
|
||||
isCancelled = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public struct Cancellables {
|
||||
public enum Cancellables {
|
||||
public static func nonCancellable() -> Cancellable {
|
||||
NonCancellable()
|
||||
}
|
||||
|
|
@ -28,21 +28,27 @@ public struct Cancellables {
|
|||
public static func scoped(scopeCancellableClosure: ScopeCancellable.ScopeCancellableClosure) -> Cancellable {
|
||||
ScopeCancellable(scopeCancellableClosure: scopeCancellableClosure)
|
||||
}
|
||||
|
||||
public static func weakTargetClosure<T: AnyObject>(target: T?,
|
||||
cancelClosure: @escaping WeakTargetCancellable<T>.CancelClosure) -> Cancellable {
|
||||
|
||||
WeakTargetCancellable(target: target, cancelClosure: cancelClosure)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 13.0.0, *)
|
||||
public func withTaskCancellableClosure<T>(closure: (@escaping (T) -> Void) -> Cancellable) async -> T {
|
||||
let cancellableBag = BaseCancellableBag()
|
||||
|
||||
return await withTaskCancellationHandler(handler: {
|
||||
cancellableBag.cancel()
|
||||
}, operation: {
|
||||
return await withTaskCancellationHandler(operation: {
|
||||
await withCheckedContinuation { continuation in
|
||||
closure {
|
||||
continuation.resume(returning: $0)
|
||||
}
|
||||
.add(to: cancellableBag)
|
||||
}
|
||||
}, onCancel: {
|
||||
cancellableBag.cancel()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIFoundationUtils'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Set of helpers for Foundation framework classes.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '10.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
sources = '**/Sources/**/*.swift'
|
||||
if ENV["DEVELOPMENT_INSTALL"] # installing using :path =>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIGoogleMapUtils'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Google Maps SDK.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '12.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIKeychainUtils'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Set of helpers for Keychain classes.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIMapUtils'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '10.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -68,9 +68,9 @@ open class DefaultJsonNetworkService: ApiInteractor {
|
|||
defaultServer: openApi.defaultServer)
|
||||
}
|
||||
|
||||
open func process<B: Encodable, S: Decodable, F: Decodable, R>(request: EndpointRequest<B, S>,
|
||||
open func process<B: Encodable, S: Decodable, AE: Decodable, R>(request: EndpointRequest<B, S>,
|
||||
mapSuccess: @escaping Closure<S, R>,
|
||||
mapFailure: @escaping Closure<F, R>,
|
||||
mapFailure: @escaping Closure<FailureMappingInput<AE>, R>,
|
||||
mapNetworkError: @escaping Closure<MoyaError, R>,
|
||||
completion: @escaping ParameterClosure<R>) -> TIFoundationUtils.Cancellable {
|
||||
|
||||
|
|
@ -115,9 +115,9 @@ open class DefaultJsonNetworkService: ApiInteractor {
|
|||
return cancellableBag
|
||||
}
|
||||
|
||||
open func process<S: Decodable, F: Decodable, R>(request: SerializedRequest,
|
||||
open func process<S: Decodable, AE: Decodable, R>(request: SerializedRequest,
|
||||
mapSuccess: @escaping Closure<S, R>,
|
||||
mapFailure: @escaping Closure<F, R>,
|
||||
mapFailure: @escaping Closure<FailureMappingInput<AE>, R>,
|
||||
mapNetworkError: @escaping Closure<MoyaError, R>,
|
||||
completion: @escaping ParameterClosure<R>) -> TIFoundationUtils.Cancellable {
|
||||
|
||||
|
|
@ -152,8 +152,10 @@ open class DefaultJsonNetworkService: ApiInteractor {
|
|||
}
|
||||
|
||||
let decodeResult = rawResponse.decode(mapping: [
|
||||
((successStatusCodes, CommonMediaTypes.applicationJson.rawValue), jsonDecoder.decoding(to: mapSuccess)),
|
||||
((failureStatusCodes, CommonMediaTypes.applicationJson.rawValue), jsonDecoder.decoding(to: mapFailure)),
|
||||
KeyValueTuple(.json(with: successStatusCodes), jsonDecoder.decoding(to: mapSuccess)),
|
||||
KeyValueTuple(.json(with: failureStatusCodes), jsonDecoder.decoding(to: {
|
||||
mapFailure(FailureMappingInput($0, rawResponse.statusCode))
|
||||
})),
|
||||
])
|
||||
|
||||
let pluginResult: Result<Response, MoyaError>
|
||||
|
|
@ -192,10 +194,10 @@ open class DefaultJsonNetworkService: ApiInteractor {
|
|||
preprocessors.append(securityPreprocessor)
|
||||
}
|
||||
|
||||
private static func preprocess<B,S,P: Collection>(request: EndpointRequest<B,S>,
|
||||
private static func preprocess<B, S, P: Collection>(request: EndpointRequest<B, S>,
|
||||
preprocessors: P,
|
||||
cancellableBag: BaseCancellableBag,
|
||||
completion: @escaping (Result<EndpointRequest<B,S>, Error>) -> Void)
|
||||
completion: @escaping (Result<EndpointRequest<B, S>, Error>) -> Void)
|
||||
where P.Element == EndpointRequestPreprocessor {
|
||||
|
||||
guard let preprocessor = preprocessors.first else {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIMoyaNetworking'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Moya + Swagger network service.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/**/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ open class FingerprintsTrustEvaluator: ServerTrustEvaluating {
|
|||
guard SecTrustGetCertificateCount(trust) > 0,
|
||||
let certificate = SecTrustGetCertificateAtIndex(trust, 0) else {
|
||||
|
||||
throw PinValidationFailed.unableToExtractPin(trust: trust)
|
||||
}
|
||||
throw PinValidationFailed.unableToExtractPin(trust: trust)
|
||||
}
|
||||
|
||||
let certificateData = SecCertificateCopyData(certificate) as Data
|
||||
|
||||
|
|
@ -65,4 +65,3 @@ open class FingerprintsTrustEvaluator: ServerTrustEvaluating {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,10 +27,11 @@ public protocol ApiInteractor {
|
|||
associatedtype NetworkError
|
||||
|
||||
typealias RequestResult<S: Decodable, AE: Decodable> = EndpointRequestResult<S, AE, NetworkError>
|
||||
typealias FailureMappingInput<AE> = (apiError: AE, statusCode: Int)
|
||||
|
||||
func process<B: Encodable, S: Decodable, AE: Decodable, R>(request: EndpointRequest<B, S>,
|
||||
mapSuccess: @escaping Closure<S, R>,
|
||||
mapFailure: @escaping Closure<AE, R>,
|
||||
mapFailure: @escaping Closure<FailureMappingInput<AE>, R>,
|
||||
mapNetworkError: @escaping Closure<NetworkError, R>,
|
||||
completion: @escaping ParameterClosure<R>) -> Cancellable
|
||||
}
|
||||
|
|
@ -40,14 +41,14 @@ public extension ApiInteractor {
|
|||
func process<B: Encodable, S, F>(request: EndpointRequest<B, S>) async -> RequestResult<S, F> {
|
||||
await process(request: request,
|
||||
mapSuccess: Result.success,
|
||||
mapFailure: { .failure(.apiError($0)) },
|
||||
mapFailure: { .failure(.apiError($0.apiError, $0.statusCode)) },
|
||||
mapNetworkError: { .failure(.networkError($0)) })
|
||||
}
|
||||
|
||||
func process<B: Encodable, S: Decodable, F: Decodable, R>(request: EndpointRequest<B, S>,
|
||||
mapSuccess: @escaping Closure<S, R>,
|
||||
mapFailure: @escaping Closure<F, R>,
|
||||
mapNetworkError: @escaping Closure<NetworkError, R>) async -> R {
|
||||
func process<B: Encodable, S: Decodable, AE: Decodable, R>(request: EndpointRequest<B, S>,
|
||||
mapSuccess: @escaping Closure<S, R>,
|
||||
mapFailure: @escaping Closure<FailureMappingInput<AE>, R>,
|
||||
mapNetworkError: @escaping Closure<NetworkError, R>) async -> R {
|
||||
|
||||
await withTaskCancellableClosure { completion in
|
||||
process(request: request,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,6 @@
|
|||
//
|
||||
|
||||
public enum EndpointErrorResult<ApiError, NetworkError>: Error {
|
||||
case apiError(ApiError)
|
||||
case apiError(ApiError, Int)
|
||||
case networkError(NetworkError)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,4 +20,6 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public typealias EndpointRecoverableRequestResult<S: Decodable, AE: Decodable, NE> = Result<S, ErrorCollection<EndpointErrorResult<AE, NE>>>
|
||||
public typealias EndpointRecoverableRequestResult<S: Decodable,
|
||||
AE: Decodable,
|
||||
NE> = Result<S, ErrorCollection<EndpointErrorResult<AE, NE>>>
|
||||
|
|
|
|||
|
|
@ -32,12 +32,16 @@ public enum HTTPCodes {
|
|||
switch self {
|
||||
case .informational:
|
||||
return Set(100...199)
|
||||
|
||||
case .success:
|
||||
return Set(200...299)
|
||||
|
||||
case .redirection:
|
||||
return Set(300...399)
|
||||
|
||||
case .clientError:
|
||||
return Set(400...499)
|
||||
|
||||
case .serverError:
|
||||
return Set(500...599)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ public struct AnyEndpointRequestRetrier<ErrorResult: Error>: EndpointRequestRetr
|
|||
self.validateAndRepairClosure = retrier.validateAndRepair
|
||||
}
|
||||
|
||||
public func validateAndRepair(errorResults: [ErrorResult], completion: @escaping (EndpointRetryResult) -> Void) -> Cancellable {
|
||||
public func validateAndRepair(errorResults: [ErrorResult],
|
||||
completion: @escaping (EndpointRetryResult) -> Void) -> Cancellable {
|
||||
|
||||
validateAndRepairClosure(errorResults, completion)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ open class DefaultTokenInterceptor<RefreshError: Error>: RequestInterceptor {
|
|||
}
|
||||
|
||||
// MARK: - RequestRetrier
|
||||
|
||||
|
||||
open func retry(_ request: Request,
|
||||
for session: Session,
|
||||
dueTo error: Error,
|
||||
|
|
@ -85,6 +85,7 @@ open class DefaultTokenInterceptor<RefreshError: Error>: RequestInterceptor {
|
|||
switch $0 {
|
||||
case let .success(retryResult):
|
||||
completion(retryResult)
|
||||
|
||||
case let .failure(refreshError):
|
||||
completion(.doNotRetryWithError(refreshError))
|
||||
}
|
||||
|
|
@ -102,7 +103,7 @@ open class DefaultTokenInterceptor<RefreshError: Error>: RequestInterceptor {
|
|||
defaultCompletionResult: T,
|
||||
recoveredCompletionResult: T) -> Cancellable {
|
||||
|
||||
let operation = ClosureAsyncOperation<T, RefreshError>(cancellableTaskClosure: { [refreshTokenClosure] operationCompletion in
|
||||
ClosureAsyncOperation<T, RefreshError>(cancellableTaskClosure: { [refreshTokenClosure] operationCompletion in
|
||||
if validationClosure() {
|
||||
return refreshTokenClosure {
|
||||
if let error = $0 {
|
||||
|
|
@ -119,9 +120,6 @@ open class DefaultTokenInterceptor<RefreshError: Error>: RequestInterceptor {
|
|||
})
|
||||
.observe(onResult: completion,
|
||||
callbackQueue: .global())
|
||||
|
||||
operation.add(to: processingQueue)
|
||||
|
||||
return operation
|
||||
.add(to: processingQueue)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@
|
|||
import Alamofire
|
||||
import TIFoundationUtils
|
||||
|
||||
open class EndpointResponseTokenInterceptor<AE, NE>: DefaultTokenInterceptor<EndpointErrorResult<AE, NE>>, EndpointRequestRetrier {
|
||||
open class EndpointResponseTokenInterceptor<AE, NE>: DefaultTokenInterceptor<EndpointErrorResult<AE, NE>>,
|
||||
EndpointRequestRetrier {
|
||||
|
||||
public typealias IsTokenInvalidErrorResultClosure = (EndpointErrorResult<AE, NE>) -> Bool
|
||||
public typealias RepairResult = Result<RetryResult, EndpointErrorResult<AE, NE>>
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public struct ApplicationJsonBodySerializer<Body>: BodySerializer {
|
|||
public func serialize(body: Body?) throws -> ContentTypeData {
|
||||
let mimeType = CommonMediaTypes.applicationJson.rawValue
|
||||
|
||||
guard let body = body else {
|
||||
guard let body else {
|
||||
|
nikita.semenov marked this conversation as resolved
nikita.semenov
commented
Как так получается, что в подфайлах у нас версия swift 5.3, а конструкции используем из 5.7? Как так получается, что в подфайлах у нас версия swift 5.3, а конструкции используем из 5.7?
|
||||
return (mimeType, Data())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
import Foundation
|
||||
|
||||
public final class EmptyResponseContent: BaseContent, ResponseContent {
|
||||
public func decodeResponse(data: Data) throws -> Void {
|
||||
public func decodeResponse(data: Data) throws {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,12 +28,14 @@ open class HeaderParameterEncoding: ParameterEncoding {
|
|||
let key = $1.key
|
||||
let nonEmptyValueComponents = pathComponents(fromKey: key, value: $1.value.value)
|
||||
.filter { !$0.value.isEmpty || (parameters[key]?.allowEmptyValue ?? true) }
|
||||
$0.merge(nonEmptyValueComponents) { _, last in last }
|
||||
$0.merge(nonEmptyValueComponents) { _, last in
|
||||
last
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open func pathComponents(fromKey key: String, value: Any?) -> [String: String] {
|
||||
guard let value = value else {
|
||||
guard let value else {
|
||||
return [:]
|
||||
}
|
||||
|
||||
|
|
@ -42,10 +44,14 @@ open class HeaderParameterEncoding: ParameterEncoding {
|
|||
switch value {
|
||||
case let dictionary as [String: Any]:
|
||||
for (nestedKey, value) in dictionary {
|
||||
components.merge(pathComponents(fromKey: nestedKey, value: value)) { _, last in last }
|
||||
components.merge(pathComponents(fromKey: nestedKey, value: value)) { _, last in
|
||||
last
|
||||
}
|
||||
}
|
||||
|
||||
case let array as [Any]:
|
||||
components.updateValue(array.map { "\($0)" }.joined(separator: ","), forKey: key)
|
||||
|
||||
default:
|
||||
components.updateValue("\(value)", forKey: key)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ open class PathParameterEncoding: BaseUrlParameterEncoding, ParameterEncoding {
|
|||
|
||||
// MARK: - ParameterEncoding
|
||||
|
||||
open func encode(parameters: [String: Parameter<LocationPath>]) -> String {
|
||||
open func encode(parameters: [String: Parameter<LocationPath>]) -> String {
|
||||
.render(template: templateUrl, using: encode(parameters: parameters))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ import Foundation
|
|||
|
||||
public extension EndpointRequest {
|
||||
func serialize<Serializer: BodySerializer>(using serializer: Serializer,
|
||||
defaultServer: Server) throws -> SerializedRequest where Serializer.Body == Body {
|
||||
defaultServer: Server)
|
||||
throws -> SerializedRequest where Serializer.Body == Body {
|
||||
|
||||
let baseUrl = try (server ?? defaultServer).url(using: customServerVariables)
|
||||
let path = PathParameterEncoding(templateUrl: templatePath).encode(parameters: pathParameters)
|
||||
|
|
@ -47,7 +48,7 @@ public extension EndpointRequest {
|
|||
if let domain = baseUrl.host {
|
||||
cookies = cookieParameters.compactMap { key, value in
|
||||
HTTPCookie(properties: [
|
||||
.name : key,
|
||||
.name: key,
|
||||
.value: value,
|
||||
.domain: domain,
|
||||
.path: path
|
||||
|
|
@ -67,4 +68,3 @@ public extension EndpointRequest {
|
|||
acceptableStatusCodes: acceptableStatusCodes)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,16 +57,16 @@ extension SerializedRequest: Hashable {
|
|||
private var comparableQueryParameters: [String: String] {
|
||||
queryParameters.mapValues(String.init(describing:))
|
||||
}
|
||||
|
||||
|
||||
public static func == (lhs: SerializedRequest, rhs: SerializedRequest) -> Bool {
|
||||
lhs.baseURL == rhs.baseURL &&
|
||||
lhs.path == rhs.path &&
|
||||
lhs.method == rhs.method &&
|
||||
lhs.bodyData == rhs.bodyData &&
|
||||
lhs.comparableQueryParameters == rhs.comparableQueryParameters &&
|
||||
lhs.headers == rhs.headers &&
|
||||
lhs.cookies == rhs.cookies &&
|
||||
lhs.acceptableStatusCodes == rhs.acceptableStatusCodes
|
||||
lhs.path == rhs.path &&
|
||||
|
vladimir.makarov marked this conversation as resolved
vladimir.makarov
commented
Не очень красиво он здесь форматирует, конечно 🙂 Не очень красиво он здесь форматирует, конечно 🙂
ivan.smolin
commented
что поделать 🤷 что поделать 🤷
|
||||
lhs.method == rhs.method &&
|
||||
lhs.bodyData == rhs.bodyData &&
|
||||
lhs.comparableQueryParameters == rhs.comparableQueryParameters &&
|
||||
lhs.headers == rhs.headers &&
|
||||
lhs.cookies == rhs.cookies &&
|
||||
lhs.acceptableStatusCodes == rhs.acceptableStatusCodes
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ open class DefaultSecuritySchemePreprocessor: SecuritySchemePreprocessor {
|
|||
|
||||
// MARK: - EndpointSecurityRequestPreprocessor
|
||||
|
||||
public func preprocess<B,S>(request: EndpointRequest<B,S>,
|
||||
using security: SecurityScheme,
|
||||
completion: @escaping (Result<EndpointRequest<B,S>, Error>) -> Void) -> Cancellable {
|
||||
public func preprocess<B, S>(request: EndpointRequest<B, S>,
|
||||
using security: SecurityScheme,
|
||||
completion: @escaping (Result<EndpointRequest<B, S>, Error>) -> Void) -> Cancellable {
|
||||
|
||||
var modifiedRequest = request
|
||||
|
||||
|
|
@ -62,6 +62,7 @@ open class DefaultSecuritySchemePreprocessor: SecuritySchemePreprocessor {
|
|||
forKey: "Authorization")
|
||||
|
||||
modifiedRequest.headerParameters = headerParameters
|
||||
|
||||
case let .apiKey(parameterLocation, parameterName):
|
||||
switch parameterLocation {
|
||||
case .header:
|
||||
|
|
@ -70,9 +71,11 @@ open class DefaultSecuritySchemePreprocessor: SecuritySchemePreprocessor {
|
|||
forKey: parameterName)
|
||||
|
||||
modifiedRequest.headerParameters = headerParameters
|
||||
|
||||
case .query:
|
||||
modifiedRequest.queryParameters.updateValue(.init(value: value),
|
||||
forKey: parameterName)
|
||||
|
||||
case .cookie:
|
||||
modifiedRequest.cookieParameters.updateValue(.init(value: value),
|
||||
forKey: parameterName)
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ open class DefaultEndpointSecurityPreprocessor: EndpointRequestPreprocessor {
|
|||
self.schemePreprocessors = schemePreprocessors
|
||||
}
|
||||
|
||||
public func preprocess<B,S>(request: EndpointRequest<B,S>,
|
||||
completion: @escaping (Result<EndpointRequest<B,S>, Error>) -> Void) -> Cancellable {
|
||||
public func preprocess<B, S>(request: EndpointRequest<B, S>,
|
||||
completion: @escaping (Result<EndpointRequest<B, S>, Error>) -> Void) -> Cancellable {
|
||||
|
||||
guard !request.security.compactMap({ $0 }).isEmpty else {
|
||||
completion(.success(request))
|
||||
|
|
@ -60,13 +60,10 @@ open class DefaultEndpointSecurityPreprocessor: EndpointRequestPreprocessor {
|
|||
return Self.preprocess(request: request,
|
||||
using: endpointSchemes,
|
||||
schemePreprocessors: schemePreprocessors) { [schemePreprocessors] in
|
||||
switch $0 {
|
||||
case let .success(modifiedRequest):
|
||||
completion(.success(modifiedRequest))
|
||||
case .failure:
|
||||
completion(.failure(PreprocessError.unableToSatisfyRequirements(anyOfRequired: request.security,
|
||||
registeredPreprocessors: schemePreprocessors)))
|
||||
}
|
||||
completion($0.mapError { _ in
|
||||
PreprocessError.unableToSatisfyRequirements(anyOfRequired: request.security,
|
||||
registeredPreprocessors: schemePreprocessors)
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
|
|
@ -78,11 +75,11 @@ open class DefaultEndpointSecurityPreprocessor: EndpointRequestPreprocessor {
|
|||
schemePreprocessors[scheme] = preprocessor
|
||||
}
|
||||
|
||||
private static func preprocess<B,S,SC: Collection>(request: EndpointRequest<B,S>,
|
||||
using schemes: SC,
|
||||
schemePreprocessors: [String: SecuritySchemePreprocessor],
|
||||
completion: @escaping (Result<EndpointRequest<B,S>, Error>) -> Void) -> Cancellable
|
||||
where SC.Element == [KeyValueTuple<String, SecurityScheme>] {
|
||||
private static func preprocess<B, S, SC: Collection>(request: EndpointRequest<B, S>,
|
||||
using schemes: SC,
|
||||
schemePreprocessors: [String: SecuritySchemePreprocessor],
|
||||
completion: @escaping (Result<EndpointRequest<B, S>, Error>) -> Void)
|
||||
-> Cancellable where SC.Element == [KeyValueTuple<String, SecurityScheme>] {
|
||||
|
||||
guard let schemeGroup = schemes.first else {
|
||||
completion(.success(request))
|
||||
|
|
@ -113,6 +110,7 @@ open class DefaultEndpointSecurityPreprocessor: EndpointRequestPreprocessor {
|
|||
switch $0 {
|
||||
case let .success(modifiedRequest):
|
||||
completion(.success(modifiedRequest))
|
||||
|
||||
case let .failure(error):
|
||||
guard !schemes.isEmpty else {
|
||||
completion(.failure(error))
|
||||
|
|
@ -129,10 +127,10 @@ open class DefaultEndpointSecurityPreprocessor: EndpointRequestPreprocessor {
|
|||
}
|
||||
}
|
||||
|
||||
private static func preprocess<B,S,G: Collection>(request: EndpointRequest<B,S>,
|
||||
with groups: G,
|
||||
completion: @escaping (Result<EndpointRequest<B,S>, Error>) -> Void) -> Cancellable
|
||||
where G.Element == KeyValueTuple<SecurityScheme, SecuritySchemePreprocessor> {
|
||||
private static func preprocess<B, S, G: Collection>(request: EndpointRequest<B, S>,
|
||||
with groups: G,
|
||||
completion: @escaping (Result<EndpointRequest<B, S>, Error>) -> Void)
|
||||
-> Cancellable where G.Element == KeyValueTuple<SecurityScheme, SecuritySchemePreprocessor> {
|
||||
|
||||
guard let group = groups.first else {
|
||||
completion(.success(request))
|
||||
|
|
@ -148,6 +146,7 @@ open class DefaultEndpointSecurityPreprocessor: EndpointRequestPreprocessor {
|
|||
with: groups.dropFirst(),
|
||||
completion: completion)
|
||||
.add(to: scope)
|
||||
|
||||
case let .failure(error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@
|
|||
import TIFoundationUtils
|
||||
|
||||
public protocol EndpointRequestPreprocessor {
|
||||
func preprocess<B,S>(request: EndpointRequest<B,S>,
|
||||
completion: @escaping (Result<EndpointRequest<B,S>, Error>) -> Void) -> Cancellable
|
||||
func preprocess<B, S>(request: EndpointRequest<B, S>,
|
||||
completion: @escaping (Result<EndpointRequest<B, S>, Error>) -> Void) -> Cancellable
|
||||
}
|
||||
|
||||
@available(iOS 13.0.0, *)
|
||||
public extension EndpointRequestPreprocessor {
|
||||
func preprocess<B,S>(request: EndpointRequest<B,S>) async -> Result<EndpointRequest<B,S>, Error> {
|
||||
func preprocess<B, S>(request: EndpointRequest<B, S>) async -> Result<EndpointRequest<B, S>, Error> {
|
||||
await withTaskCancellableClosure { completion in
|
||||
preprocess(request: request) {
|
||||
completion($0)
|
||||
|
|
|
|||
|
|
@ -23,14 +23,16 @@
|
|||
import TIFoundationUtils
|
||||
|
||||
public protocol SecuritySchemePreprocessor {
|
||||
func preprocess<B,S>(request: EndpointRequest<B,S>,
|
||||
using security: SecurityScheme,
|
||||
completion: @escaping (Result<EndpointRequest<B,S>, Error>) -> Void) -> Cancellable
|
||||
func preprocess<B, S>(request: EndpointRequest<B, S>,
|
||||
using security: SecurityScheme,
|
||||
completion: @escaping (Result<EndpointRequest<B, S>, Error>) -> Void) -> Cancellable
|
||||
}
|
||||
|
||||
@available(iOS 13.0.0, *)
|
||||
public extension SecuritySchemePreprocessor {
|
||||
func preprocess<B,S>(request: EndpointRequest<B,S>, using security: SecurityScheme) async -> Result<EndpointRequest<B,S>, Error> {
|
||||
func preprocess<B, S>(request: EndpointRequest<B, S>, using security: SecurityScheme)
|
||||
async -> Result<EndpointRequest<B, S>, Error> {
|
||||
|
||||
await withTaskCancellableClosure { completion in
|
||||
preprocess(request: request, using: security) {
|
||||
completion($0)
|
||||
|
|
|
|||
|
|
@ -23,15 +23,12 @@
|
|||
import Foundation
|
||||
import TISwiftUtils
|
||||
|
||||
public typealias StatusCodeMimeType = (statusCode: Int, mimeType: String?)
|
||||
public typealias StatusCodesMimeType = (statusCodes: Set<Int>, mimeType: String?)
|
||||
|
||||
public typealias DecodingClosure<R> = ThrowableClosure<Data, R>
|
||||
|
||||
public extension ResponseType {
|
||||
typealias DecodingClosure<R> = ThrowableClosure<Data, R>
|
||||
|
||||
func decode<R>(mapping: [KeyValueTuple<StatusCodeMimeType, DecodingClosure<R>>]) -> Result<R, ErrorType> {
|
||||
for ((mappingStatusCode, mappingMimeType), decodeClosure) in mapping
|
||||
where mappingStatusCode == statusCode && mappingMimeType == mimeType {
|
||||
for (statusCodesMimeType, decodeClosure) in mapping
|
||||
where statusCodesMimeType.statusCode == statusCode && statusCodesMimeType.mimeType == mimeType {
|
||||
do {
|
||||
return .success(try decodeClosure(data))
|
||||
} catch {
|
||||
|
|
@ -52,7 +49,8 @@ public extension ResponseType {
|
|||
|
||||
func decode<R>(mapping: [KeyValueTuple<StatusCodesMimeType, DecodingClosure<R>>]) -> Result<R, ErrorType> {
|
||||
decode(mapping: mapping.map { key, value in
|
||||
key.statusCodes.map { KeyValueTuple(StatusCodeMimeType($0, key.mimeType), value) }
|
||||
}.flatMap { $0 })
|
||||
key.statusCodes.map { KeyValueTuple(StatusCodeMimeType(statusCode: $0, mimeType: key.mimeType), value) }
|
||||
}
|
||||
.flatMap { $0 })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public struct StatusCodeMimeType {
|
||||
public let statusCode: Int
|
||||
public let mimeType: String?
|
||||
|
||||
public init(statusCode: Int, mimeType: String?) {
|
||||
self.statusCode = statusCode
|
||||
self.mimeType = mimeType
|
||||
}
|
||||
|
||||
public static func json(with statusCode: Int) -> Self {
|
||||
.init(statusCode: statusCode, mimeType: CommonMediaTypes.applicationJson.rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright (c) 2023 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public struct StatusCodesMimeType {
|
||||
public let statusCodes: Set<Int>
|
||||
public let mimeType: String?
|
||||
|
||||
public init(statusCodes: Set<Int>, mimeType: String?) {
|
||||
self.statusCodes = statusCodes
|
||||
self.mimeType = mimeType
|
||||
}
|
||||
|
||||
public static func json(with statusCodes: Set<Int>) -> Self {
|
||||
.init(statusCodes: statusCodes, mimeType: CommonMediaTypes.applicationJson.rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -21,8 +21,8 @@
|
|||
//
|
||||
|
||||
public struct HTTPAuthenticationScheme: RawRepresentable, Equatable, Hashable {
|
||||
public static let basic = HTTPAuthenticationScheme(rawValue: "Basic")
|
||||
public static let bearer = HTTPAuthenticationScheme(rawValue: "Bearer")
|
||||
public static let basic = Self(rawValue: "Basic")
|
||||
public static let bearer = Self(rawValue: "Bearer")
|
||||
|
||||
public let rawValue: String
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,9 @@ public struct Server {
|
|||
}
|
||||
|
||||
let defaultVariablesToApply = self.defaultVariables
|
||||
.filter { (key, _) in variables.contains { $0.key == key } }
|
||||
.filter { key, _ in
|
||||
variables.contains { $0.key == key }
|
||||
}
|
||||
|
||||
let defaultParametersTemplate = String.render(template: urlTemplate,
|
||||
using: defaultVariablesToApply)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import Foundation
|
|||
|
||||
public extension String {
|
||||
var urlEscaped: String {
|
||||
return addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? self
|
||||
addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? self
|
||||
}
|
||||
|
||||
var urlHost: String {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TINetworking'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Swagger-frendly networking layer helpers.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '10.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TINetworkingCache'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Caching results of EndpointRequests.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIPagination'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Generic pagination component.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '10.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TISwiftUICore'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Core UI elements: protocols, views and helpers.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '13.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TISwiftUtils'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Bunch of useful helpers for Swift development.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '9.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
sources = 'Sources/**/*.swift'
|
||||
if ENV["DEVELOPMENT_INSTALL"] # installing using :path =>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TITableKitUtils'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Set of helpers for TableKit classes.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TITextProcessing'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'A text processing service helping to get a text mask and a placeholder from incoming regex.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' }
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '10.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
sources = '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIUIElements'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Bunch of useful protocols and views.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -9,7 +9,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
sources = '/Sources/**/*'
|
||||
if ENV["DEVELOPMENT_INSTALL"] # installing using :path =>
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ public struct AlertAction {
|
|||
public let id = UUID()
|
||||
|
||||
/// Alert button title
|
||||
public let title: String
|
||||
public var title: String
|
||||
|
||||
/// Alert button style
|
||||
public let style: UIAlertAction.Style
|
||||
public var style: UIAlertAction.Style
|
||||
|
||||
/// Alert button action
|
||||
public let action: VoidClosure?
|
||||
public var action: VoidClosure?
|
||||
|
||||
public init(title: String, style: UIAlertAction.Style = .default, action: VoidClosure? = nil) {
|
||||
self.title = title
|
||||
|
|
|
|||
|
|
@ -26,19 +26,19 @@ import UIKit
|
|||
public struct AlertDescriptor {
|
||||
|
||||
/// Alert title
|
||||
public let title: String?
|
||||
public var title: String?
|
||||
|
||||
/// Alert message
|
||||
public let message: String?
|
||||
public var message: String?
|
||||
|
||||
/// Alert style
|
||||
public let style: UIAlertController.Style
|
||||
public var style: UIAlertController.Style
|
||||
|
||||
/// Alert tint color
|
||||
public let tintColor: UIColor
|
||||
public var tintColor: UIColor
|
||||
|
||||
/// Alert actions
|
||||
public let actions: [AlertAction]
|
||||
public var actions: [AlertAction]
|
||||
|
||||
public init(title: String? = nil,
|
||||
message: String? = nil,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIUIKitCore'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Core UI elements: protocols, views and helpers.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -9,7 +9,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
s.framework = 'UIKit'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIWebView'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Universal web view API'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -9,7 +9,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIYandexMapUtils'
|
||||
s.version = '1.43.1'
|
||||
s.version = '1.44.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Yandex Maps SDK.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
|||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/LeadKit.git', :tag => s.version.to_s }
|
||||
|
||||
s.ios.deployment_target = '12.0'
|
||||
s.swift_versions = ['5.3']
|
||||
s.swift_versions = ['5.7']
|
||||
|
||||
s.source_files = s.name + '/Sources/**/*'
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 1f83bf5d08bbc2c2346141621a42b2d2e0dd6517
|
||||
Subproject commit 39109c6e6032b2a59f4cdd7b80ac06c4dc8b33c0
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
readonly CONFIG_PATH=${PROJECT_DIR}/build-scripts/xcode/.swiftlint.yml
|
||||
|
||||
readonly SWIFTLINT_VERSION=0.39.1
|
||||
readonly SWIFTLINT_PORTABLE_FILENAME=portable_swiftlint.zip
|
||||
|
||||
readonly SWIFTLINT_PORTABLE_URL=https://github.com/realm/SwiftLint/releases/download/${SWIFTLINT_VERSION}/${SWIFTLINT_PORTABLE_FILENAME}
|
||||
|
||||
. build-scripts/xcode/aux_scripts/download_file.sh ${SWIFTLINT_PORTABLE_FILENAME} ${SWIFTLINT_PORTABLE_URL} Downloads --remove-cached
|
||||
|
||||
cd Downloads && unzip -o ${SWIFTLINT_PORTABLE_FILENAME}
|
||||
|
||||
${PROJECT_DIR}/Downloads/swiftlint autocorrect --path ${PROJECT_DIR}/Sources --config ${CONFIG_PATH} && ${PROJECT_DIR}/Downloads/swiftlint --path ${PROJECT_DIR}/Sources --config ${CONFIG_PATH}
|
||||
|
|
@ -0,0 +1 @@
|
|||
build-scripts/xcode/.swiftlint.yml
|
||||
Loading…
Reference in New Issue
оставшиеся комменты - ок?
Да, предполагается, что тут как и в проектах будет инкрементальный линтинг, но если хочеться поправить всё - то просто расскоментировать.
Ну последний пустой коммент это артефакт конечно, но пофикшу если будут еще комменты