diff --git a/.github/workflows/pr_check.yml b/.github/workflows/pr_check.yml new file mode 100644 index 00000000..d88d747d --- /dev/null +++ b/.github/workflows/pr_check.yml @@ -0,0 +1,22 @@ +name: PR Check +on: [pull_request] +concurrency: + group: ${{ github.head_ref }} + cancel-in-progress: true +jobs: + pr_check: + runs-on: macOS-11 + if: github.event.pull_request.draft == false + steps: + - name: Checkout Project + uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: Danger + env: + DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: bundle exec danger + - name: Test + run: swift test + diff --git a/.gitignore b/.gitignore index f657dea3..8270a930 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ xcuserdata/ *.moved-aside *.xcuserstate *.xcscmblueprint +.DS_Store ## Obj-C/Swift specific *.hmap @@ -53,6 +54,9 @@ Pods/ Carthage/Build +# SwiftPM +.swiftpm/ + # fastlane # # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..860487ca --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.7.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index c2c3544a..d24b4a67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ## Main ##### New Features/Enhancements +- Add Swift Package Manager Support + [notbenoit](https://github.com/notbenoit) + [#73](https://github.com/Fueled/ios-utilities/pulls/73) - Add `ActionProtocol` - Add `AnyAction`, allowing to type-erase any actions represented by a `ActionProtocol` diff --git a/FueledUtils.podspec b/FueledUtils.podspec index 3966e47b..9d426985 100644 --- a/FueledUtils.podspec +++ b/FueledUtils.podspec @@ -2,7 +2,7 @@ Pod::Spec.new do |s| s.name = 'FueledUtils' - s.version = '3.0.0' + s.version = '3.1.0' s.summary = 'A collection of utilities used at Fueled' s.description = 'This is a collection of classes, extensions, methods and functions used within Fueled projects that aims at decomplexifying tasks that should be easy.' s.swift_version = '5' diff --git a/FueledUtils/Combine/Action.swift b/FueledUtils/Combine/Action.swift index ed38326c..fe42255f 100644 --- a/FueledUtils/Combine/Action.swift +++ b/FueledUtils/Combine/Action.swift @@ -14,6 +14,10 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsReactiveCommon) +import FueledUtilsCore +import FueledUtilsReactiveCommon +#endif @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) public final class Action { diff --git a/FueledUtils/Combine/ActionError.swift b/FueledUtils/Combine/ActionError.swift index f1cc075e..4783697c 100644 --- a/FueledUtils/Combine/ActionError.swift +++ b/FueledUtils/Combine/ActionError.swift @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(FueledUtilsReactiveCommon) +import FueledUtilsReactiveCommon +#endif + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) public enum ActionError: Swift.Error { case disabled diff --git a/FueledUtils/Combine/AnyCurrentValuePublisher.swift b/FueledUtils/Combine/AnyCurrentValuePublisher.swift index af94361b..4ad849af 100644 --- a/FueledUtils/Combine/AnyCurrentValuePublisher.swift +++ b/FueledUtils/Combine/AnyCurrentValuePublisher.swift @@ -14,6 +14,9 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsReactiveCommon) +import FueledUtilsReactiveCommon +#endif /// /// A type-erasing current value publisher. @@ -35,7 +38,7 @@ public struct AnyCurrentValuePublisher: CurrentVal self.receiveSubcriberClosure = { _ = $0.receive(value) } } - public init(_ publisher: CurrentValuePublisher) where CurrentValuePublisher.Output == Output, CurrentValuePublisher.Failure == Failure { + public init(_ publisher: Publisher) where Publisher.Output == Output, Publisher.Failure == Failure { self.valueGetter = { publisher.value } self.receiveSubcriberClosure = { publisher.receive(subscriber: $0) } } diff --git a/FueledUtils/Combine/CoalescingAction.swift b/FueledUtils/Combine/CoalescingAction.swift index 3139b2f1..57c70ec0 100644 --- a/FueledUtils/Combine/CoalescingAction.swift +++ b/FueledUtils/Combine/CoalescingAction.swift @@ -14,6 +14,10 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsReactiveCommon) +import FueledUtilsCore +import FueledUtilsReactiveCommon +#endif /// /// Similar to `Action`, except if the action is already executing, subsequent `apply()` call will not fail, diff --git a/FueledUtils/Combine/CombineExtensions+Cancellables.swift b/FueledUtils/Combine/CombineExtensions+Cancellables.swift index 0e73cd3c..7532e42d 100644 --- a/FueledUtils/Combine/CombineExtensions+Cancellables.swift +++ b/FueledUtils/Combine/CombineExtensions+Cancellables.swift @@ -14,6 +14,11 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsReactiveCommon) +import Foundation +import FueledUtilsCore +import FueledUtilsReactiveCommon +#endif private var cancellablesKey: UInt8 = 0 diff --git a/FueledUtils/Combine/ObservableObjectExtensions.swift b/FueledUtils/Combine/ObservableObjectExtensions.swift index ad2fe6e0..254772d9 100644 --- a/FueledUtils/Combine/ObservableObjectExtensions.swift +++ b/FueledUtils/Combine/ObservableObjectExtensions.swift @@ -14,6 +14,11 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsReactiveCommon) +import Foundation +import FueledUtilsCore +import FueledUtilsReactiveCommon +#endif @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) extension ObservableObject where Self.ObjectWillChangePublisher == ObservableObjectPublisher { diff --git a/FueledUtils/Combine/Publisher+TransferState.swift b/FueledUtils/Combine/Publisher+TransferState.swift index fa553bcb..2243e01c 100644 --- a/FueledUtils/Combine/Publisher+TransferState.swift +++ b/FueledUtils/Combine/Publisher+TransferState.swift @@ -14,6 +14,10 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsReactiveCommon) +import FueledUtilsCore +import FueledUtilsReactiveCommon +#endif @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) extension Publisher where Output: TransferStateProtocol { diff --git a/FueledUtils/Combine/PublisherExtensions.swift b/FueledUtils/Combine/PublisherExtensions.swift index 02e3cfa7..dc2bb976 100644 --- a/FueledUtils/Combine/PublisherExtensions.swift +++ b/FueledUtils/Combine/PublisherExtensions.swift @@ -14,6 +14,10 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsReactiveCommon) +import FueledUtilsCore +import FueledUtilsReactiveCommon +#endif @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) extension Publisher { diff --git a/FueledUtils/Combine/PublishersExtensions.swift b/FueledUtils/Combine/PublishersExtensions.swift index 3367e143..d1126ce7 100644 --- a/FueledUtils/Combine/PublishersExtensions.swift +++ b/FueledUtils/Combine/PublishersExtensions.swift @@ -14,6 +14,10 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsReactiveCommon) +import FueledUtilsCore +import FueledUtilsReactiveCommon +#endif @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) extension Publishers { diff --git a/FueledUtils/CombineOperators/Combine+Operators.swift b/FueledUtils/CombineOperators/Combine+Operators.swift index 6f03c618..9d521a82 100644 --- a/FueledUtils/CombineOperators/Combine+Operators.swift +++ b/FueledUtils/CombineOperators/Combine+Operators.swift @@ -14,6 +14,12 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsCombine) +import FueledUtilsCombine +import FueledUtilsCore +import FueledUtilsReactiveCommon +public typealias OptionalProtocol = FueledUtilsCore.OptionalProtocol +#endif // swiftlint:disable generic_type_name diff --git a/FueledUtils/CombineUIKit/ControlProtocol+Tapped.swift b/FueledUtils/CombineUIKit/ControlProtocol+Tapped.swift index 80c5b9c1..6869bd7a 100644 --- a/FueledUtils/CombineUIKit/ControlProtocol+Tapped.swift +++ b/FueledUtils/CombineUIKit/ControlProtocol+Tapped.swift @@ -14,6 +14,10 @@ #if canImport(UIKit) && !os(watchOS) && canImport(Combine) import Combine +#if canImport(FueledUtilsUIKit) +import Foundation +import FueledUtilsUIKit +#endif private var tapActionStorage: UInt8 = 0 private var tapActionKey: UInt8 = 0 diff --git a/FueledUtils/CombineUIKit/TapAction.swift b/FueledUtils/CombineUIKit/TapAction.swift index c36e8d2e..cd65ef1d 100644 --- a/FueledUtils/CombineUIKit/TapAction.swift +++ b/FueledUtils/CombineUIKit/TapAction.swift @@ -13,9 +13,14 @@ // limitations under the License. #if canImport(UIKit) && !os(watchOS) +import Foundation +import UIKit #if canImport(Combine) import Combine #endif +#if canImport(FueledUtilsCombine) +import FueledUtilsCombine +#endif /// /// `TapAction` wraps a `ActionProtocol` for use by any `ControlProtocol`. diff --git a/FueledUtils/CombineUIKit/UIControl+ControlEventsPublisher.swift b/FueledUtils/CombineUIKit/UIControl+ControlEventsPublisher.swift index c6a5a998..4de3f5a0 100644 --- a/FueledUtils/CombineUIKit/UIControl+ControlEventsPublisher.swift +++ b/FueledUtils/CombineUIKit/UIControl+ControlEventsPublisher.swift @@ -15,6 +15,10 @@ #if canImport(UIKit) && !os(watchOS) && canImport(Combine) import Combine import UIKit +#if canImport(FueledUtilsUIKit) +import Foundation +import FueledUtilsUIKit +#endif private var publisherControlEventsProcessorsHolderKey: UInt8 = 0 diff --git a/FueledUtils/CombineUIKit/UITextInput+Combine.swift b/FueledUtils/CombineUIKit/UITextInput+Combine.swift index aef9a4dd..20296309 100644 --- a/FueledUtils/CombineUIKit/UITextInput+Combine.swift +++ b/FueledUtils/CombineUIKit/UITextInput+Combine.swift @@ -14,6 +14,12 @@ #if canImport(UIKit) && !os(watchOS) && canImport(Combine) import Combine +#if canImport(FueledUtilsCombine) +import FueledUtilsCombine +#endif +#if canImport(FueledUtilsUIKit) +import FueledUtilsUIKit +#endif import UIKit @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) diff --git a/FueledUtils/ReactiveSwift/ActionError+ActionErrorProtocol.swift b/FueledUtils/ReactiveSwift/ActionError+ActionErrorProtocol.swift index 27e7ee47..b8daf445 100644 --- a/FueledUtils/ReactiveSwift/ActionError+ActionErrorProtocol.swift +++ b/FueledUtils/ReactiveSwift/ActionError+ActionErrorProtocol.swift @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(FueledUtilsReactiveCommon) +import FueledUtilsReactiveCommon +#endif import ReactiveSwift extension ReactiveSwift.ActionError: ActionErrorProtocol { diff --git a/FueledUtils/ReactiveSwift/ReactiveCoalescingAction.swift b/FueledUtils/ReactiveSwift/ReactiveCoalescingAction.swift index be08060c..ec35448d 100644 --- a/FueledUtils/ReactiveSwift/ReactiveCoalescingAction.swift +++ b/FueledUtils/ReactiveSwift/ReactiveCoalescingAction.swift @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(FueledUtilsCore) +import FueledUtilsCore +#endif import ReactiveSwift /// diff --git a/FueledUtils/ReactiveSwift/ReactiveCocoaExtensions.swift b/FueledUtils/ReactiveSwift/ReactiveCocoaExtensions.swift index 6998d928..03cd1ffc 100644 --- a/FueledUtils/ReactiveSwift/ReactiveCocoaExtensions.swift +++ b/FueledUtils/ReactiveSwift/ReactiveCocoaExtensions.swift @@ -13,11 +13,16 @@ // limitations under the License. import Foundation +#if canImport(FueledUtilsReactiveCommon) +import FueledUtilsCore +import FueledUtilsReactiveCommon +#endif import ReactiveCocoa import ReactiveSwift -#if canImport(UIKit) +#if canImport(UIKit) && !os(watchOS) import UIKit #elseif canImport(AppKit) +import AppKit #endif /// diff --git a/FueledUtils/ReactiveSwift/TransferState+Reactive.swift b/FueledUtils/ReactiveSwift/TransferState+Reactive.swift index 91be0e0f..0c5f1a4b 100644 --- a/FueledUtils/ReactiveSwift/TransferState+Reactive.swift +++ b/FueledUtils/ReactiveSwift/TransferState+Reactive.swift @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(FueledUtilsCore) +import FueledUtilsCore +#endif import ReactiveSwift extension SignalProtocol { diff --git a/FueledUtils/ReactiveSwiftUIKit/ReactiveControlProtocol+Tapped.swift b/FueledUtils/ReactiveSwiftUIKit/ReactiveControlProtocol+Tapped.swift index 7c9912d5..3679bf10 100644 --- a/FueledUtils/ReactiveSwiftUIKit/ReactiveControlProtocol+Tapped.swift +++ b/FueledUtils/ReactiveSwiftUIKit/ReactiveControlProtocol+Tapped.swift @@ -12,9 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Foundation #if canImport(UIKit) && !os(watchOS) import ReactiveCocoa import ReactiveSwift +#if canImport(FueledUtilsUIKit) +import FueledUtilsUIKit +#endif private var tapActionStorage: UInt8 = 0 private var tapActionKey: UInt8 = 0 diff --git a/FueledUtils/ReactiveSwiftUIKit/ReactiveTapAction.swift b/FueledUtils/ReactiveSwiftUIKit/ReactiveTapAction.swift index 705bfda1..7e0e1bce 100644 --- a/FueledUtils/ReactiveSwiftUIKit/ReactiveTapAction.swift +++ b/FueledUtils/ReactiveSwiftUIKit/ReactiveTapAction.swift @@ -14,6 +14,11 @@ #if canImport(UIKit) && !os(watchOS) import ReactiveSwift +import Foundation +#if canImport(FueledUtilsUIKit) +import FueledUtilsUIKit +import FueledUtilsReactiveSwift +#endif /// /// `ReactiveTapAction` wraps a `ReactiveActionProtocol` for use by any `ButtonProtocol` diff --git a/FueledUtils/ReactiveSwiftUIKit/SignalingAlert.swift b/FueledUtils/ReactiveSwiftUIKit/SignalingAlert.swift index f15cb756..27cd1f0b 100644 --- a/FueledUtils/ReactiveSwiftUIKit/SignalingAlert.swift +++ b/FueledUtils/ReactiveSwiftUIKit/SignalingAlert.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(UIKit) && !os(watchOS) import Foundation import ReactiveCocoa import ReactiveSwift @@ -107,3 +108,4 @@ public final class SignalingAlert { } } } +#endif diff --git a/FueledUtils/ReactiveSwiftUIKit/UIReactiveExtensions.swift b/FueledUtils/ReactiveSwiftUIKit/UIReactiveExtensions.swift index 4ecc9b23..927cab5e 100644 --- a/FueledUtils/ReactiveSwiftUIKit/UIReactiveExtensions.swift +++ b/FueledUtils/ReactiveSwiftUIKit/UIReactiveExtensions.swift @@ -12,9 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(UIKit) import Foundation import ReactiveSwift import UIKit +#if canImport(FueledUtilsUIKit) +import FueledUtilsUIKit +#endif extension Reactive where Base: UILabel { /// @@ -34,3 +38,4 @@ extension Reactive where Base: UILabel { } } } +#endif diff --git a/FueledUtils/UIKit/ButtonWithTitleAdjustment.swift b/FueledUtils/UIKit/ButtonWithTitleAdjustment.swift index 4bd7fc97..d3f39194 100644 --- a/FueledUtils/UIKit/ButtonWithTitleAdjustment.swift +++ b/FueledUtils/UIKit/ButtonWithTitleAdjustment.swift @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - +#if canImport(UIKit) && !os(watchOS) import UIKit /// @@ -108,3 +108,4 @@ open class ButtonWithTitleAdjustment: UIButton { self.setAttributedTitle(adjustedString, for: state) } } +#endif diff --git a/FueledUtils/UIKit/DecoratingTextFieldDelegate.swift b/FueledUtils/UIKit/DecoratingTextFieldDelegate.swift index 348a0b5e..62e37809 100644 --- a/FueledUtils/UIKit/DecoratingTextFieldDelegate.swift +++ b/FueledUtils/UIKit/DecoratingTextFieldDelegate.swift @@ -12,8 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(UIKit) && !os(watchOS) import UIKit import Foundation +#if canImport(FueledUtilsCore) +import FueledUtilsCore +#endif /// /// Adds formatting (decoration) characters to text field's content according to a variable pattern. Can be used for @@ -192,3 +196,4 @@ extension DecoratingTextFieldDelegate: UITextFieldDelegate { return false } } +#endif diff --git a/FueledUtils/UIKit/DimmingButton.swift b/FueledUtils/UIKit/DimmingButton.swift index 08faf380..f0d862be 100644 --- a/FueledUtils/UIKit/DimmingButton.swift +++ b/FueledUtils/UIKit/DimmingButton.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(UIKit) && !os(watchOS) import Foundation import UIKit @@ -58,3 +59,4 @@ public final class DimmingButton: UIButton { (view ?? self).alpha = dimmedAlpha } } +#endif diff --git a/FueledUtils/UIKit/GradientView.swift b/FueledUtils/UIKit/GradientView.swift index baae13d8..d059cc29 100644 --- a/FueledUtils/UIKit/GradientView.swift +++ b/FueledUtils/UIKit/GradientView.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(UIKit) && !os(watchOS) import UIKit /// @@ -274,3 +275,4 @@ extension CGPoint { return CGPoint(x: 1.0, y: 0.0) } } +#endif diff --git a/FueledUtils/UIKit/HairlineView.swift b/FueledUtils/UIKit/HairlineView.swift index 3af3fdfd..fa135ba6 100644 --- a/FueledUtils/UIKit/HairlineView.swift +++ b/FueledUtils/UIKit/HairlineView.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(UIKit) && !os(watchOS) import Foundation import UIKit @@ -42,3 +43,4 @@ open class HairlineView: UIView { } } } +#endif diff --git a/FueledUtils/UIKit/KeyboardInsetHelper.swift b/FueledUtils/UIKit/KeyboardInsetHelper.swift index e6ada319..7efe9d17 100644 --- a/FueledUtils/UIKit/KeyboardInsetHelper.swift +++ b/FueledUtils/UIKit/KeyboardInsetHelper.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(UIKit) && !os(watchOS) && !os(tvOS) import Foundation import UIKit @@ -104,3 +105,4 @@ open class KeyboardInsetHelper: NSObject { referenceView?.layoutIfNeeded() } } +#endif diff --git a/FueledUtils/UIKit/LabelWithTitleAdjustment.swift b/FueledUtils/UIKit/LabelWithTitleAdjustment.swift index cae83a7a..4b5a59ab 100644 --- a/FueledUtils/UIKit/LabelWithTitleAdjustment.swift +++ b/FueledUtils/UIKit/LabelWithTitleAdjustment.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(UIKit) && !os(watchOS) import UIKit import Foundation @@ -111,3 +112,4 @@ open class LabelWithTitleAdjustment: UILabel { self.setAdjustedAttributedText(text.map { NSAttributedString(string: $0) }) } } +#endif diff --git a/FueledUtils/UIKit/ScrollViewPage.swift b/FueledUtils/UIKit/ScrollViewPage.swift index f9ad5033..813c2838 100644 --- a/FueledUtils/UIKit/ScrollViewPage.swift +++ b/FueledUtils/UIKit/ScrollViewPage.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(UIKit) && !os(watchOS) import UIKit extension UIScrollView { @@ -83,3 +84,4 @@ extension UIScrollView { self.setContentOffset(offset, animated: animated) } } +#endif diff --git a/FueledUtils/UIKit/SetRootViewController.swift b/FueledUtils/UIKit/SetRootViewController.swift index 84401c4a..b1208b9e 100644 --- a/FueledUtils/UIKit/SetRootViewController.swift +++ b/FueledUtils/UIKit/SetRootViewController.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(UIKit) && !os(watchOS) import Foundation import UIKit @@ -51,3 +52,4 @@ extension UIApplicationDelegate { } } } +#endif diff --git a/FueledUtils/UIKit/UIExtensions.swift b/FueledUtils/UIKit/UIExtensions.swift index d7f896aa..614a2c44 100644 --- a/FueledUtils/UIKit/UIExtensions.swift +++ b/FueledUtils/UIKit/UIExtensions.swift @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if canImport(UIKit) && !os(watchOS) import Foundation import UIKit @@ -663,3 +664,4 @@ extension UIImage { return image.resizableImage(withCapInsets: .init(top: capInset, left: capInset, bottom: capInset, right: capInset), resizingMode: .stretch) } } +#endif diff --git a/Gemfile.lock b/Gemfile.lock index 42b17db2..3faaa27e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,7 +3,7 @@ GEM specs: addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) - claide (1.0.3) + claide (1.1.0) claide-plugins (0.9.2) cork nap @@ -11,7 +11,7 @@ GEM colored2 (3.1.2) cork (0.3.0) colored2 (~> 3.1) - danger (8.0.6) + danger (8.6.1) claide (~> 1.0) claide-plugins (>= 0.9.2) colored2 (~> 3.1) @@ -23,33 +23,55 @@ GEM kramdown-parser-gfm (~> 1.0) no_proxy_fix octokit (~> 4.7) - terminal-table (~> 1) - faraday (1.0.1) - multipart-post (>= 1.2, < 3) - faraday-http-cache (2.2.0) + terminal-table (>= 1, < 4) + faraday (1.10.1) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-http-cache (2.4.1) faraday (>= 0.8) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) git (1.11.0) rchardet (~> 1.8) - kramdown (2.3.1) + kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) - multipart-post (2.1.1) + multipart-post (2.2.3) nap (1.1.0) no_proxy_fix (0.1.2) - octokit (4.18.0) - faraday (>= 0.9) - sawyer (~> 0.8.0, >= 0.5.3) + octokit (4.25.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) open4 (1.3.4) - public_suffix (4.0.6) + public_suffix (4.0.7) rchardet (1.8.0) rexml (3.2.5) - sawyer (0.8.2) + ruby2_keywords (0.0.5) + sawyer (0.9.2) addressable (>= 2.3.5) - faraday (> 0.8, < 2.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - unicode-display_width (1.7.0) + faraday (>= 0.17.3, < 3) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + unicode-display_width (2.2.0) PLATFORMS ruby diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 00000000..14e9a975 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,61 @@ +{ + "object": { + "pins": [ + { + "package": "CwlCatchException", + "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git", + "state": { + "branch": null, + "revision": "35f9e770f54ce62dd8526470f14c6e137cef3eea", + "version": "2.1.1" + } + }, + { + "package": "CwlPreconditionTesting", + "repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git", + "state": { + "branch": null, + "revision": "c21f7bab5ca8eee0a9998bbd17ca1d0eb45d4688", + "version": "2.1.0" + } + }, + { + "package": "Nimble", + "repositoryURL": "https://github.com/Quick/Nimble.git", + "state": { + "branch": null, + "revision": "c93f16c25af5770f0d3e6af27c9634640946b068", + "version": "9.2.1" + } + }, + { + "package": "Quick", + "repositoryURL": "https://github.com/Quick/Quick.git", + "state": { + "branch": null, + "revision": "bd86ca0141e3cfb333546de5a11ede63f0c4a0e6", + "version": "4.0.0" + } + }, + { + "package": "ReactiveCocoa", + "repositoryURL": "https://github.com/ReactiveCocoa/ReactiveCocoa.git", + "state": { + "branch": null, + "revision": "8a5b07b18c6abb8e12fb845bf742675dc4914ed8", + "version": "12.0.0" + } + }, + { + "package": "ReactiveSwift", + "repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift.git", + "state": { + "branch": null, + "revision": "efb2f0a6f6c8739cce8fb14148a5bd3c83f2f91d", + "version": "7.0.0" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 00000000..8bba45a7 --- /dev/null +++ b/Package.swift @@ -0,0 +1,136 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "FueledUtils", + platforms: [ + .macOS(.v10_12), .iOS(.v13), .tvOS(.v13), .watchOS(.v6) + ], + products: [ + .library( + name: "FueledUtilsCore", + targets: ["FueledUtilsCore"] + ), + .library( + name: "FueledUtilsReactiveCommon", + targets: ["FueledUtilsReactiveCommon"] + ), + .library( + name: "FueledUtilsReactiveSwift", + targets: ["FueledUtilsReactiveSwift"] + ), + .library( + name: "FueledUtilsUIKit", + targets: ["FueledUtilsUIKit"] + ), + .library( + name: "FueledUtilsReactiveSwiftUIKit", + targets: ["FueledUtilsReactiveSwiftUIKit"] + ), + .library( + name: "FueledUtilsCombine", + targets: ["FueledUtilsCombine"] + ), + .library( + name: "FueledUtilsCombineOperators", + targets: ["FueledUtilsCombineOperators"] + ), + .library( + name: "FueledUtilsCombineUIKit", + targets: ["FueledUtilsCombineUIKit"] + ), + .library( + name: "FueledUtilsSwiftUI", + targets: ["FueledUtilsSwiftUI"] + ), + .library( + name: "FueledUtilsReactiveCombineBridge", + targets: ["FueledUtilsReactiveCombineBridge"] + ), + ], + dependencies: [ + .package(url: "https://github.com/ReactiveCocoa/ReactiveSwift.git", from: "7.0.0"), + .package(url: "https://github.com/ReactiveCocoa/ReactiveCocoa.git", from: "12.0.0"), + .package(url: "https://github.com/Quick/Quick.git", from: "4.0.0"), + .package(url: "https://github.com/Quick/Nimble.git", from: "9.0.0"), + ], + targets: [ + .target( + name: "FueledUtilsCore", + path: "FueledUtils/Core", + linkerSettings: [ + .linkedFramework("Foundation") + ] + ), + .target( + name: "FueledUtilsReactiveCommon", + dependencies: ["FueledUtilsCore"], + path: "FueledUtils/ReactiveCommon" + ), + .target( + name: "FueledUtilsReactiveSwift", + dependencies: ["FueledUtilsReactiveCommon", "ReactiveSwift", "ReactiveCocoa"], + path: "FueledUtils/ReactiveSwift" + ), + .target( + name: "FueledUtilsUIKit", + dependencies: ["FueledUtilsCore"], + path: "FueledUtils/UIKit", + linkerSettings: [ + .linkedFramework("Foundation"), + .linkedFramework("UIKit", .when(platforms: [.iOS, .tvOS])), + .linkedFramework("AppKit", .when(platforms: [.macOS])), + ] + ), + .target( + name: "FueledUtilsReactiveSwiftUIKit", + dependencies: ["FueledUtilsReactiveSwift", "FueledUtilsUIKit"], + path: "FueledUtils/ReactiveSwiftUIKit", + linkerSettings: [ + .linkedFramework("Foundation"), + .linkedFramework("UIKit", .when(platforms: [.iOS, .tvOS])), + .linkedFramework("AppKit", .when(platforms: [.macOS])), + ] + ), + .target( + name: "FueledUtilsCombine", + dependencies: ["FueledUtilsReactiveCommon"], + path: "FueledUtils/Combine" + ), + .target( + name: "FueledUtilsCombineOperators", + dependencies: ["FueledUtilsCombine"], + path: "FueledUtils/CombineOperators" + ), + .target( + name: "FueledUtilsCombineUIKit", + dependencies: ["FueledUtilsCombine", "FueledUtilsUIKit"], + path: "FueledUtils/CombineUIKit" + ), + .target( + name: "FueledUtilsSwiftUI", + dependencies: ["FueledUtilsCombine", "FueledUtilsCore"], + path: "FueledUtils/SwiftUI", + linkerSettings: [ + .linkedFramework("SwiftUI", .when(platforms: [.iOS, .tvOS, .macOS])), + ] + ), + .target( + name: "FueledUtilsReactiveCombineBridge", + dependencies: ["FueledUtilsCombine", "FueledUtilsReactiveSwift"], + path: "FueledUtils/ReactiveCombineBridge" + ), + .testTarget( + name: "FueledUtils", + dependencies: [ + "FueledUtilsCombineUIKit", + "FueledUtilsReactiveSwiftUIKit", + "Quick", + "Nimble", + ], + path: "Tests/Tests" + ), + ] +) diff --git a/Tests/FueledUtils.xcodeproj/project.pbxproj b/Tests/FueledUtils.xcodeproj/project.pbxproj index d7d2f3df..e69d879b 100644 --- a/Tests/FueledUtils.xcodeproj/project.pbxproj +++ b/Tests/FueledUtils.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ F453AA832538E05E008F045B /* ReactiveCoalescingActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = F463C73A241835DD000A0B29 /* ReactiveCoalescingActionSpec.swift */; }; F453AA842538E05E008F045B /* ReactiveOverridingActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = F429742925378348004BFA85 /* ReactiveOverridingActionSpec.swift */; }; F453AA892538EF16008F045B /* SinkForLifetimeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453AA882538EF16008F045B /* SinkForLifetimeSpec.swift */; }; + FC204075279EE4C9001A767C /* AnyCurrentValuePublisherSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC204074279EE4C9001A767C /* AnyCurrentValuePublisherSpec.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -46,6 +47,7 @@ F46D2DA5252E4E4A00B6987A /* OrderedSetSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedSetSpec.swift; sourceTree = ""; }; F499631F2537459200E2D4B5 /* CoalescingActionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoalescingActionSpec.swift; sourceTree = ""; }; F7F10FE9C8384333882C2368 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; + FC204074279EE4C9001A767C /* AnyCurrentValuePublisherSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyCurrentValuePublisherSpec.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -121,6 +123,7 @@ F463C739241835DD000A0B29 /* Tests */ = { isa = PBXGroup; children = ( + FC204074279EE4C9001A767C /* AnyCurrentValuePublisherSpec.swift */, F499631F2537459200E2D4B5 /* CoalescingActionSpec.swift */, F46D2DA5252E4E4A00B6987A /* OrderedSetSpec.swift */, F429742D25378425004BFA85 /* OverridingActionSpec.swift */, @@ -265,6 +268,7 @@ files = ( F453AA7B2538DE55008F045B /* CombineLatestManySpec.swift in Sources */, F453AA802538E05E008F045B /* OrderedSetSpec.swift in Sources */, + FC204075279EE4C9001A767C /* AnyCurrentValuePublisherSpec.swift in Sources */, F453AA892538EF16008F045B /* SinkForLifetimeSpec.swift in Sources */, F453AA822538E05E008F045B /* OverridingActionSpec.swift in Sources */, F453AA832538E05E008F045B /* ReactiveCoalescingActionSpec.swift in Sources */, diff --git a/Tests/Podfile b/Tests/Podfile index 100fe2ae..24576481 100644 --- a/Tests/Podfile +++ b/Tests/Podfile @@ -5,7 +5,7 @@ abstract_target 'Common' do platform :ios, '13.0' pod 'Quick', '~> 2.0' - pod 'Nimble', '~> 8.0' + pod 'Nimble', '~> 9.0' pod 'FueledUtils/ReactiveCombineBridge', path: '../' pod 'FueledUtils/ReactiveSwift', path: '../' diff --git a/Tests/Podfile.lock b/Tests/Podfile.lock index d67c157c..882a6d77 100644 --- a/Tests/Podfile.lock +++ b/Tests/Podfile.lock @@ -1,30 +1,30 @@ PODS: - - FueledUtils/Combine (3.0-alpha1): + - FueledUtils/Combine (3.1.0): - FueledUtils/ReactiveCommon - - FueledUtils/CombineOperators (3.0-alpha1): + - FueledUtils/CombineOperators (3.1.0): - FueledUtils/Combine - - FueledUtils/CombineUIKit (3.0-alpha1): + - FueledUtils/CombineUIKit (3.1.0): - FueledUtils/Combine - FueledUtils/UIKit - - FueledUtils/Core (3.0-alpha1) - - FueledUtils/ReactiveCombineBridge (3.0-alpha1): + - FueledUtils/Core (3.1.0) + - FueledUtils/ReactiveCombineBridge (3.1.0): - FueledUtils/Combine - FueledUtils/ReactiveSwift - - FueledUtils/ReactiveCommon (3.0-alpha1): + - FueledUtils/ReactiveCommon (3.1.0): - FueledUtils/Core - - FueledUtils/ReactiveSwift (3.0-alpha1): + - FueledUtils/ReactiveSwift (3.1.0): - FueledUtils/ReactiveCommon - ReactiveCocoa (~> 10.0) - ReactiveSwift (~> 6.0) - - FueledUtils/ReactiveSwiftUIKit (3.0-alpha1): + - FueledUtils/ReactiveSwiftUIKit (3.1.0): - FueledUtils/ReactiveSwift - FueledUtils/UIKit - - FueledUtils/SwiftUI (3.0-alpha1): + - FueledUtils/SwiftUI (3.1.0): - FueledUtils/Combine - FueledUtils/Core - - FueledUtils/UIKit (3.0-alpha1): + - FueledUtils/UIKit (3.1.0): - FueledUtils/Core - - Nimble (8.0.5) + - Nimble (9.2.1) - Quick (2.2.0) - ReactiveCocoa (10.3.0): - ReactiveSwift (~> 6.2) @@ -37,7 +37,7 @@ DEPENDENCIES: - FueledUtils/ReactiveSwift (from `../`) - FueledUtils/ReactiveSwiftUIKit (from `../`) - FueledUtils/SwiftUI (from `../`) - - Nimble (~> 8.0) + - Nimble (~> 9.0) - Quick (~> 2.0) SPEC REPOS: @@ -52,12 +52,12 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - FueledUtils: aa4b2a1e780f4f7e870ad53b64d0d7d16ab78604 - Nimble: 4ab1aeb9b45553c75b9687196b0fa0713170a332 + FueledUtils: b0c089ddab7e706669f31c45325c399eabd52868 + Nimble: e7e615c0335ee4bf5b0d786685451e62746117d5 Quick: 7fb19e13be07b5dfb3b90d4f9824c855a11af40e ReactiveCocoa: 083ae559e6f588ce519cab412ea119b431c26a24 ReactiveSwift: 7555791a608c0679563a3f72546f971b2a06de98 -PODFILE CHECKSUM: 57c718acafcbfdffd0d4c0cd376a0088c9cc4812 +PODFILE CHECKSUM: d7dfdb27266ecccb25fe117e52721c9b19977a13 -COCOAPODS: 1.10.0 +COCOAPODS: 1.11.2 diff --git a/Tests/Tests/AnyCurrentValuePublisherSpec.swift b/Tests/Tests/AnyCurrentValuePublisherSpec.swift new file mode 100644 index 00000000..695fab22 --- /dev/null +++ b/Tests/Tests/AnyCurrentValuePublisherSpec.swift @@ -0,0 +1,43 @@ +// Copyright © 2020 Fueled Digital Media, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if canImport(Combine) +import Combine +#if canImport(FueledUtilsCombine) +import FueledUtilsCombine +#elseif canImport(FueledUtils) +import FueledUtils +#endif +import Foundation +import Quick +import Nimble + +class AnyCurrentValuePublisherSpec: QuickSpec { + override func spec() { + describe("AnyCurrentValuePublisherSpec") { + describe("Initialization") { + it("Should initialize with a stored value") { + let publisher = AnyCurrentValuePublisher(1) + expect(publisher.value) == 1 + } + it("Should initialize with a nested CurrentValueSubject") { + let subject = CurrentValueSubject(2) + let publisher = AnyCurrentValuePublisher(subject) + expect(publisher.value) == 2 + } + } + } + } +} +#endif diff --git a/Tests/Tests/CoalescingActionSpec.swift b/Tests/Tests/CoalescingActionSpec.swift index f3027130..d8d5f27d 100644 --- a/Tests/Tests/CoalescingActionSpec.swift +++ b/Tests/Tests/CoalescingActionSpec.swift @@ -14,7 +14,12 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsCombine) +import FueledUtilsCombine +#elseif canImport(FueledUtils) import FueledUtils +#endif +import Foundation import Quick import Nimble diff --git a/Tests/Tests/CombineLatestManySpec.swift b/Tests/Tests/CombineLatestManySpec.swift index 80125cfd..5005e1d4 100644 --- a/Tests/Tests/CombineLatestManySpec.swift +++ b/Tests/Tests/CombineLatestManySpec.swift @@ -14,7 +14,12 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsCombine) +import FueledUtilsCombine +#elseif canImport(FueledUtils) import FueledUtils +#endif +import Foundation import Quick import Nimble diff --git a/Tests/Tests/OrderedSetSpec.swift b/Tests/Tests/OrderedSetSpec.swift index e02f7032..b5b2ad41 100644 --- a/Tests/Tests/OrderedSetSpec.swift +++ b/Tests/Tests/OrderedSetSpec.swift @@ -14,7 +14,11 @@ import Quick import Nimble +#if canImport(FueledUtilsCore) +import FueledUtilsCore +#elseif canImport(FueledUtils) import FueledUtils +#endif import XCTest class OrderedSetSpec: QuickSpec { diff --git a/Tests/Tests/OverridingActionSpec.swift b/Tests/Tests/OverridingActionSpec.swift index 35c77e6c..0e8f5205 100644 --- a/Tests/Tests/OverridingActionSpec.swift +++ b/Tests/Tests/OverridingActionSpec.swift @@ -14,7 +14,12 @@ #if canImport(Combine) import Combine +#if canImport(FueledUtilsCombine) +import FueledUtilsCombine +#elseif canImport(FueledUtils) import FueledUtils +#endif +import Foundation import Quick import Nimble @@ -56,8 +61,8 @@ class OverridingActionSpec: QuickSpec { expect(cancelledCounter) == publishersCount - 1 expect(interruptedCounter) == publishersCount - 1 - expect(cancelledCounter).toEventually(equal(publishersCount - 1), timeout: 2.0) - expect(cancelledCounter).toEventually(equal(interruptedCounter), timeout: 2.0) + expect(cancelledCounter).toEventually(equal(publishersCount - 1), timeout: .seconds(2)) + expect(cancelledCounter).toEventually(equal(interruptedCounter), timeout: .seconds(2)) } } } diff --git a/Tests/Tests/ReactiveCoalescingActionSpec.swift b/Tests/Tests/ReactiveCoalescingActionSpec.swift index 3a9c0d68..4bf97b15 100644 --- a/Tests/Tests/ReactiveCoalescingActionSpec.swift +++ b/Tests/Tests/ReactiveCoalescingActionSpec.swift @@ -14,7 +14,12 @@ import Quick import Nimble +#if canImport(FueledUtilsReactiveSwift) +import FueledUtilsReactiveSwift +#elseif canImport(FueledUtils) import FueledUtils +#endif +import Foundation import ReactiveSwift class ReactiveCoalescingActionSpec: QuickSpec { diff --git a/Tests/Tests/ReactiveOverridingActionSpec.swift b/Tests/Tests/ReactiveOverridingActionSpec.swift index 6c949a4b..b2ea0a7a 100644 --- a/Tests/Tests/ReactiveOverridingActionSpec.swift +++ b/Tests/Tests/ReactiveOverridingActionSpec.swift @@ -13,7 +13,12 @@ // limitations under the License. import Quick +#if canImport(FueledUtilsReactiveSwift) +import FueledUtilsReactiveSwift +#elseif canImport(FueledUtils) import FueledUtils +#endif +import Foundation import Nimble import ReactiveSwift @@ -50,11 +55,11 @@ class ReactiveOverridingActionSpec: QuickSpec { } expect(startCounter) == producersCount - expect(disposeCounter).toEventually(equal(producersCount - 1), timeout: 0.01) - expect(interruptedCounter).toEventually(equal(producersCount - 1), timeout: 0.01) + expect(disposeCounter).toEventually(equal(producersCount - 1), timeout: .milliseconds(10)) + expect(interruptedCounter).toEventually(equal(producersCount - 1), timeout: .milliseconds(10)) - expect(disposeCounter).toEventually(equal(producersCount), timeout: 2.0) - expect(interruptedCounter).toEventually(equal(producersCount - 1), timeout: 2.0) + expect(disposeCounter).toEventually(equal(producersCount), timeout: .seconds(2)) + expect(interruptedCounter).toEventually(equal(producersCount - 1), timeout: .seconds(2)) } } } diff --git a/Tests/Tests/ReactiveSwiftExtensionsSpec.swift b/Tests/Tests/ReactiveSwiftExtensionsSpec.swift index fd9f5580..ab6b74bc 100644 --- a/Tests/Tests/ReactiveSwiftExtensionsSpec.swift +++ b/Tests/Tests/ReactiveSwiftExtensionsSpec.swift @@ -14,7 +14,12 @@ import Quick import Nimble +#if canImport(FueledUtilsReactiveSwift) +import FueledUtilsReactiveSwift +#elseif canImport(FueledUtils) import FueledUtils +#endif +import Foundation import ReactiveSwift class ReactiveSwiftExtensionsSpec: QuickSpec { @@ -33,7 +38,7 @@ class ReactiveSwiftExtensionsSpec: QuickSpec { expect(valuesReceived) == 0 - expect(valuesReceived).toEventually(equal(1), timeout: 1.0) + expect(valuesReceived).toEventually(equal(1), timeout: .seconds(1)) observer.send(value: ()) @@ -51,7 +56,7 @@ class ReactiveSwiftExtensionsSpec: QuickSpec { expect(errorsReceived) == 0 - expect(errorsReceived).toEventually(equal(1), timeout: 1.0) + expect(errorsReceived).toEventually(equal(1), timeout: .seconds(1)) } it("should send any values instantly and receive an interrupted event if interrupted before the minimum interval is met") { let (signal, observer) = Signal.pipe() diff --git a/Tests/Tests/SinkForLifetimeSpec.swift b/Tests/Tests/SinkForLifetimeSpec.swift index ea1253a2..a0100cfb 100644 --- a/Tests/Tests/SinkForLifetimeSpec.swift +++ b/Tests/Tests/SinkForLifetimeSpec.swift @@ -14,7 +14,12 @@ import Quick import Nimble +#if canImport(FueledUtilsCombine) +import FueledUtilsCombine +#elseif canImport(FueledUtils) import FueledUtils +#endif +import Foundation import ReactiveSwift #if canImport(Combine) import Combine @@ -45,7 +50,7 @@ class SinkForLifetimeSpec: QuickSpec { } expect(valueCount).toEventually(equal(2)) - expect(cancelCount).toEventually(equal(1), timeout: 2.0) + expect(cancelCount).toEventually(equal(1), timeout: .seconds(2)) } } }