diff --git a/CHANGELOG.md b/CHANGELOG.md index a52ab4d..efa47ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.1.1 + +- **Add**: `acceptableStatusCodes` property in `DefaultNetworkService`. +- **Add**: `retry(retryLimit:canRetryClosure:)` to `Observable` extension. +- **Removed**: `retryWithinErrors` method from `Observable` extension. +- **Update**: LeadKit to `0.6.x` version +- **Removed**: `ConnectionError`. (Replaced by `LeadKit.RequestError`) +- **Removed**: `handleConnectionErrors` from Observable+Extensions + + ## 0.1.0 - **Add**: support for Swift 3.2 / 4 diff --git a/LeadKitAdditions.podspec b/LeadKitAdditions.podspec index f3a6ccf..b65c5c3 100644 --- a/LeadKitAdditions.podspec +++ b/LeadKitAdditions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKitAdditions" - s.version = "0.1.0" + s.version = "0.1.1" s.summary = "iOS framework with a bunch of tools for rapid development" s.homepage = "https://github.com/TouchInstinct/LeadKitAdditions" s.license = "Apache License, Version 2.0" @@ -16,7 +16,7 @@ Pod::Spec.new do |s| "LeadKitAdditions/Sources/Services/Network/DefaultNetworkService+ActivityIndicator+Extension.swift", ] - ss.dependency "LeadKit", '0.6.0' + ss.dependency "LeadKit", '~> 0.6.0' ss.dependency "KeychainAccess", '3.1.0' ss.dependency "IDZSwiftCommonCrypto", '0.9.1' ss.dependency "InputMask", '3.0.0' @@ -31,7 +31,7 @@ Pod::Spec.new do |s| "LeadKitAdditions/Sources/Services/Network/DefaultNetworkService+ActivityIndicator.swift", ] - ss.dependency "LeadKit/Core-iOS-Extension", '0.6.0' + ss.dependency "LeadKit/Core-iOS-Extension", '~> 0.6.0' ss.dependency "KeychainAccess", '3.1.0' ss.dependency "IDZSwiftCommonCrypto", '0.9.1' ss.dependency "InputMask", '3.0.0' diff --git a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj index e6fa317..e974905 100644 --- a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj +++ b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj @@ -36,8 +36,6 @@ ED0C34181F2906EC00FAE9FD /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E31F2906EC00FAE9FD /* ApiError.swift */; }; ED0C34191F2906EC00FAE9FD /* ApiErrorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E41F2906EC00FAE9FD /* ApiErrorProtocol.swift */; }; ED0C341A1F2906EC00FAE9FD /* ApiErrorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E41F2906EC00FAE9FD /* ApiErrorProtocol.swift */; }; - ED0C341B1F2906EC00FAE9FD /* ConnectionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E51F2906EC00FAE9FD /* ConnectionError.swift */; }; - ED0C341C1F2906EC00FAE9FD /* ConnectionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E51F2906EC00FAE9FD /* ConnectionError.swift */; }; ED0C341D1F2906EC00FAE9FD /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E71F2906EC00FAE9FD /* Observable+Extensions.swift */; }; ED0C341E1F2906EC00FAE9FD /* Observable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E71F2906EC00FAE9FD /* Observable+Extensions.swift */; }; ED0C341F1F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C33E81F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift */; }; @@ -108,7 +106,6 @@ ED0C33E11F2906EC00FAE9FD /* BasePassCodeViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePassCodeViewModel.swift; sourceTree = ""; }; ED0C33E31F2906EC00FAE9FD /* ApiError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiError.swift; sourceTree = ""; }; ED0C33E41F2906EC00FAE9FD /* ApiErrorProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiErrorProtocol.swift; sourceTree = ""; }; - ED0C33E51F2906EC00FAE9FD /* ConnectionError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionError.swift; sourceTree = ""; }; ED0C33E71F2906EC00FAE9FD /* Observable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Extensions.swift"; sourceTree = ""; }; ED0C33E81F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBarButtonItem+Extensions.swift"; sourceTree = ""; }; ED0C33E91F2906EC00FAE9FD /* UserDefaults+UserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+UserService.swift"; sourceTree = ""; }; @@ -263,7 +260,6 @@ children = ( ED0C33E31F2906EC00FAE9FD /* ApiError.swift */, ED0C33E41F2906EC00FAE9FD /* ApiErrorProtocol.swift */, - ED0C33E51F2906EC00FAE9FD /* ConnectionError.swift */, ); path = Enums; sourceTree = ""; @@ -583,7 +579,6 @@ ED0C343D1F2906EC00FAE9FD /* TouchIDService.swift in Sources */, ED0C343F1F2906EC00FAE9FD /* ValidationError.swift in Sources */, ED0C342F1F2906EC00FAE9FD /* BaseUserService.swift in Sources */, - ED0C341B1F2906EC00FAE9FD /* ConnectionError.swift in Sources */, ED0C34231F2906EC00FAE9FD /* CellFieldJumpingProtocol.swift in Sources */, ED0C34411F2906EC00FAE9FD /* ValidationItem.swift in Sources */, ED0C341F1F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */, @@ -626,7 +621,6 @@ ED0C343E1F2906EC00FAE9FD /* TouchIDService.swift in Sources */, ED0C34401F2906EC00FAE9FD /* ValidationError.swift in Sources */, ED0C34301F2906EC00FAE9FD /* BaseUserService.swift in Sources */, - ED0C341C1F2906EC00FAE9FD /* ConnectionError.swift in Sources */, ED0C34241F2906EC00FAE9FD /* CellFieldJumpingProtocol.swift in Sources */, ED0C34421F2906EC00FAE9FD /* ValidationItem.swift in Sources */, ED0C34201F2906EC00FAE9FD /* UIBarButtonItem+Extensions.swift in Sources */, diff --git a/LeadKitAdditions/Podfile b/LeadKitAdditions/Podfile index d16fd63..01d07e2 100644 --- a/LeadKitAdditions/Podfile +++ b/LeadKitAdditions/Podfile @@ -14,7 +14,7 @@ abstract_target 'LeadKitAdditions' do use_frameworks! - pod "LeadKit", '0.6.0' + pod "LeadKit", '~> 0.6.0' end target 'LeadKitAdditions iOS Extensions' do @@ -22,7 +22,7 @@ abstract_target 'LeadKitAdditions' do use_frameworks! - pod "LeadKit/Core-iOS-Extension", '0.6.0' + pod "LeadKit/Core-iOS-Extension", '~> 0.6.0' end end diff --git a/LeadKitAdditions/Podfile.lock b/LeadKitAdditions/Podfile.lock index a6d89a7..4c2a1b4 100644 --- a/LeadKitAdditions/Podfile.lock +++ b/LeadKitAdditions/Podfile.lock @@ -6,9 +6,9 @@ PODS: - IDZSwiftCommonCrypto (0.10.0) - InputMask (3.0.0) - KeychainAccess (3.1.0) - - LeadKit (0.6.0): - - LeadKit/Core (= 0.6.0) - - LeadKit/Core (0.6.0): + - LeadKit (0.6.5): + - LeadKit/Core (= 0.6.5) + - LeadKit/Core (0.6.5): - CocoaLumberjack/Swift (~> 3.3.0) - ObjectMapper (~> 3.0.0) - RxAlamofire (= 4.0.0) @@ -16,7 +16,7 @@ PODS: - RxSwift (= 4.0.0) - TableKit (~> 2.5.0) - UIScrollView-InfiniteScroll (~> 1.0.0) - - LeadKit/Core-iOS-Extension (0.6.0): + - LeadKit/Core-iOS-Extension (0.6.5): - CocoaLumberjack/Swift (~> 3.3.0) - ObjectMapper (~> 3.0.0) - RxAlamofire (= 4.0.0) @@ -39,8 +39,8 @@ DEPENDENCIES: - IDZSwiftCommonCrypto - InputMask (= 3.0.0) - KeychainAccess (= 3.1.0) - - LeadKit (= 0.6.0) - - LeadKit/Core-iOS-Extension (= 0.6.0) + - LeadKit (~> 0.6.0) + - LeadKit/Core-iOS-Extension (~> 0.6.0) - SwiftValidator (= 5.0.0) SPEC CHECKSUMS: @@ -49,7 +49,7 @@ SPEC CHECKSUMS: IDZSwiftCommonCrypto: 4eef2c46e262dfbcbc1fd76365e066336680ad7d InputMask: 37c273bde6705187d80cf0b4240cb42ea92096c3 KeychainAccess: 94c5540b32eabf7bc32bfb976a268e8ea05fd6da - LeadKit: a8b51716890f6d19e2b4e6e5217883d7834d4185 + LeadKit: 583c724f25852e40eebad8af5a945e101c282fde ObjectMapper: 92230db59bf8f341a5c3a3cf0b9fbdde3cf0d87f RxAlamofire: 6ea579ac53bf14cb4bc7049a3866e5a769989b1d RxCocoa: d62846ca96495d862fa4c59ea7d87e5031d7340e @@ -58,6 +58,6 @@ SPEC CHECKSUMS: TableKit: 42d4dff2944f273cdeec2ef6352064eb6a9a355b UIScrollView-InfiniteScroll: c132d6d5851daff229ab4a1060ccf70a05a051c9 -PODFILE CHECKSUM: 96dea11fb103a275758c4ef3e731c4cce206746d +PODFILE CHECKSUM: 9ba64f509b50aaf860b0df0c12cca7969aa50842 COCOAPODS: 1.3.1 diff --git a/LeadKitAdditions/Sources/Enums/ConnectionError.swift b/LeadKitAdditions/Sources/Enums/ConnectionError.swift deleted file mode 100644 index f6980c4..0000000 --- a/LeadKitAdditions/Sources/Enums/ConnectionError.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright (c) 2017 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 Foundation - -/// Describes "no connection to server" error -public enum ConnectionError: LocalizedError { - - case noConnection - -} diff --git a/LeadKitAdditions/Sources/Extensions/Observable+Extensions.swift b/LeadKitAdditions/Sources/Extensions/Observable+Extensions.swift index 6e51991..c6dbe09 100644 --- a/LeadKitAdditions/Sources/Extensions/Observable+Extensions.swift +++ b/LeadKitAdditions/Sources/Extensions/Observable+Extensions.swift @@ -23,53 +23,29 @@ import RxSwift import Alamofire import CocoaLumberjack +import LeadKit public typealias VoidBlock = () -> Void public extension Observable { - /// Handles connection errors during request - func handleConnectionErrors() -> Observable { - return observeOn(CurrentThreadScheduler.instance) + /// A closure that checks for "retryable" error + typealias CanRetryClosure = (Error) -> Bool - // handle no internet connection - .do(onError: { error in - if let urlError = error as? URLError, - urlError.code == .notConnectedToInternet || - urlError.code == .timedOut { - DDLogError("Error: No Connection") - throw ConnectionError.noConnection - } - }) - - // handle unacceptable http status code like "500 Internal Server Error" and others - .do(onError: { error in - if let afError = error as? AFError, - case let .responseValidationFailed(reason: reason) = afError, - case let .unacceptableStatusCode(code: statusCode) = reason { - DDLogError("Error: Unacceptable HTTP Status Code - \(statusCode)") - throw ConnectionError.noConnection - } - }) - } - - /** - Allow to configure request to restart if error occured - - - parameters: - - errorTypes: list of error types, which triggers request restart - - retryLimit: how many times request can restarts - */ - func retryWithinErrors(_ errorTypes: [Error.Type] = [ConnectionError.self], - retryLimit: UInt = DefaultNetworkService.retryLimit) - -> Observable { + /// Allow to configure request to restart if error occured + /// + /// - Parameters: + /// - retryLimit: how many times request can restarts + /// - canRetryClosure: a closure that checks for "retryable" error + /// - Returns: An observable sequence producing the elements of the given sequence repeatedly + /// until it terminates successfully or is notified to error or complete. + func retry(retryLimit: UInt = DefaultNetworkService.retryLimit, + canRetryClosure: @escaping CanRetryClosure) -> Observable { return observeOn(CurrentThreadScheduler.instance) - .retryWhen { errors -> Observable in - return errors.enumerated().flatMap { attempt, error -> Observable in - let canRetry = errorTypes.contains { type(of: error) == $0 } - - return (canRetry && attempt < retryLimit - 1) ? self : .error(error) + .retryWhen { errorsObservable -> Observable in + return errorsObservable.enumerated().flatMap { + (canRetryClosure($1) && $0 < retryLimit - 1) ? self : .error($1) } } } diff --git a/LeadKitAdditions/Sources/Services/Network/ApiNetworkService.swift b/LeadKitAdditions/Sources/Services/Network/ApiNetworkService.swift index 762a737..2293906 100644 --- a/LeadKitAdditions/Sources/Services/Network/ApiNetworkService.swift +++ b/LeadKitAdditions/Sources/Services/Network/ApiNetworkService.swift @@ -33,7 +33,6 @@ open class ApiNetworkService: DefaultNetworkService { let apiResponseRequest = rxRequest(with: parameters) as Observable<(response: HTTPURLResponse, model: ApiResponse)> return apiResponseRequest - .handleConnectionErrors() .map { if $0.model.errorCode == 0 { return try T(JSON: try cast($0.model.result) as [String: Any]) @@ -48,7 +47,6 @@ open class ApiNetworkService: DefaultNetworkService { let apiResponseRequest = rxRequest(with: parameters) as Observable<(response: HTTPURLResponse, model: ApiResponse)> return apiResponseRequest - .handleConnectionErrors() .map { if $0.model.errorCode == 0, let result = $0.model.result as? Bool { diff --git a/LeadKitAdditions/Sources/Services/Network/DefaultNetworkService.swift b/LeadKitAdditions/Sources/Services/Network/DefaultNetworkService.swift index be13373..54d8c2f 100644 --- a/LeadKitAdditions/Sources/Services/Network/DefaultNetworkService.swift +++ b/LeadKitAdditions/Sources/Services/Network/DefaultNetworkService.swift @@ -43,10 +43,18 @@ open class DefaultNetworkService: NetworkService { return 20.0 } - public override init(sessionManager: SessionManager) { - super.init(sessionManager: sessionManager) + /// The default acceptable range 200…299 + open var acceptableStatusCodes: [Int] { + return Alamofire.SessionManager.defaultAcceptableStatusCodes + } - activityIndicatorBinding()?.disposed(by: disposeBag) + public init(sessionManager: SessionManager) { + super.init(sessionManager: sessionManager, acceptableStatusCodes: acceptableStatusCodes) + + // Fatal error: `drive*` family of methods can be only called from `MainThread` + DispatchQueue.main.async { + self.activityIndicatorBinding()?.disposed(by: self.disposeBag) + } } // MARK: - Default Values