Merge pull request #292 from TouchInstinct/feature/tinetworking_nullable_body

Few fixes for code generation
This commit is contained in:
Ivan Smolin 2022-03-29 12:55:54 +03:00 committed by GitHub
commit 3e5d8d13fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 183 additions and 54 deletions

View File

@ -1,5 +1,12 @@
# Changelog
### 1.12.0
- **Update**: EndpointRequest Body can take a nil value
- **Update**: Parameter value can be nil as well
- **Update**: observe operator of AsyncOperation now accepts callback queue parameter
### 1.11.1
- **Fix**: `timeoutIntervalForRequest` parameter for `URLSessionConfiguration` in `NetworkServiceConfiguration` added.

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "LeadKit"
s.version = "1.11.1"
s.version = "1.12.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"

View File

@ -27,18 +27,25 @@ private final class ClosureObserverOperation<Output, Failure: Error>: AsyncOpera
public init(dependency: AsyncOperation<Output, Failure>,
onSuccess: ((Output) -> Void)? = nil,
onFailure: ((Failure) -> Void)? = nil) {
onFailure: ((Failure) -> Void)? = nil,
callbackQueue: DispatchQueue = .main) {
super.init()
cancelOnCancellation(of: dependency)
dependencyObservation = dependency.subscribe { [weak self] in
onSuccess?($0)
self?.handle(result: $0)
} onFailure: { [weak self] in
onFailure?($0)
self?.handle(error: $0)
dependencyObservation = dependency.subscribe { [weak self] result in
callbackQueue.async {
onSuccess?(result)
}
self?.handle(result: result)
} onFailure: { [weak self] error in
callbackQueue.async {
onFailure?(error)
}
self?.handle(error: error)
}
addDependency(dependency) // keeps strong reference to dependency as well
@ -49,8 +56,12 @@ private final class ClosureObserverOperation<Output, Failure: Error>: AsyncOpera
public extension AsyncOperation {
func observe(onSuccess: ((Output) -> Void)? = nil,
onFailure: ((Failure) -> Void)? = nil) -> AsyncOperation<Output, Failure> {
onFailure: ((Failure) -> Void)? = nil,
callbackQueue: DispatchQueue = .main) -> AsyncOperation<Output, Failure> {
ClosureObserverOperation(dependency: self, onSuccess: onSuccess, onFailure: onFailure)
ClosureObserverOperation(dependency: self,
onSuccess: onSuccess,
onFailure: onFailure,
callbackQueue: callbackQueue)
}
}

View File

@ -28,5 +28,6 @@ public protocol CancellableTask {
@available(iOS 13.0, *)
extension Task: CancellableTask {}
extension Operation: CancellableTask {}
extension DispatchWorkItem: CancellableTask {}

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'TIFoundationUtils'
s.version = '1.11.1'
s.version = '1.12.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' }

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'TIKeychainUtils'
s.version = '1.11.1'
s.version = '1.12.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' }

View File

@ -0,0 +1,34 @@
//
// Copyright (c) 2022 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 Moya
import TISwiftUtils
public extension Cancellable {
static func nonCancellable() -> Cancellable {
NonCancellable()
}
static func scoped(scopeCancellableClosure: ScopeCancellable.ScopeCancellableClosure) -> Cancellable {
ScopeCancellable(scopeCancellableClosure: scopeCancellableClosure)
}
}

View File

@ -0,0 +1,31 @@
//
// Copyright (c) 2022 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 Moya
public struct NonCancellable: Cancellable {
public let isCancelled = true
public func cancel() {
// nothing
}
}

View File

@ -24,7 +24,9 @@ import Moya
import TISwiftUtils
public final class ScopeCancellable: CancellableBag {
public init(scopeCancellableClosure: Closure<ScopeCancellable, Cancellable>) {
public typealias ScopeCancellableClosure = Closure<ScopeCancellable, Cancellable>
public init(scopeCancellableClosure: ScopeCancellableClosure) {
super.init()
cancellables = [scopeCancellableClosure(self)]

View File

@ -28,11 +28,11 @@ import Moya
open class DefaultRecoverableJsonNetworkService<ApiError: Decodable & Error>: DefaultJsonNetworkService {
public typealias ErrorHandler = AnyAsyncEventHandler<ApiError>
private var defaultErrorHandlers: [ErrorHandler] = []
private(set) public var defaultErrorHandlers: [ErrorHandler] = []
public func process<B: Encodable, S: Decodable>(request: EndpointRequest<B, S>,
prependErrorHandlers: [ErrorHandler] = [],
appendErrorHandlers: [ErrorHandler] = [],
prependErrorHandlers: [ErrorHandler],
appendErrorHandlers: [ErrorHandler],
mapMoyaError: @escaping Closure<MoyaError, ApiError>) async -> Result<S, ApiError> {
await process(request: request,
@ -41,7 +41,7 @@ open class DefaultRecoverableJsonNetworkService<ApiError: Decodable & Error>: De
}
public func process<B: Encodable, S: Decodable>(request: EndpointRequest<B, S>,
errorHandlers: [ErrorHandler] = [],
errorHandlers: [ErrorHandler],
mapMoyaError: @escaping Closure<MoyaError, ApiError>) async -> Result<S, ApiError> {
let result = await process(request: request, mapMoyaError: mapMoyaError)

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'TIMoyaNetworking'
s.version = '1.11.1'
s.version = '1.12.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' }

View File

@ -38,7 +38,13 @@ public struct ApplicationJsonBodySerializer<Body>: BodySerializer {
}
}
public func serialize(body: Body) throws -> ContentTypeData {
(CommonMediaTypes.applicationJson.rawValue, try encodingClosure(body))
public func serialize(body: Body?) throws -> ContentTypeData {
let mimeType = CommonMediaTypes.applicationJson.rawValue
guard let body = body else {
return (mimeType, Data())
}
return ContentTypeData(mimeType, try encodingClosure(body))
}
}

View File

@ -27,5 +27,5 @@ public protocol BodySerializer {
typealias ContentTypeData = (String, Data)
func serialize(body: Body) throws -> ContentTypeData
func serialize(body: Body?) throws -> ContentTypeData
}

View File

@ -20,8 +20,6 @@
// THE SOFTWARE.
//
import Foundation
public struct EmptyBody: Encodable {
public struct Nothing: Codable {
public init() {}
}

View File

@ -31,11 +31,11 @@ open class BaseUrlParameterEncoding {
var filteredComponents: [KeyValueTuple<String, String>] = []
for key in parameters.keys.sorted(by: <) {
guard let parameter = parameters[key] else {
guard let parameter = parameters[key], let value = parameter.value else {
continue
}
let components = encoding.queryComponents(fromKey: key, value: parameter.value)
let components = encoding.queryComponents(fromKey: key, value: value)
// filter components with empty values if parameter doesn't allow empty value
.filter { !$0.1.isEmpty || parameter.allowEmptyValue }

View File

@ -0,0 +1,29 @@
//
// Copyright (c) 2022 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.
//
open class HeaderParameterEncoding: BaseUrlParameterEncoding, ParameterEncoding {
// MARK: - ParameterEncoding
open func encode(parameters: [String: Parameter<LocationHeader>]) -> [String: String] {
return Dictionary(uniqueKeysWithValues: super.encode(parameters: parameters))
}
}

View File

@ -25,10 +25,10 @@ open class QueryStringParameterEncoding: BaseUrlParameterEncoding, ParameterEnco
// MARK: - ParameterEncoding
open func encode(parameters: [String: Parameter<LocationQuery>]) -> [String: Any] {
let includedKeys = Set(super.encode(parameters: parameters).map { $0.key })
let nonEmptyValueKeys = Set(super.encode(parameters: parameters).map { $0.key })
return parameters
.filter { includedKeys.contains($0.key) }
.mapValues { $0.value }
.filter { nonEmptyValueKeys.contains($0.key) }
.compactMapValues { $0.value }
}
}

View File

@ -21,10 +21,10 @@
//
public struct Parameter<Location: ParameterLocation> {
public let value: Any
public let value: Any?
public let allowEmptyValue: Bool
public init(value: Any, allowEmptyValue: Bool = false) {
public init(value: Any?, allowEmptyValue: Bool = false) {
self.value = value
self.allowEmptyValue = allowEmptyValue
}

View File

@ -30,10 +30,15 @@ public extension EndpointRequest {
let baseUrl = try (server ?? defaultServer).url(using: customServerVariables)
let path = PathParameterEncoding(templateUrl: templatePath).encode(parameters: pathParameters)
let (contentType, bodyData) = try serializer.serialize(body: body)
let queryParameters = QueryStringParameterEncoding().encode(parameters: queryParameters)
let serializedQueryParameters = QueryStringParameterEncoding().encode(parameters: queryParameters)
var headerParameters = headerParameters ?? HTTPHeaders()
headerParameters.add(.contentType(contentType))
var serializedHeaderParameters: [String: String]?
if let customHeaderParameters = headerParameters {
serializedHeaderParameters = HeaderParameterEncoding().encode(parameters: customHeaderParameters)
}
serializedHeaderParameters?[HTTPHeader.contentType(contentType).name] = contentType
let cookies: [HTTPCookie]
@ -54,8 +59,8 @@ public extension EndpointRequest {
path: path,
method: method,
bodyData: bodyData,
queryParameters: queryParameters,
headers: headerParameters.dictionary,
queryParameters: serializedQueryParameters,
headers: serializedHeaderParameters,
cookies: cookies,
acceptableStatusCodes: acceptableStatusCodes)
}

View File

@ -25,10 +25,10 @@ import Alamofire
public struct EndpointRequest<Body, SuccessResponse> {
public var templatePath: String
public var method: HTTPMethod
public var body: Body
public var body: Body?
public var queryParameters: [String: Parameter<LocationQuery>]
public var pathParameters: [String: Parameter<LocationPath>]
public var headerParameters: HTTPHeaders?
public var headerParameters: [String: Parameter<LocationHeader>]?
public var cookieParameters: [String: Parameter<LocationCookie>]
public var acceptableStatusCodes: Set<Int>
public var server: Server?
@ -36,10 +36,10 @@ public struct EndpointRequest<Body, SuccessResponse> {
public init(templatePath: String,
method: HTTPMethod,
body: Body,
body: Body?,
queryParameters: [String: Parameter<LocationQuery>] = [:],
pathParameters: [String: Parameter<LocationPath>] = [:],
headerParameters: HTTPHeaders? = nil,
headerParameters: [String: Parameter<LocationHeader>]? = nil,
cookieParameters: [String: Parameter<LocationCookie>] = [:],
acceptableStatusCodes: Set<Int> = HTTPCodes.success.asSet(),
server: Server? = nil,

View File

@ -38,7 +38,7 @@ public struct SerializedRequest {
method: HTTPMethod,
bodyData: Data,
queryParameters: Parameters,
headers: [String: String],
headers: [String: String]?,
cookies: [HTTPCookie],
acceptableStatusCodes: Set<Int>) {

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'TINetworking'
s.version = '1.11.1'
s.version = '1.12.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' }

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'TISwiftUtils'
s.version = '1.11.1'
s.version = '1.12.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' }

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'TITableKitUtils'
s.version = '1.11.1'
s.version = '1.12.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' }

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'TITransitions'
s.version = '1.11.1'
s.version = '1.12.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' }

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'TIUIElements'
s.version = '1.11.1'
s.version = '1.12.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' }

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'TIUIKitCore'
s.version = '1.11.1'
s.version = '1.12.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' }

View File

@ -5,10 +5,15 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$DIR"
# Push changes
find ../ -name '*.podspec' \
-not -path "../Carthage/*" \
-not -path "../*/Carthage/*" \
-not -path "../Pods/*" \
-not -path "../*/Pods/*" \
| xargs -I% pod repo push git@github.com:TouchInstinct/Podspecs % --allow-warnings
ORDERED_PODSPECS="../TISwiftUtils/TISwiftUtils.podspec
../TIFoundationUtils/TIFoundationUtils.podspec
../TIKeychainUtils/TIKeychainUtils.podspec
../TIUIKitCore/TIUIKitCore.podspec
../TIUIElements/TIUIElements.podspec
../TITableKitUtils/TITableKitUtils.podspec
../TINetworking/TINetworking.podspec
../TIMoyaNetworking/TIMoyaNetworking.podspec"
for podspec_path in ${ORDERED_PODSPECS}; do
pod repo push git@github.com:TouchInstinct/Podspecs ${podspec_path} --allow-warnings
done