diff --git a/.swift-version b/.swift-version deleted file mode 100644 index 819e07a2..00000000 --- a/.swift-version +++ /dev/null @@ -1 +0,0 @@ -5.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 13694890..69c56b9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,17 @@ # Changelog -### 0.9.45 +### 0.10.2 - **Add**: `RefreshControl` - a basic UIRefreshControl with fixed refresh action. +### 0.10.1 +- **Update**: Third party dependencies: `Alamofire` 5.2.2, `RxAlamofire` 5.6.1 + +### 0.10.0 +- **Update**: Third party dependencies: `RxSwift` (and all sub-dependencies) to 5.1.0, `Alamofire` 5.0, `SnapKit` 5.0 +- **Refactored**: NetworkManager to use new Alamofire API +- **API BreakingChanges**: NetworkServiceConfiguration no longer accepts `ServerTrustPolicy`, it is now replaced by an instance of a `ServerTrustEvaluating` protocol. Full description and default implementations can be found at Alamofire [sources](https://github.com/Alamofire/Alamofire/blob/master/Source/ServerTrustEvaluation.swift). Since new evaluation is used, evaluation against self-signed certificates will now throw an AfError and abort any outcoming request. To support self-signed certificates use `DisabledTrustEvaluator` for specified host in configuration. +- **Removed**: UIImage+SupportExtensions, UIScrollView+Support + ### 0.9.44 - **Add**: `TIFoundationUtils` - set of helpers for Foundation framework classes. diff --git a/Cartfile b/Cartfile index 47fe345f..d85cec25 100644 --- a/Cartfile +++ b/Cartfile @@ -1,7 +1,7 @@ github "malcommac/SwiftDate" github "Alamofire/Alamofire" -github "RxSwiftCommunity/RxAlamofire" ~> 4.5 +github "RxSwiftCommunity/RxAlamofire" ~> 5.6.0 github "TouchInstinct/TableKit" -github "ReactiveX/RxSwift" ~> 4.5 +github "ReactiveX/RxSwift" ~> 5.1.0 github "pronebird/UIScrollView-InfiniteScroll" -github "SnapKit/SnapKit" ~> 4.2 \ No newline at end of file +github "SnapKit/SnapKit" ~> 5.0 \ No newline at end of file diff --git a/Cartfile.resolved b/Cartfile.resolved index 5e06585c..0653077e 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,7 +1,7 @@ -github "Alamofire/Alamofire" "4.9.1" -github "ReactiveX/RxSwift" "4.5.0" -github "RxSwiftCommunity/RxAlamofire" "4.5.0" -github "SnapKit/SnapKit" "4.2.0" +github "Alamofire/Alamofire" "5.2.2" +github "ReactiveX/RxSwift" "5.1.1" +github "RxSwiftCommunity/RxAlamofire" "v5.6.1" +github "SnapKit/SnapKit" "5.0.1" github "TouchInstinct/TableKit" "2.10008.1" github "malcommac/SwiftDate" "6.1.0" github "pronebird/UIScrollView-InfiniteScroll" "1.1.0" diff --git a/LeadKit.podspec b/LeadKit.podspec index d9405e74..f2c5d9de 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -1,12 +1,13 @@ Pod::Spec.new do |s| s.name = "LeadKit" - s.version = "0.9.45" + s.version = "0.10.2" 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" s.author = "Touch Instinct" s.source = { :git => "https://github.com/TouchInstinct/LeadKit.git", :tag => s.version } - s.platform = :ios, '9.0' + s.platform = :ios, '10.0' + s.swift_versions = ['5.0'] s.subspec 'UIColorHex' do |ss| ss.ios.deployment_target = '8.0' @@ -17,9 +18,9 @@ Pod::Spec.new do |s| end s.subspec 'Core' do |ss| - ss.ios.deployment_target = '9.0' - ss.tvos.deployment_target = '9.0' - ss.watchos.deployment_target = '2.0' + ss.ios.deployment_target = '10.0' + ss.tvos.deployment_target = '10.0' + ss.watchos.deployment_target = '3.0' ss.source_files = "Sources/**/*.swift" ss.watchos.exclude_files = [ @@ -43,7 +44,6 @@ Pod::Spec.new do |s| "Sources/Extensions/NetworkService/NetworkService+RxLoadImage.swift", "Sources/Extensions/DataLoading/GeneralDataLoading/GeneralDataLoadingController+DefaultImplementation.swift", "Sources/Extensions/DataLoading/PaginationDataLoading/*", - "Sources/Extensions/Support/UIScrollView+Support.swift", "Sources/Extensions/Support/UINavigationItem+Support.swift", "Sources/Extensions/TableKit/**/*.swift", "Sources/Extensions/Array/Array+SeparatorRowBoxExtensions.swift", @@ -80,7 +80,6 @@ Pod::Spec.new do |s| "Sources/Structures/Drawing/CALayerDrawingOperation.swift", "Sources/Enums/Search/*", "Sources/Extensions/DataLoading/PaginationDataLoading/*", - "Sources/Extensions/Support/UIScrollView+Support.swift", "Sources/Extensions/Support/UINavigationItem+Support.swift", "Sources/Extensions/TableKit/**/*.swift", "Sources/Extensions/Array/Array+SeparatorRowBoxExtensions.swift", @@ -93,13 +92,13 @@ Pod::Spec.new do |s| "Sources/Structures/DataLoading/PaginationDataLoading/*" ] - ss.dependency "RxSwift", '~> 4' - ss.dependency "RxCocoa", '~> 4' - ss.dependency "RxAlamofire", '~> 4' + ss.dependency "RxSwift", '~> 5.1.0' + ss.dependency "RxCocoa", '~> 5.1.0' + ss.dependency "RxAlamofire", '~> 5.6.0' ss.dependency "SwiftDate", '~> 6' ss.ios.dependency "TableKit", '~> 2.8' - ss.ios.dependency "SnapKit", '~> 4.0.0' + ss.ios.dependency "SnapKit", '~> 5.0.0' ss.ios.dependency "UIScrollView-InfiniteScroll", '~> 1.1.0' end diff --git a/LeadKit.xcodeproj/project.pbxproj b/LeadKit.xcodeproj/project.pbxproj index 66fb2a14..e040f794 100644 --- a/LeadKit.xcodeproj/project.pbxproj +++ b/LeadKit.xcodeproj/project.pbxproj @@ -95,7 +95,6 @@ 671462C81EB3396E00EAB194 /* String+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461FC1EB3396E00EAB194 /* String+Localization.swift */; }; 671462CA1EB3396E00EAB194 /* String+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461FC1EB3396E00EAB194 /* String+Localization.swift */; }; 671462CB1EB3396E00EAB194 /* String+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461FC1EB3396E00EAB194 /* String+Localization.swift */; }; - 671462D01EB3396E00EAB194 /* UIScrollView+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671461FF1EB3396E00EAB194 /* UIScrollView+Support.swift */; }; 671462D41EB3396E00EAB194 /* TableDirector+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671462011EB3396E00EAB194 /* TableDirector+Extensions.swift */; }; 671462D81EB3396E00EAB194 /* TimeInterval+DateComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671462031EB3396E00EAB194 /* TimeInterval+DateComponents.swift */; }; 671462DA1EB3396E00EAB194 /* TimeInterval+DateComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671462031EB3396E00EAB194 /* TimeInterval+DateComponents.swift */; }; @@ -105,8 +104,6 @@ 671462E71EB3396E00EAB194 /* UIColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671462091EB3396E00EAB194 /* UIColor+Hex.swift */; }; 671462EC1EB3396E00EAB194 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6714620D1EB3396E00EAB194 /* UIImage+Extensions.swift */; }; 671462EF1EB3396E00EAB194 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6714620D1EB3396E00EAB194 /* UIImage+Extensions.swift */; }; - 671462F01EB3396E00EAB194 /* UIImage+SupportExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6714620E1EB3396E00EAB194 /* UIImage+SupportExtensions.swift */; }; - 671462F31EB3396E00EAB194 /* UIImage+SupportExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6714620E1EB3396E00EAB194 /* UIImage+SupportExtensions.swift */; }; 671462FC1EB3396E00EAB194 /* UIView+XibNameProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671462131EB3396E00EAB194 /* UIView+XibNameProtocol.swift */; }; 671462FF1EB3396E00EAB194 /* UIView+XibNameProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671462131EB3396E00EAB194 /* UIView+XibNameProtocol.swift */; }; 671463001EB3396E00EAB194 /* UIView+LoadFromNib.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671462141EB3396E00EAB194 /* UIView+LoadFromNib.swift */; }; @@ -589,12 +586,10 @@ 671461F11EB3396E00EAB194 /* Observable+DeferredJust.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+DeferredJust.swift"; sourceTree = ""; }; 671461F61EB3396E00EAB194 /* Sequence+ConcurrentMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Sequence+ConcurrentMap.swift"; sourceTree = ""; }; 671461FC1EB3396E00EAB194 /* String+Localization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Localization.swift"; sourceTree = ""; }; - 671461FF1EB3396E00EAB194 /* UIScrollView+Support.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIScrollView+Support.swift"; sourceTree = ""; }; 671462011EB3396E00EAB194 /* TableDirector+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TableDirector+Extensions.swift"; sourceTree = ""; }; 671462031EB3396E00EAB194 /* TimeInterval+DateComponents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TimeInterval+DateComponents.swift"; sourceTree = ""; }; 671462091EB3396E00EAB194 /* UIColor+Hex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Hex.swift"; sourceTree = ""; }; 6714620D1EB3396E00EAB194 /* UIImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = ""; }; - 6714620E1EB3396E00EAB194 /* UIImage+SupportExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+SupportExtensions.swift"; sourceTree = ""; }; 671462131EB3396E00EAB194 /* UIView+XibNameProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+XibNameProtocol.swift"; sourceTree = ""; }; 671462141EB3396E00EAB194 /* UIView+LoadFromNib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+LoadFromNib.swift"; sourceTree = ""; }; 671462151EB3396E00EAB194 /* UIView+LoadingIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+LoadingIndicator.swift"; sourceTree = ""; }; @@ -1110,7 +1105,6 @@ isa = PBXGroup; children = ( 7295474121E6628C009558E7 /* UINavigationItem+Support.swift */, - 671461FF1EB3396E00EAB194 /* UIScrollView+Support.swift */, ); path = Support; sourceTree = ""; @@ -1145,7 +1139,6 @@ isa = PBXGroup; children = ( 6714620D1EB3396E00EAB194 /* UIImage+Extensions.swift */, - 6714620E1EB3396E00EAB194 /* UIImage+SupportExtensions.swift */, ); path = UIImage; sourceTree = ""; @@ -2470,7 +2463,6 @@ 671462841EB3396E00EAB194 /* CGContext+Initializers.swift in Sources */, EFBE57DB1EC361620040E00A /* UIView+Layout.swift in Sources */, 6714634C1EB3396E00EAB194 /* ReuseIdentifierProtocol.swift in Sources */, - 671462F01EB3396E00EAB194 /* UIImage+SupportExtensions.swift in Sources */, 6741CEAF20E242A500FEC4D9 /* TableViewHolder+ScrollViewHolder.swift in Sources */, 67CAF8C620652E2A00527085 /* TextFieldViewModel.swift in Sources */, 671462681EB3396E00EAB194 /* NetworkService.swift in Sources */, @@ -2493,7 +2485,6 @@ 72005A1F2266226800ECE090 /* CustomizableButtonViewModel.swift in Sources */, 677B06C4211884F3006C947D /* BaseTextAttributes.swift in Sources */, 675E0AA921072FF400CDC143 /* BaseScrollContentController.swift in Sources */, - 671462D01EB3396E00EAB194 /* UIScrollView+Support.swift in Sources */, 671463901EB3396E00EAB194 /* TemplateDrawingOperation.swift in Sources */, A658E54D1F8CD7790093527A /* TableRow+SeparatorsExtensions.swift in Sources */, 85A5D49522AA975000C7D254 /* Decimal+Rounding.swift in Sources */, @@ -2814,7 +2805,6 @@ 671463631EB3396E00EAB194 /* SupportProtocol.swift in Sources */, 671462871EB3396E00EAB194 /* CGContext+Initializers.swift in Sources */, 6714634F1EB3396E00EAB194 /* ReuseIdentifierProtocol.swift in Sources */, - 671462F31EB3396E00EAB194 /* UIImage+SupportExtensions.swift in Sources */, 6714626B1EB3396E00EAB194 /* NetworkService.swift in Sources */, 67E352612119B7570035BDDB /* BasePlaceholerView.swift in Sources */, 673CF43A2063E7CE00C329F6 /* GeneralDataLoadingController+DefaultImplementation.swift in Sources */, diff --git a/Sources/Classes/DataLoading/PaginationDataLoading/PaginationWrapper.swift b/Sources/Classes/DataLoading/PaginationDataLoading/PaginationWrapper.swift index e1ecbed9..c58bee3f 100644 --- a/Sources/Classes/DataLoading/PaginationDataLoading/PaginationWrapper.swift +++ b/Sources/Classes/DataLoading/PaginationDataLoading/PaginationWrapper.swift @@ -162,7 +162,7 @@ final public class PaginationWrapper: GeneralDataLoadingViewModel } open var searchDebounceInterval: RxTimeInterval { - return 1 + return .seconds(1) } open var searchResultsDriver: Driver<[ItemViewModel]> { diff --git a/Sources/Classes/Services/NetworkService.swift b/Sources/Classes/Services/NetworkService.swift index 10cb8f8e..cd36c7b5 100644 --- a/Sources/Classes/Services/NetworkService.swift +++ b/Sources/Classes/Services/NetworkService.swift @@ -144,7 +144,7 @@ public extension Observable { /// /// - Parameter networkService: NetworkService to operate on it /// - Returns: The source sequence with the side-effecting behavior applied. - func counterTracking(for networkService: NetworkService) -> Observable { + func counterTracking(for networkService: NetworkService) -> Observable { return `do`(onSubscribe: { networkService.increaseRequestCounter() }, onDispose: { diff --git a/Sources/Classes/Services/SessionManager.swift b/Sources/Classes/Services/SessionManager.swift index d4d7bee8..9a1ed574 100644 --- a/Sources/Classes/Services/SessionManager.swift +++ b/Sources/Classes/Services/SessionManager.swift @@ -23,7 +23,7 @@ import Alamofire /// Session Manager stored in NetworkService -open class SessionManager: Alamofire.SessionManager { +open class SessionManager: Alamofire.Session { /// Response with HTTP URL Response and target object public typealias ModelResponse = (response: HTTPURLResponse, model: T) @@ -38,25 +38,40 @@ open class SessionManager: Alamofire.SessionManager { public let mappingQueue: DispatchQueue public init(configuration: URLSessionConfiguration, - serverTrustPolicyManager: ServerTrustPolicyManager, + serverTrustManager: ServerTrustManager, acceptableStatusCodes: Set, mappingQueue: DispatchQueue) { self.acceptableStatusCodes = acceptableStatusCodes self.mappingQueue = mappingQueue - super.init(configuration: configuration, serverTrustPolicyManager: serverTrustPolicyManager) + let delegate = SessionDelegate() + + let delegateQueue = OperationQueue() + delegateQueue.underlyingQueue = mappingQueue + + let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: delegateQueue) + + super.init(session: session, + delegate: delegate, + rootQueue: mappingQueue, + serverTrustManager: serverTrustManager) } - public init?(session: URLSession, - delegate: SessionDelegate, - serverTrustPolicyManager: ServerTrustPolicyManager, - acceptableStatusCodes: Set, - mappingQueue: DispatchQueue) { + public init(session: URLSession, + delegate: SessionDelegate, + serverTrustManager: ServerTrustManager, + acceptableStatusCodes: Set, + mappingQueue: DispatchQueue) { self.acceptableStatusCodes = acceptableStatusCodes self.mappingQueue = mappingQueue - super.init(session: session, delegate: delegate, serverTrustPolicyManager: serverTrustPolicyManager) + session.delegateQueue.underlyingQueue = mappingQueue + + super.init(session: session, + delegate: delegate, + rootQueue: mappingQueue, + serverTrustManager: serverTrustManager) } } diff --git a/Sources/Classes/Views/TextField/DataModelFieldBinding.swift b/Sources/Classes/Views/TextField/DataModelFieldBinding.swift index c91a03a1..53ea5cbc 100644 --- a/Sources/Classes/Views/TextField/DataModelFieldBinding.swift +++ b/Sources/Classes/Views/TextField/DataModelFieldBinding.swift @@ -109,8 +109,9 @@ public extension BehaviorRelay { /// - getFieldClosure: Closure for getting field string reprerentation from data model. /// - mergeFieldClosure: Closure for merging new field value into data model. /// - Returns: DataModelFieldBinding instance. - func fieldBinding(getFieldClosure: @escaping DataModelFieldBinding.GetFieldClosure, - mergeFieldClosure: @escaping DataModelFieldBinding.MergeFieldClosure) -> DataModelFieldBinding { + func fieldBinding(getFieldClosure: @escaping DataModelFieldBinding.GetFieldClosure, + mergeFieldClosure: @escaping DataModelFieldBinding.MergeFieldClosure) + -> DataModelFieldBinding { return DataModelFieldBinding(modelRelay: self, getFieldClosure: getFieldClosure, @@ -123,7 +124,7 @@ public extension BehaviorRelay where Element == String? { /// Creates DataModelFieldBinding configured with behaviour relay itself. /// /// - Returns: DataModelFieldBinding instance. - func fieldBinding() -> DataModelFieldBinding { + func fieldBinding() -> DataModelFieldBinding { return DataModelFieldBinding(modelRelay: self) } } diff --git a/Sources/Extensions/Alamofire/DataRequest+Extensions.swift b/Sources/Extensions/Alamofire/DataRequest+Extensions.swift index 22fdd5c7..64ab85d3 100644 --- a/Sources/Extensions/Alamofire/DataRequest+Extensions.swift +++ b/Sources/Extensions/Alamofire/DataRequest+Extensions.swift @@ -70,25 +70,25 @@ public extension Reactive where Base: DataRequest { } private func response(onQueue queue: DispatchQueue) -> Observable<(HTTPURLResponse, Data)> { - return responseResult(queue: queue, responseSerializer: DataRequest.dataResponseSerializer()) + return responseResult(queue: queue, responseSerializer: DataResponseSerializer()) } } -public extension ObservableType where E == DataRequest { +public extension ObservableType where Element == DataRequest { /// Method that validates status codes and catch network errors /// /// - Parameter statusCodes: set of status codes to validate /// - Returns: Observable on self - func validate(statusCodes: Set) -> Observable { + func validate(statusCodes: Set) -> Observable { return map { $0.validate(statusCode: statusCodes) } .catchAsRequestError() } } -private extension ObservableType where E == ServerResponse { +private extension ObservableType where Element == ServerResponse { - func tryMapResult(_ transform: @escaping (E) throws -> R) -> Observable { + func tryMapResult(_ transform: @escaping (Element) throws -> R) -> Observable { return map { do { return try transform($0) @@ -98,7 +98,7 @@ private extension ObservableType where E == ServerResponse { } } - func tryMapObservableResult(_ transform: @escaping (E) throws -> Observable) -> Observable { + func tryMapObservableResult(_ transform: @escaping (Element) throws -> Observable) -> Observable { return flatMap { response, result -> Observable in do { return try transform((response, result)) @@ -114,10 +114,10 @@ private extension ObservableType where E == ServerResponse { private extension ObservableType { - func catchAsRequestError(with request: DataRequest? = nil) -> Observable { + func catchAsRequestError(with request: DataRequest? = nil) -> Observable { return catchError { error in let resultError: RequestError - let response = request?.delegate.data + let response = request?.data switch error { case let requestError as RequestError: diff --git a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift index cc4816d8..fcaa7f86 100644 --- a/Sources/Extensions/Alamofire/SessionManager+Extensions.swift +++ b/Sources/Extensions/Alamofire/SessionManager+Extensions.swift @@ -50,7 +50,7 @@ public extension Reactive where Base: SessionManager { _ url: URLConvertible, parameters: [Any]? = nil, encoding: JSONEncoding = .default, - headers: [String: String]? = nil) + headers: HTTPHeaders? = nil) -> Observable { return Observable.deferred { diff --git a/Sources/Extensions/Drawing/UIImage/UIImage+Extensions.swift b/Sources/Extensions/Drawing/UIImage/UIImage+Extensions.swift index 20f7a487..8992b89b 100644 --- a/Sources/Extensions/Drawing/UIImage/UIImage+Extensions.swift +++ b/Sources/Extensions/Drawing/UIImage/UIImage+Extensions.swift @@ -22,7 +22,6 @@ import UIKit -@available(iOS 10.0, tvOS 10.0, *) public extension UIImage { /// Creates an image filled by given color. @@ -235,7 +234,6 @@ public extension UIImage { } } -@available(iOS 10.0, tvOS 10.0, *) internal extension DrawingOperation { func imageFromNewRenderer(scale: CGFloat) -> UIImage { diff --git a/Sources/Extensions/Drawing/UIImage/UIImage+SupportExtensions.swift b/Sources/Extensions/Drawing/UIImage/UIImage+SupportExtensions.swift deleted file mode 100644 index 73091608..00000000 --- a/Sources/Extensions/Drawing/UIImage/UIImage+SupportExtensions.swift +++ /dev/null @@ -1,283 +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 UIKit - -public extension Support where Base: UIImage { - - /// Creates an image filled by given color. - /// - /// - Parameters: - /// - color: The color to fill - /// - size: The size of an new image. - /// - Returns: A new instanse of UIImage with given size and color or nil if something goes wrong. - static func imageWith(color: UIColor, size: CGSize) -> Support? { - let width = Int(ceil(size.width)) - let height = Int(ceil(size.height)) - - let operation = SolidFillDrawingOperation(color: color.cgColor, width: width, height: height) - - return operation.imageFromNewContext(scale: UIScreen.main.scale)?.support - } - - /// Creates an image from a UIView. - /// - /// - Parameter fromView: The source view. - /// - Returns: A new instance of UIImage or nil if something goes wrong. - static func imageFrom(view: UIView) -> Support? { - let operation = CALayerDrawingOperation(layer: view.layer, size: view.bounds.size) - - guard let rotatedImage = operation.imageFromNewContext(scale: UIScreen.main.scale) else { - return nil - } - - let flipOperation = rotatedImage.cgImage?.flipYOperation(size: rotatedImage.size) - - return flipOperation?.imageFromNewContext(scale: rotatedImage.scale)?.support - } - - /// Render current template UIImage into new image using given color. - /// - /// - Parameter color: Color to fill template image. - /// - Returns: A new UIImage rendered with given color or nil if something goes wrong. - func renderTemplate(withColor color: UIColor) -> Support? { - return withCGImage { image in - let operation = TemplateDrawingOperation(image: image, - imageSize: base.size, - color: color.cgColor) - - guard let templateImage = operation.imageFromNewContext(scale: base.scale) else { - return nil - } - - let flipOperation = templateImage.cgImage?.flipYOperation(size: templateImage.size) - - return flipOperation?.imageFromNewContext(scale: templateImage.scale) - } - } - - /// Creates a new image with rounded corners and border. - /// - /// - Parameters: - /// - cornerRadius: The corner radius. - /// - borderWidth: The size of the border. - /// - color: The color of the border. - /// - extendSize: Extend result image size and don't overlap source image by border. - /// - Returns: A new image with rounded corners or nil if something goes wrong. - func roundCorners(cornerRadius: CGFloat, - borderWidth: CGFloat, - color: UIColor, - extendSize: Bool = false) -> Support? { - - return withCGImage { image in - let roundOperation = RoundDrawingOperation(image: image, - imageSize: base.size, - radius: cornerRadius) - - guard let roundImage = roundOperation.cgImageFromNewContext(scale: base.scale) else { - return nil - } - - let borderOperation = BorderDrawingOperation(image: roundImage, - imageSize: base.size, - border: borderWidth, - color: color.cgColor, - radius: cornerRadius, - extendSize: extendSize) - - return borderOperation.imageFromNewContext(scale: base.scale) - } - } - - /// Creates a new circle image. - /// - /// - Returns: A new circled image or nil if something goes wrong. - func roundCornersToCircle() -> Support? { - return withCGImage { image in - let radius = CGFloat(min(base.size.width, base.size.height) / 2) - - let operation = RoundDrawingOperation(image: image, - imageSize: base.size, - radius: radius) - - return operation.imageFromNewContext(scale: base.scale) - } - } - - /// Creates a new circle image with a border. - /// - /// - Parameters: - /// - borderWidth: The size of the border. - /// - borderColor: The color of the border. - /// - extendSize: Extend result image size and don't overlap source image by border (default = false). - /// - Returns: A new image with rounded corners or nil if something goes wrong. - func roundCornersToCircle(borderWidth: CGFloat, - borderColor: UIColor, - extendSize: Bool = false) -> Support? { - - return withCGImage { image in - let radius = CGFloat(min(base.size.width, base.size.height) / 2) - - let roundOperation = RoundDrawingOperation(image: image, - imageSize: base.size, - radius: radius) - - guard let roundImage = roundOperation.cgImageFromNewContext(scale: base.scale) else { - return nil - } - - let borderOperation = BorderDrawingOperation(image: roundImage, - imageSize: base.size, - border: borderWidth, - color: borderColor.cgColor, - radius: radius, - extendSize: extendSize) - - return borderOperation.imageFromNewContext(scale: base.scale) - } - } - - /// Creates a resized copy of an image. - /// - /// - Parameters: - /// - newSize: The new size of the image. - /// - contentMode: The way to handle the content in the new size. - /// - cropToImageBounds: Should output image size match resized image size. - /// Note: If passed true with ResizeMode.scaleAspectFit content mode it will give the original image. - /// - Returns: A new image scaled to new size. - func resize(newSize: CGSize, - contentMode: ResizeMode = .scaleToFill, - cropToImageBounds: Bool = false) -> Support? { - - return withCGImage { image in - let operation = ResizeDrawingOperation(image: image, - imageSize: base.size, - preferredNewSize: newSize, - resizeMode: contentMode, - cropToImageBounds: cropToImageBounds) - - return operation.imageFromNewContext(scale: base.scale) - } - } - - /// Adds an alpha channel if UIImage doesn't already have one. - /// - /// - Returns: A copy of the given image, adding an alpha channel if it doesn't already have one. - func applyAlpha() -> Support? { - return withCGImage { image in - let operation = ImageDrawingOperation(image: image, - newSize: base.size, - opaque: false) - - return operation.imageFromNewContext(scale: base.scale) - } - } - - /// Creates a copy of the image with border of the given size added around its edges. - /// - /// - Parameter padding: The padding amount. - /// - Returns: A new padded image or nil if something goes wrong. - func applyPadding(_ padding: CGFloat) -> Support? { - return withCGImage { image in - let operation = PaddingDrawingOperation(image: image, - imageSize: base.size, - padding: padding) - - return operation.imageFromNewContext(scale: base.scale) - } - } - - /// Creates a copy of the image rotated by the given amount of degrees. - /// - /// - Parameters: - /// - degrees: The number of degrees. - /// - clockwise: Should rotate image clockwise. - /// - Returns: A new rotated image or nil if something goes wrong. - func rotate(degrees: CGFloat, clockwise: Bool = true) -> Support? { - return withCGImage { image in - let radians = degrees.degreesToRadians() - - let operation = RotateDrawingOperation(image: image, - imageSize: base.size, - radians: radians, - clockwise: clockwise) - - guard let rotatedImage = operation.imageFromNewContext(scale: base.scale) else { - return nil - } - - let flipOperation = rotatedImage.cgImage?.flipYOperation(size: rotatedImage.size) - - return flipOperation?.imageFromNewContext(scale: rotatedImage.scale) - } - } - - private func withCGImage(_ actionClosure: (CGImage) -> UIImage?) -> Support? { - guard let image = base.cgImage else { - return Support(base) - } - - return actionClosure(image)?.support - } -} - -private extension CGImage { - - func flipYOperation(size: CGSize) -> ImageDrawingOperation { - return ImageDrawingOperation(image: self, - newSize: size, - origin: .zero, - opaque: false, - flipY: true) - } -} - -private extension DrawingOperation { - - func cgImageFromNewContext(scale: CGFloat) -> CGImage? { - let ctxSize = contextSize - - let intScale = Int(scale) - - let context = CGContext.create(width: ctxSize.width * intScale, - height: ctxSize.height * intScale, - bitmapInfo: opaque ? .opaqueBitmapInfo : .alphaBitmapInfo) - - guard let ctx = context else { - return nil - } - - ctx.scaleBy(x: scale, y: scale) - - apply(in: ctx) - - return ctx.makeImage() - } - - func imageFromNewContext(scale: CGFloat) -> UIImage? { - guard let image = cgImageFromNewContext(scale: scale) else { - return nil - } - - return UIImage(cgImage: image, scale: scale, orientation: .up) - } -} diff --git a/Sources/Extensions/Error/Error+NetworkExtensions.swift b/Sources/Extensions/Error/Error+NetworkExtensions.swift index 872a0508..36cd9b31 100644 --- a/Sources/Extensions/Error/Error+NetworkExtensions.swift +++ b/Sources/Extensions/Error/Error+NetworkExtensions.swift @@ -51,7 +51,7 @@ public extension ObservableType { /// - handler: closure that recieves serialized response /// - Returns: Observable on caller func handleMappingError(with decoder: JSONDecoder = JSONDecoder(), - handler: @escaping ParameterClosure) -> Observable { + handler: @escaping ParameterClosure) -> Observable { return self.do(onError: { error in guard let errorModel = try error.handleMappingError(with: decoder) as T? else { return diff --git a/Sources/Extensions/Rx/ObservableType/Observable+DeferredJust.swift b/Sources/Extensions/Rx/ObservableType/Observable+DeferredJust.swift index aabcc604..9abaa737 100644 --- a/Sources/Extensions/Rx/ObservableType/Observable+DeferredJust.swift +++ b/Sources/Extensions/Rx/ObservableType/Observable+DeferredJust.swift @@ -29,7 +29,7 @@ public extension ObservableType { /// - Parameter elementFactory: Element factory function to invoke for each observer /// that subscribes to the resulting sequence. /// - Returns: An observable sequence whose observers trigger an invocation of the given element factory function. - static func deferredJust(_ elementFactory: @escaping () throws -> E) -> Observable { + static func deferredJust(_ elementFactory: @escaping () throws -> Element) -> Observable { return .create { observer in do { observer.onNext(try elementFactory()) diff --git a/Sources/Extensions/Rx/ObservableType/ObservableType+Extensions.swift b/Sources/Extensions/Rx/ObservableType/ObservableType+Extensions.swift index 26e21936..4745cd1b 100644 --- a/Sources/Extensions/Rx/ObservableType/ObservableType+Extensions.swift +++ b/Sources/Extensions/Rx/ObservableType/ObservableType+Extensions.swift @@ -42,7 +42,7 @@ public extension ObservableType { /// Cast all emitted elements to optional type. /// /// - Returns: An observable sequence whose elements are equals to optional type of element. - func asOptional() -> Observable { + func asOptional() -> Observable { return map { $0 } } } diff --git a/Sources/Extensions/Sequence/Sequence+ConcurrentMap.swift b/Sources/Extensions/Sequence/Sequence+ConcurrentMap.swift index a6fcfff7..71843141 100644 --- a/Sources/Extensions/Sequence/Sequence+ConcurrentMap.swift +++ b/Sources/Extensions/Sequence/Sequence+ConcurrentMap.swift @@ -63,5 +63,6 @@ public extension Sequence { array.sorted { $0.idx < $1.idx } .flatMap { $0.results } } + .asObservable() } } diff --git a/Sources/Extensions/Support/UIScrollView+Support.swift b/Sources/Extensions/Support/UIScrollView+Support.swift deleted file mode 100644 index c72a387d..00000000 --- a/Sources/Extensions/Support/UIScrollView+Support.swift +++ /dev/null @@ -1,47 +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 UIKit - -public extension Support where Base: UIScrollView { - - var refreshControl: UIRefreshControl? { - if #available(iOS 10.0, *) { - return base.refreshControl - } else { - return base.subviews.first { $0 is UIRefreshControl } as? UIRefreshControl - } - } - - func setRefreshControl(_ newRefreshControl: UIRefreshControl?) { - if #available(iOS 10.0, *) { - base.refreshControl = newRefreshControl - } else { - if let newControl = newRefreshControl { - refreshControl?.removeFromSuperview() - base.addSubview(newControl) - } else { - refreshControl?.removeFromSuperview() - } - } - } -} diff --git a/Sources/Info-iOS.plist b/Sources/Info-iOS.plist index 24cd7b7e..ec0cc7b0 100644 --- a/Sources/Info-iOS.plist +++ b/Sources/Info-iOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.9.28 + $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift b/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift index 50c68e75..1bcac763 100644 --- a/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift +++ b/Sources/Structures/NetworkService/NetworkServiceConfiguration.swift @@ -37,7 +37,7 @@ public struct NetworkServiceConfiguration { public let additionalHttpHeaders: HTTPHeaders /// Server trust policies. - public var serverTrustPolicies: [String: ServerTrustPolicy] + public var serverTrustPolicies: [String: ServerTrustEvaluating] /// HTTP response status codes regarded as non-erroneous public var acceptableStatusCodes: Set = Set(200..<300) @@ -48,20 +48,19 @@ public struct NetworkServiceConfiguration { public init(baseUrl: String, timeoutInterval: TimeInterval = 20, encoding: ParameterEncoding = URLEncoding.default, - additionalHttpHeaders: HTTPHeaders = [:], - trustPolicies: [String: ServerTrustPolicy] = [:]) { + additionalHttpHeaders: [String: String] = [:], + trustPolicies: [String: ServerTrustEvaluating] = [:]) { self.baseUrl = baseUrl self.timeoutInterval = timeoutInterval self.encoding = encoding - self.additionalHttpHeaders = additionalHttpHeaders.merging(SessionManager.defaultHTTPHeaders) { current, _ in current } + self.additionalHttpHeaders = HTTPHeaders(additionalHttpHeaders) sessionConfiguration = URLSessionConfiguration.default sessionConfiguration.timeoutIntervalForResource = timeoutInterval sessionConfiguration.httpAdditionalHeaders = additionalHttpHeaders - let updatedPolicies = Dictionary(uniqueKeysWithValues: trustPolicies.map { ($0.key.asHost, $0.value) }) - serverTrustPolicies = trustPolicies.isEmpty ? [baseUrl.asHost: .disableEvaluation] : updatedPolicies + serverTrustPolicies = Dictionary(uniqueKeysWithValues: trustPolicies.map { ($0.key.asHost, $0.value) }) } } @@ -70,7 +69,8 @@ public extension NetworkServiceConfiguration { /// SessionManager constructed with given parameters (session configuration and trust policies) var sessionManager: SessionManager { return SessionManager(configuration: sessionConfiguration, - serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies), + serverTrustManager: ServerTrustManager(allHostsMustBeEvaluated: !serverTrustPolicies.isEmpty, + evaluators: serverTrustPolicies), acceptableStatusCodes: acceptableStatusCodes, mappingQueue: .global()) }