From 10ec9408add8790c12df4e4586a89560e0260bab Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Wed, 6 Apr 2022 13:44:06 +0300 Subject: [PATCH] feat: Change access modifiers in `DefaultJsonNetworkService` from `public` to `open`, added additional Moya plugins processing add `DisplayDecodingErrorPlugin` for showing developer-frendly decoding error messages add Gemfile for cocoapods versioning --- .bundle/config | 2 + .gitignore | 3 + CHANGELOG.md | 6 + Gemfile | 5 + Gemfile.lock | 97 ++++++++++++++++ LeadKit.podspec | 2 +- TIFoundationUtils/TIFoundationUtils.podspec | 2 +- TIKeychainUtils/TIKeychainUtils.podspec | 2 +- .../DefaultJsonNetworkService.swift | 48 +++++--- .../DecodingErrorSummary.swift | 108 ++++++++++++++++++ .../DecodingErrorSummaryFormatter.swift | 61 ++++++++++ .../DisplayDecodingErrorPlugin.swift | 47 ++++++++ TIMoyaNetworking/TIMoyaNetworking.podspec | 4 +- TINetworking/TINetworking.podspec | 2 +- TISwiftUtils/TISwiftUtils.podspec | 2 +- TITableKitUtils/TITableKitUtils.podspec | 2 +- TITransitions/TITransitions.podspec | 2 +- TIUIElements/TIUIElements.podspec | 2 +- TIUIKitCore/TIUIKitCore.podspec | 2 +- 19 files changed, 370 insertions(+), 29 deletions(-) create mode 100644 .bundle/config create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DecodingErrorSummary.swift create mode 100644 TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DecodingErrorSummaryFormatter.swift create mode 100644 TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DisplayDecodingErrorPlugin.swift diff --git a/.bundle/config b/.bundle/config new file mode 100644 index 00000000..94c09909 --- /dev/null +++ b/.bundle/config @@ -0,0 +1,2 @@ +--- +BUNDLE_PATH: ".gem" diff --git a/.gitignore b/.gitignore index 7836f155..2731fa25 100644 --- a/.gitignore +++ b/.gitignore @@ -104,4 +104,7 @@ cpd-output.xml *.swp *IDEWorkspaceChecks.plist +# Gem +.gem/ + .DS_Store \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ae0b8b6..751f39bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +### 1.13.0 + +- **Update**: Change access modifiers in `DefaultJsonNetworkService` from `public` to `open`, added additional Moya plugins processing +- **Add**: `DisplayDecodingErrorPlugin` for showing developer-frendly decoding error messages +- **Add**: Gemfile for cocoapods versioning + ### 1.12.3 - **Fix**: Try parse date in ISO8601 format appending `.withFractionalSeconds` if `.withInternetDateTime` fails diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..597dd9a2 --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "cocoapods", "~> 1.11" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..3e0e8625 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,97 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.5) + rexml + activesupport (6.1.5) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + algoliasearch (1.27.5) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) + atomos (0.1.3) + claide (1.1.0) + cocoapods (1.11.3) + addressable (~> 2.8) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.11.3) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.4.0, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.4.0, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.8.0) + nap (~> 1.0) + ruby-macho (>= 1.0, < 3.0) + xcodeproj (>= 1.21.0, < 2.0) + cocoapods-core (1.11.3) + activesupport (>= 5.0, < 7) + addressable (~> 2.8) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + netrc (~> 0.11) + public_suffix (~> 4.0) + typhoeus (~> 1.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (1.6.2) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.1) + cocoapods-trunk (1.6.0) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.2.0) + colored2 (3.1.2) + concurrent-ruby (1.1.10) + escape (0.0.4) + ethon (0.15.0) + ffi (>= 1.15.0) + ffi (1.15.5) + fourflusher (2.3.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + httpclient (2.8.3) + i18n (1.10.0) + concurrent-ruby (~> 1.0) + json (2.6.1) + minitest (5.15.0) + molinillo (0.8.0) + nanaimo (0.3.0) + nap (1.1.0) + netrc (0.11.0) + public_suffix (4.0.6) + rexml (3.2.5) + ruby-macho (2.5.1) + typhoeus (1.4.0) + ethon (>= 0.9.0) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) + xcodeproj (1.21.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) + zeitwerk (2.5.4) + +PLATFORMS + x86_64-darwin-20 + +DEPENDENCIES + cocoapods (~> 1.11) + +BUNDLED WITH + 2.3.10 diff --git a/LeadKit.podspec b/LeadKit.podspec index 4acd8148..2638f6f4 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKit" - s.version = "1.12.3" + s.version = "1.13.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/TIFoundationUtils/TIFoundationUtils.podspec b/TIFoundationUtils/TIFoundationUtils.podspec index f51881db..3de7247d 100644 --- a/TIFoundationUtils/TIFoundationUtils.podspec +++ b/TIFoundationUtils/TIFoundationUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIFoundationUtils' - s.version = '1.12.3' + s.version = '1.13.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/TIKeychainUtils/TIKeychainUtils.podspec b/TIKeychainUtils/TIKeychainUtils.podspec index 688065e9..855208ac 100644 --- a/TIKeychainUtils/TIKeychainUtils.podspec +++ b/TIKeychainUtils/TIKeychainUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIKeychainUtils' - s.version = '1.12.3' + s.version = '1.13.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/TIMoyaNetworking/Sources/NetworkService/DefaultJsonNetworkService.swift b/TIMoyaNetworking/Sources/NetworkService/DefaultJsonNetworkService.swift index ec9146d8..c7952237 100644 --- a/TIMoyaNetworking/Sources/NetworkService/DefaultJsonNetworkService.swift +++ b/TIMoyaNetworking/Sources/NetworkService/DefaultJsonNetworkService.swift @@ -60,8 +60,8 @@ open class DefaultJsonNetworkService { } @available(iOS 13.0.0, *) - public func process(request: EndpointRequest, - mapMoyaError: @escaping Closure) async -> Result { + open func process(request: EndpointRequest, + mapMoyaError: @escaping Closure) async -> Result { await process(request: request, mapSuccess: Result.success, mapFailure: Result.failure, @@ -69,10 +69,10 @@ open class DefaultJsonNetworkService { } @available(iOS 13.0.0, *) - public func process(request: EndpointRequest, - mapSuccess: @escaping Closure, - mapFailure: @escaping Closure, - mapMoyaError: @escaping Closure) async -> R { + open func process(request: EndpointRequest, + mapSuccess: @escaping Closure, + mapFailure: @escaping Closure, + mapMoyaError: @escaping Closure) async -> R { let cancellableBag = CancellableBag() @@ -87,16 +87,16 @@ open class DefaultJsonNetworkService { continuation.resume(returning: $0) } - .add(to: cancellableBag) + .add(to: cancellableBag) } }) } - public func process(request: EndpointRequest, - mapSuccess: @escaping Closure, - mapFailure: @escaping Closure, - mapMoyaError: @escaping Closure, - completion: @escaping ParameterClosure) -> Cancellable { + open func process(request: EndpointRequest, + mapSuccess: @escaping Closure, + mapFailure: @escaping Closure, + mapMoyaError: @escaping Closure, + completion: @escaping ParameterClosure) -> Cancellable { ScopeCancellable { [jsonEncoder, serializationQueue, callbackQueue, defaultServer] scope in let workItem = DispatchWorkItem { @@ -126,13 +126,17 @@ open class DefaultJsonNetworkService { } } - public func process(request: SerializedRequest, - mapSuccess: @escaping Closure, - mapFailure: @escaping Closure, - mapMoyaError: @escaping Closure, - completion: @escaping ParameterClosure) -> Cancellable { + open func process(request: SerializedRequest, + mapSuccess: @escaping Closure, + mapFailure: @escaping Closure, + mapMoyaError: @escaping Closure, + completion: @escaping ParameterClosure) -> Cancellable { - createProvider().request(request) { [jsonDecoder, callbackQueue, decodableSuccessStatusCodes, decodableFailureStatusCodes] in + createProvider().request(request) { [jsonDecoder, + callbackQueue, + decodableSuccessStatusCodes, + decodableFailureStatusCodes, + plugins] in let result: R switch $0 { @@ -163,11 +167,19 @@ open class DefaultJsonNetworkService { ((failureStatusCodes, CommonMediaTypes.applicationJson.rawValue), jsonDecoder.decoding(to: mapFailure)), ]) + let pluginResult: Result + switch decodeResult { case let .success(model): result = model + pluginResult = .success(rawResponse) case let .failure(moyaError): result = mapMoyaError(moyaError) + pluginResult = .failure(moyaError) + } + + plugins.forEach { + $0.didReceive(pluginResult, target: request) } case let .failure(moyaError): result = mapMoyaError(moyaError) diff --git a/TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DecodingErrorSummary.swift b/TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DecodingErrorSummary.swift new file mode 100644 index 00000000..41e15363 --- /dev/null +++ b/TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DecodingErrorSummary.swift @@ -0,0 +1,108 @@ +// +// 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 Foundation + +public struct DecodingErrorSummary: CustomStringConvertible { + public let url: String + public let requestMethod: String + public let statusCode: Int + public let codingPath: String + public let jsonValue: String + public let errorDescription: String + + public let formatter: DecodingErrorSummaryFormatter + + public var description: String { + formatter.format(summary: self) + } + + public init(decodingError: DecodingError, + response: Response, + formatter: DecodingErrorSummaryFormatter) { + + switch decodingError { + case let .typeMismatch(_, context): + self.init(context: context, response: response, formatter: formatter) + case let .dataCorrupted(context): + self.init(context: context, response: response, formatter: formatter) + case let .keyNotFound(_, context): + self.init(context: context, response: response, formatter: formatter) + case let .valueNotFound(_, context): + self.init(context: context, response: response, formatter: formatter) + @unknown default: + self.init(context: .init(codingPath: [], + debugDescription: ">>UNHANDLED DECODING ERROR CASE<<", + underlyingError: nil), + response: response, + formatter: formatter) + } + } + + public init(context: DecodingError.Context, + response: Response, + formatter: DecodingErrorSummaryFormatter) { + + self.url = response.request?.url?.relativePath ?? formatter.missingDataPlaceholder + self.statusCode = response.statusCode + self.requestMethod = response.request?.httpMethod ?? formatter.missingDataPlaceholder + self.codingPath = formatter.format(codingPath: context.codingPath) + self.errorDescription = context.debugDescription + self.jsonValue = formatter.format(jsonValue: context.extractJsonValue(from: response)) + + self.formatter = formatter + } +} + +private extension DecodingError.Context { + func extractJsonValue(from response: Response) -> Any? { + do { + let jsonObject = try JSONSerialization.jsonObject(with: response.data, options: .fragmentsAllowed) + + return value(forCodingPath: codingPath, in: jsonObject) + } catch { + return nil + } + } +} + +private func value(forCodingPath path: C, in obj: Any?) -> Any? where C.Element == CodingKey { + guard let part = path.first else { + return obj + } + + switch obj { + case let dict as [AnyHashable: Any]: + return value(forCodingPath: path.dropFirst(), in: dict[part.stringValue]) + case let array as [Any]: + guard let arrayIndex = part.intValue else { + return nil + } + + return value(forCodingPath: path.dropFirst(), in: array[arrayIndex]) + case let anyObj as AnyObject: + return value(forCodingPath: path.dropFirst(), in: anyObj.value(forKey: part.stringValue)) + default: + return nil + } +} diff --git a/TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DecodingErrorSummaryFormatter.swift b/TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DecodingErrorSummaryFormatter.swift new file mode 100644 index 00000000..82b10758 --- /dev/null +++ b/TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DecodingErrorSummaryFormatter.swift @@ -0,0 +1,61 @@ +// +// 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 DecodingErrorSummaryFormatter { + public var codingPathSeparator = "." + public var missingDataPlaceholder = "N/A" + + public init() {} + + open func format(summary: DecodingErrorSummary) -> String { + """ + \(summary.errorDescription) + + json_value: \(summary.jsonValue) + + json_path: \(summary.codingPath) + + \(summary.requestMethod) \(summary.statusCode) \(summary.url) + """ + } + + open func format(codingPath: [CodingKey]) -> String { + let formattedPath = codingPath.reduce(into: "") { + if let intValue = $1.intValue { + $0.append("[\(intValue)]") + } else { + $0.append(codingPathSeparator) + $0.append($1.stringValue) + } + } + + guard formattedPath.range(of: codingPathSeparator)?.lowerBound != formattedPath.startIndex else { + return String(formattedPath.dropFirst()) + } + + return formattedPath + } + + open func format(jsonValue: Any?) -> String { + String(reflecting: jsonValue ?? missingDataPlaceholder) + } +} diff --git a/TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DisplayDecodingErrorPlugin.swift b/TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DisplayDecodingErrorPlugin.swift new file mode 100644 index 00000000..23b8f3e4 --- /dev/null +++ b/TIMoyaNetworking/Sources/NetworkService/Plugins/DisplayDecodingError/DisplayDecodingErrorPlugin.swift @@ -0,0 +1,47 @@ +// +// 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 + +open class DisplayDecodingErrorPlugin: PluginType { + public var formatter: DecodingErrorSummaryFormatter + + public init(formatter: DecodingErrorSummaryFormatter = .init()) { + self.formatter = formatter + } + + open func didReceive(_ result: Result, target: TargetType) { + guard case let .failure(moyaError) = result else { + return + } + + guard case let .objectMapping(error as DecodingError, response) = moyaError else { + return + } + + display(summary: .init(decodingError: error, response: response, formatter: formatter)) + } + + open func display(summary: DecodingErrorSummary) { + debugPrint(summary.description) + } +} diff --git a/TIMoyaNetworking/TIMoyaNetworking.podspec b/TIMoyaNetworking/TIMoyaNetworking.podspec index bb755b15..cf732da7 100644 --- a/TIMoyaNetworking/TIMoyaNetworking.podspec +++ b/TIMoyaNetworking/TIMoyaNetworking.podspec @@ -1,13 +1,13 @@ Pod::Spec.new do |s| s.name = 'TIMoyaNetworking' - s.version = '1.12.3' + s.version = '1.13.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' } s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' } s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s } - s.ios.deployment_target = '10.0' + s.ios.deployment_target = '11.0' s.swift_versions = ['5.3'] s.source_files = s.name + '/**/Sources/**/*' diff --git a/TINetworking/TINetworking.podspec b/TINetworking/TINetworking.podspec index 44c60c41..9c884e99 100644 --- a/TINetworking/TINetworking.podspec +++ b/TINetworking/TINetworking.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TINetworking' - s.version = '1.12.3' + s.version = '1.13.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/TISwiftUtils/TISwiftUtils.podspec b/TISwiftUtils/TISwiftUtils.podspec index 108d05e3..0a54d842 100644 --- a/TISwiftUtils/TISwiftUtils.podspec +++ b/TISwiftUtils/TISwiftUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TISwiftUtils' - s.version = '1.12.3' + s.version = '1.13.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 806a6561..10a9fb51 100644 --- a/TITableKitUtils/TITableKitUtils.podspec +++ b/TITableKitUtils/TITableKitUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITableKitUtils' - s.version = '1.12.3' + s.version = '1.13.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 0c5acd19..9dc292ff 100644 --- a/TITransitions/TITransitions.podspec +++ b/TITransitions/TITransitions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITransitions' - s.version = '1.12.3' + s.version = '1.13.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 df5ea882..113370ce 100644 --- a/TIUIElements/TIUIElements.podspec +++ b/TIUIElements/TIUIElements.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIElements' - s.version = '1.12.3' + s.version = '1.13.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 aa202504..9aada394 100644 --- a/TIUIKitCore/TIUIKitCore.podspec +++ b/TIUIKitCore/TIUIKitCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIKitCore' - s.version = '1.12.3' + s.version = '1.13.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' }