diff --git a/.github/workflows/build-and-tests.yml b/.github/workflows/build-and-tests.yml index dc523a2..3b737b3 100644 --- a/.github/workflows/build-and-tests.yml +++ b/.github/workflows/build-and-tests.yml @@ -48,6 +48,8 @@ jobs: - name: Run all tests ⚙️ run: arch -arm64 rake tests + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: Validate Podfile ⚙️ run: arch -arm64 rake validate_podfile diff --git a/.swiftlint.yml b/.swiftlint.yml index 16e7d92..1bc7c8c 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -11,7 +11,6 @@ opt_in_rules: - yoda_condition - modifier_order - anyobject_protocol - - unused_import #- explicit_acl - cyclomatic_complexity #style @@ -26,7 +25,6 @@ opt_in_rules: - file_header - conditional_returns_on_newline - implicit_return - - explicit_self - multiline_function_chains - switch_case_on_newline - prefer_self_type_over_type_of_self @@ -74,8 +72,8 @@ line_length: warning: 250 error: 400 large_tuple: - warning: 3 - error: 3 + warning: 5 + error: 5 file_length: warning: 2000 error: 2000 diff --git a/Cartfile b/Cartfile deleted file mode 100644 index 3f17aa5..0000000 --- a/Cartfile +++ /dev/null @@ -1 +0,0 @@ -github "ReactiveX/RxSwift" ~> 6.0 \ No newline at end of file diff --git a/Cartfile.resolved b/Cartfile.resolved deleted file mode 100644 index e321691..0000000 --- a/Cartfile.resolved +++ /dev/null @@ -1 +0,0 @@ -github "ReactiveX/RxSwift" "6.2.0" \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 432c63c..eecdca0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,36 +1,35 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.5) + CFPropertyList (3.0.6) rexml - activesupport (6.1.6) + activesupport (7.0.4.2) 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) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.600.0) - aws-sdk-core (3.131.2) + aws-partitions (1.717.0) + aws-sdk-core (3.170.0) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.525.0) - aws-sigv4 (~> 1.1) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.57.0) - aws-sdk-core (~> 3, >= 3.127.0) + aws-sdk-kms (1.62.0) + aws-sdk-core (~> 3, >= 3.165.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.114.0) - aws-sdk-core (~> 3, >= 3.127.0) + aws-sdk-s3 (1.119.1) + aws-sdk-core (~> 3, >= 3.165.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) - aws-sigv4 (1.5.0) + aws-sigv4 (1.5.2) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) childprocess (4.1.0) @@ -39,15 +38,15 @@ GEM cork nap open4 (~> 1.3) - cocoapods (1.11.3) + cocoapods (1.12.0) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.11.3) + cocoapods-core (= 1.12.0) cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 1.4.0, < 2.0) + cocoapods-downloader (>= 1.6.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-trunk (>= 1.6.0, < 2.0) cocoapods-try (>= 1.1.0, < 2.0) colored2 (~> 3.1) escape (~> 0.0.4) @@ -55,10 +54,10 @@ GEM gh_inspector (~> 1.0) molinillo (~> 0.8.0) nap (~> 1.0) - ruby-macho (>= 1.0, < 3.0) + ruby-macho (>= 2.3.0, < 3.0) xcodeproj (>= 1.21.0, < 2.0) - cocoapods-core (1.11.3) - activesupport (>= 5.0, < 7) + cocoapods-core (1.12.0) + activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) concurrent-ruby (~> 1.1) @@ -80,28 +79,28 @@ GEM colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) - concurrent-ruby (1.1.10) + concurrent-ruby (1.2.2) cork (0.3.0) colored2 (~> 3.1) - danger (8.6.1) + danger (9.2.0) claide (~> 1.0) claide-plugins (>= 0.9.2) colored2 (~> 3.1) cork (~> 0.1) - faraday (>= 0.9.0, < 2.0) + faraday (>= 0.9.0, < 3.0) faraday-http-cache (~> 2.0) git (~> 1.7) kramdown (~> 2.3) kramdown-parser-gfm (~> 1.0) no_proxy_fix - octokit (~> 4.7) + octokit (~> 5.0) terminal-table (>= 1, < 4) danger-junit (1.0.2) danger (> 2.0) ox (~> 2.0) danger-plugin-api (1.0.0) danger (> 2.0) - danger-swiftlint (0.30.2) + danger-swiftlint (0.31.0) danger rake (> 10) thor (~> 0.19) @@ -118,13 +117,13 @@ GEM rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.6) + dotenv (2.8.1) emoji_regex (3.2.3) escape (0.0.4) - ethon (0.15.0) + ethon (0.16.0) ffi (>= 1.15.0) - excon (0.92.3) - faraday (1.10.0) + excon (0.99.0) + faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -142,7 +141,7 @@ GEM faraday-em_http (1.0.0) faraday-em_synchrony (1.0.0) faraday-excon (1.1.0) - faraday-http-cache (2.4.0) + faraday-http-cache (2.4.1) faraday (>= 0.8) faraday-httpclient (1.0.1) faraday-multipart (1.0.4) @@ -155,7 +154,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.2.6) - fastlane (2.206.2) + fastlane (2.212.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -198,11 +197,12 @@ GEM fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - git (1.11.0) + git (1.14.0) + addressable (~> 2.8) rchardet (~> 1.8) - google-apis-androidpublisher_v3 (0.22.0) - google-apis-core (>= 0.5, < 2.a) - google-apis-core (0.6.0) + google-apis-androidpublisher_v3 (0.34.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-core (0.11.0) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -211,27 +211,27 @@ GEM retriable (>= 2.0, < 4.a) rexml webrick - google-apis-iamcredentials_v1 (0.12.0) - google-apis-core (>= 0.6, < 2.a) - google-apis-playcustomapp_v1 (0.9.0) - google-apis-core (>= 0.6, < 2.a) - google-apis-storage_v1 (0.15.0) - google-apis-core (>= 0.5, < 2.a) + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.19.0) + google-apis-core (>= 0.9.0, < 2.a) google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.2.0) - google-cloud-storage (1.36.2) + google-cloud-errors (1.3.0) + google-cloud-storage (1.44.0) addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.19.0) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.1.3) + googleauth (1.3.0) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) @@ -242,20 +242,20 @@ GEM http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) - i18n (1.10.0) + i18n (1.12.0) concurrent-ruby (~> 1.0) iniparse (1.5.0) - jmespath (1.6.1) - json (2.6.2) - jwt (2.4.1) + jmespath (1.6.2) + json (2.6.3) + jwt (2.7.0) kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) memoist (0.16.2) - mini_magick (4.11.0) + mini_magick (4.12.0) mini_mime (1.1.2) - minitest (5.16.1) + minitest (5.17.0) molinillo (0.8.0) multi_json (1.15.0) multipart-post (2.0.0) @@ -264,18 +264,18 @@ GEM naturally (2.2.1) netrc (0.11.0) no_proxy_fix (0.1.2) - octokit (4.25.0) + octokit (5.6.1) faraday (>= 1, < 3) sawyer (~> 0.9) open4 (1.3.4) optparse (0.1.1) os (1.1.4) - overcommit (0.59.1) + overcommit (0.60.0) childprocess (>= 0.6.3, < 5) iniparse (~> 1.4) rexml (~> 3.2) - ox (2.14.11) - plist (3.6.0) + ox (2.14.14) + plist (3.7.0) public_suffix (4.0.7) rake (13.0.6) rchardet (1.8.0) @@ -293,12 +293,12 @@ GEM addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) security (0.1.3) - signet (0.16.1) + signet (0.17.0) addressable (~> 2.8) - faraday (>= 0.17.5, < 3.0) + faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.8) + simctl (1.6.10) CFPropertyList naturally slack-notifier (2.4.0) @@ -313,14 +313,14 @@ GEM tty-cursor (~> 0.7) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (2.0.4) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) unf (0.1.4) unf_ext unf_ext (0.0.8.2) unicode-display_width (1.8.0) - webrick (1.7.0) + webrick (1.8.1) word_wrap (1.0.0) xcodeproj (1.22.0) CFPropertyList (>= 2.3.3, < 4.0) @@ -343,7 +343,6 @@ GEM xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) xcresult (0.2.1) - zeitwerk (2.6.0) PLATFORMS ruby diff --git a/MasMini-Swift.podspec b/MasMini-Swift.podspec index e11c02f..beb6d08 100644 --- a/MasMini-Swift.podspec +++ b/MasMini-Swift.podspec @@ -28,8 +28,6 @@ Pod::Spec.new do |s| s.frameworks = 'Foundation' - s.dependency('RxSwift', '~> 6') - s.default_subspec = 'Core' s.module_name = 'Mini' diff --git a/Mini.xcodeproj/project.pbxproj b/Mini.xcodeproj/project.pbxproj index ae1b4a7..dfcae73 100644 --- a/Mini.xcodeproj/project.pbxproj +++ b/Mini.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -14,10 +14,6 @@ E281243B2122CC4C00E4F6D0 /* Mini.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E28124322122CC4C00E4F6D0 /* Mini.framework */; }; E28124562122CCE300E4F6D0 /* Mini.h in Headers */ = {isa = PBXBuildFile; fileRef = E26053F12121880000D013B5 /* Mini.h */; settings = {ATTRIBUTES = (Public, ); }; }; E28124572122CCE300E4F6D0 /* Mini.h in Headers */ = {isa = PBXBuildFile; fileRef = E26053F12121880000D013B5 /* Mini.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F20396B125E7C7D1002DBD08 /* RxSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F20396B025E7C7D1002DBD08 /* RxSwift.xcframework */; }; - F20396B225E7C7D1002DBD08 /* RxSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F20396B025E7C7D1002DBD08 /* RxSwift.xcframework */; }; - F20396B325E7C7D1002DBD08 /* RxSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F20396B025E7C7D1002DBD08 /* RxSwift.xcframework */; }; - F20396B425E7C7D1002DBD08 /* RxSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F20396B025E7C7D1002DBD08 /* RxSwift.xcframework */; }; F222D4AF25249B7E00672E7B /* Dispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4A625249B7D00672E7B /* Dispatcher.swift */; }; F222D4B025249B7E00672E7B /* Dispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4A625249B7D00672E7B /* Dispatcher.swift */; }; F222D4B125249B7E00672E7B /* Dispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4A625249B7D00672E7B /* Dispatcher.swift */; }; @@ -54,14 +50,6 @@ F222D4D025249B7E00672E7B /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4AE25249B7D00672E7B /* Store.swift */; }; F222D4D125249B7E00672E7B /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4AE25249B7D00672E7B /* Store.swift */; }; F222D4D225249B7E00672E7B /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4AE25249B7D00672E7B /* Store.swift */; }; - F222D4ED25249B9A00672E7B /* ObservableTypeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4DE25249B9A00672E7B /* ObservableTypeExtensions.swift */; }; - F222D4EE25249B9A00672E7B /* ObservableTypeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4DE25249B9A00672E7B /* ObservableTypeExtensions.swift */; }; - F222D4EF25249B9A00672E7B /* ObservableTypeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4DE25249B9A00672E7B /* ObservableTypeExtensions.swift */; }; - F222D4F025249B9A00672E7B /* ObservableTypeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4DE25249B9A00672E7B /* ObservableTypeExtensions.swift */; }; - F222D4F125249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4DF25249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift */; }; - F222D4F225249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4DF25249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift */; }; - F222D4F325249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4DF25249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift */; }; - F222D4F425249B9B00672E7B /* PrimitiveSequenceTypeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4DF25249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift */; }; F222D4F525249B9B00672E7B /* DispatchQueueExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4E125249B9A00672E7B /* DispatchQueueExtensions.swift */; }; F222D4F625249B9B00672E7B /* DispatchQueueExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4E125249B9A00672E7B /* DispatchQueueExtensions.swift */; }; F222D4F725249B9B00672E7B /* DispatchQueueExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F222D4E125249B9A00672E7B /* DispatchQueueExtensions.swift */; }; @@ -108,21 +96,70 @@ F26C3AF922537A4600189D28 /* Mini.swift in Sources */ = {isa = PBXBuildFile; fileRef = F26C3AF722537A4600189D28 /* Mini.swift */; }; F26C3AFA22537A4600189D28 /* Mini.swift in Sources */ = {isa = PBXBuildFile; fileRef = F26C3AF722537A4600189D28 /* Mini.swift */; }; F26C3AFB22537A4600189D28 /* Mini.swift in Sources */ = {isa = PBXBuildFile; fileRef = F26C3AF722537A4600189D28 /* Mini.swift */; }; + F288761028649AFE0069790E /* AnyPublisherExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288760F28649AFE0069790E /* AnyPublisherExtensions.swift */; }; + F288761128649AFE0069790E /* AnyPublisherExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288760F28649AFE0069790E /* AnyPublisherExtensions.swift */; }; + F288761228649AFE0069790E /* AnyPublisherExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288760F28649AFE0069790E /* AnyPublisherExtensions.swift */; }; + F288761328649AFE0069790E /* AnyPublisherExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288760F28649AFE0069790E /* AnyPublisherExtensions.swift */; }; + F288761528649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288761428649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift */; }; + F288761628649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288761428649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift */; }; + F288761728649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288761428649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift */; }; + F288761828649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288761428649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift */; }; + F288DCC929BA8BF600FBFED1 /* None.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCC829BA8BF600FBFED1 /* None.swift */; }; + F288DCCA29BA8BF600FBFED1 /* None.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCC829BA8BF600FBFED1 /* None.swift */; }; + F288DCCB29BA8BF600FBFED1 /* None.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCC829BA8BF600FBFED1 /* None.swift */; }; + F288DCCC29BA8BF600FBFED1 /* None.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCC829BA8BF600FBFED1 /* None.swift */; }; + F288DCCD29BA906800FBFED1 /* PublishersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AD824D286B7065005C024F /* PublishersTests.swift */; }; + F288DCCF29BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCCE29BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift */; }; + F288DCD029BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCCE29BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift */; }; + F288DCD129BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCCE29BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift */; }; + F288DCD229BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCCE29BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift */; }; + F288DCD429BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCD329BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift */; }; + F288DCD529BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCD329BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift */; }; + F288DCD629BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCD329BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift */; }; + F288DCD729BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCD329BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift */; }; + F288DCD929BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCD829BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift */; }; + F288DCDA29BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCD829BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift */; }; + F288DCDB29BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCD829BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift */; }; + F288DCDC29BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288DCD829BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift */; }; + F297D269286A02E200323F24 /* KeyedPayloadAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F297D268286A02E200323F24 /* KeyedPayloadAction.swift */; }; + F297D26A286A02E200323F24 /* KeyedPayloadAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F297D268286A02E200323F24 /* KeyedPayloadAction.swift */; }; + F297D26B286A02E200323F24 /* KeyedPayloadAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F297D268286A02E200323F24 /* KeyedPayloadAction.swift */; }; + F297D26C286A02E200323F24 /* KeyedPayloadAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F297D268286A02E200323F24 /* KeyedPayloadAction.swift */; }; + F297D27E286A0C6900323F24 /* Dispatcher+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = F297D27D286A0C6900323F24 /* Dispatcher+Combine.swift */; }; + F297D27F286A0C6900323F24 /* Dispatcher+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = F297D27D286A0C6900323F24 /* Dispatcher+Combine.swift */; }; + F297D280286A0C6900323F24 /* Dispatcher+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = F297D27D286A0C6900323F24 /* Dispatcher+Combine.swift */; }; + F297D281286A0C6900323F24 /* Dispatcher+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = F297D27D286A0C6900323F24 /* Dispatcher+Combine.swift */; }; + F2AD8249286B6AD9005C024F /* TaskExpiration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AD8248286B6AD9005C024F /* TaskExpiration.swift */; }; + F2AD824A286B6AD9005C024F /* TaskExpiration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AD8248286B6AD9005C024F /* TaskExpiration.swift */; }; + F2AD824B286B6AD9005C024F /* TaskExpiration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AD8248286B6AD9005C024F /* TaskExpiration.swift */; }; + F2AD824C286B6AD9005C024F /* TaskExpiration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AD8248286B6AD9005C024F /* TaskExpiration.swift */; }; + F2AD8252286B7087005C024F /* PublishersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AD824D286B7065005C024F /* PublishersTests.swift */; }; + F2AD8254286B7088005C024F /* PublishersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AD824D286B7065005C024F /* PublishersTests.swift */; }; + F2C09DAC286B1490009C9C8E /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DAB286B1490009C9C8E /* TestError.swift */; }; + F2C09DAD286B1490009C9C8E /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DAB286B1490009C9C8E /* TestError.swift */; }; + F2C09DAE286B1490009C9C8E /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DAB286B1490009C9C8E /* TestError.swift */; }; + F2C09DB0286B14B7009C9C8E /* TestActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DAF286B14B7009C9C8E /* TestActions.swift */; }; + F2C09DB1286B14B7009C9C8E /* TestActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DAF286B14B7009C9C8E /* TestActions.swift */; }; + F2C09DB2286B14B7009C9C8E /* TestActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DAF286B14B7009C9C8E /* TestActions.swift */; }; + F2C09DB4286B1598009C9C8E /* TestServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DB3286B1598009C9C8E /* TestServices.swift */; }; + F2C09DB5286B1598009C9C8E /* TestServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DB3286B1598009C9C8E /* TestServices.swift */; }; + F2C09DB6286B1598009C9C8E /* TestServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DB3286B1598009C9C8E /* TestServices.swift */; }; + F2C09DB8286B2672009C9C8E /* TestState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DB7286B2672009C9C8E /* TestState.swift */; }; + F2C09DB9286B2672009C9C8E /* TestState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DB7286B2672009C9C8E /* TestState.swift */; }; + F2C09DBA286B2672009C9C8E /* TestState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DB7286B2672009C9C8E /* TestState.swift */; }; + F2C09DBC286B2698009C9C8E /* TestStoreController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DBB286B2698009C9C8E /* TestStoreController.swift */; }; + F2C09DBD286B2698009C9C8E /* TestStoreController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DBB286B2698009C9C8E /* TestStoreController.swift */; }; + F2C09DBE286B2698009C9C8E /* TestStoreController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DBB286B2698009C9C8E /* TestStoreController.swift */; }; + F2C09DC0286B530D009C9C8E /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DBF286B530D009C9C8E /* ActionTests.swift */; }; + F2C09DC1286B530D009C9C8E /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DBF286B530D009C9C8E /* ActionTests.swift */; }; + F2C09DC2286B530D009C9C8E /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DBF286B530D009C9C8E /* ActionTests.swift */; }; + F2C09DC4286B57EA009C9C8E /* TaskStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DC3286B57EA009C9C8E /* TaskStatus.swift */; }; + F2C09DC5286B57EA009C9C8E /* TaskStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DC3286B57EA009C9C8E /* TaskStatus.swift */; }; + F2C09DC6286B57EA009C9C8E /* TaskStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DC3286B57EA009C9C8E /* TaskStatus.swift */; }; + F2C09DC7286B57EA009C9C8E /* TaskStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C09DC3286B57EA009C9C8E /* TaskStatus.swift */; }; F2DF4A2B26C2B69A00C082CF /* SharedDictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A2A26C2B69A00C082CF /* SharedDictionaryTests.swift */; }; F2DF4A2C26C2B69A00C082CF /* SharedDictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A2A26C2B69A00C082CF /* SharedDictionaryTests.swift */; }; F2DF4A2D26C2B69A00C082CF /* SharedDictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A2A26C2B69A00C082CF /* SharedDictionaryTests.swift */; }; - F2DF4A3226C2C31400C082CF /* Dispatcher+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A2E26C2C29300C082CF /* Dispatcher+Rx.swift */; }; - F2DF4A3326C2C31500C082CF /* Dispatcher+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A2E26C2C29300C082CF /* Dispatcher+Rx.swift */; }; - F2DF4A3426C2C31600C082CF /* Dispatcher+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A2E26C2C29300C082CF /* Dispatcher+Rx.swift */; }; - F2DF4A3526C2C31700C082CF /* Dispatcher+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A2E26C2C29300C082CF /* Dispatcher+Rx.swift */; }; - F2DF4A3A26C2DACC00C082CF /* DemandBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A3626C2DAC300C082CF /* DemandBuffer.swift */; }; - F2DF4A3B26C2DACD00C082CF /* DemandBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A3626C2DAC300C082CF /* DemandBuffer.swift */; }; - F2DF4A3C26C2DACE00C082CF /* DemandBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A3626C2DAC300C082CF /* DemandBuffer.swift */; }; - F2DF4A3D26C2DACE00C082CF /* DemandBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A3626C2DAC300C082CF /* DemandBuffer.swift */; }; - F2DF4A4026C2DBDC00C082CF /* Store+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A3F26C2DBDC00C082CF /* Store+Combine.swift */; }; - F2DF4A4126C2DBDC00C082CF /* Store+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A3F26C2DBDC00C082CF /* Store+Combine.swift */; }; - F2DF4A4226C2DBDC00C082CF /* Store+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A3F26C2DBDC00C082CF /* Store+Combine.swift */; }; - F2DF4A4326C2DBDC00C082CF /* Store+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF4A3F26C2DBDC00C082CF /* Store+Combine.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -160,7 +197,6 @@ E28124322122CC4C00E4F6D0 /* Mini.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Mini.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E281243A2122CC4C00E4F6D0 /* Mini-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Mini-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; E281244E2122CC5B00E4F6D0 /* Mini.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Mini.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F20396B025E7C7D1002DBD08 /* RxSwift.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = RxSwift.xcframework; path = Carthage/Build/RxSwift.xcframework; sourceTree = ""; }; F222D4A625249B7D00672E7B /* Dispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dispatcher.swift; sourceTree = ""; }; F222D4A725249B7D00672E7B /* Task.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = ""; }; F222D4A825249B7D00672E7B /* Middleware.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Middleware.swift; sourceTree = ""; }; @@ -170,8 +206,6 @@ F222D4AC25249B7D00672E7B /* ServiceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceType.swift; sourceTree = ""; }; F222D4AD25249B7D00672E7B /* StateType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateType.swift; sourceTree = ""; }; F222D4AE25249B7D00672E7B /* Store.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = ""; }; - F222D4DE25249B9A00672E7B /* ObservableTypeExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableTypeExtensions.swift; sourceTree = ""; }; - F222D4DF25249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimitiveSequenceTypeExtensions.swift; sourceTree = ""; }; F222D4E125249B9A00672E7B /* DispatchQueueExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DispatchQueueExtensions.swift; sourceTree = ""; }; F222D4E325249B9A00672E7B /* DictionaryExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DictionaryExtensions.swift; sourceTree = ""; }; F222D4E425249B9A00672E7B /* PayloadAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayloadAction.swift; sourceTree = ""; }; @@ -185,10 +219,24 @@ F222D7692525361700672E7B /* KeyedTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedTask.swift; sourceTree = ""; }; F222D7752525373B00672E7B /* KeyedTaskTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedTaskTests.swift; sourceTree = ""; }; F26C3AF722537A4600189D28 /* Mini.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mini.swift; sourceTree = ""; }; + F288760F28649AFE0069790E /* AnyPublisherExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyPublisherExtensions.swift; sourceTree = ""; }; + F288761428649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Publishers.CombineMiniTasksTuple2.swift; sourceTree = ""; }; + F288DCC829BA8BF600FBFED1 /* None.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = None.swift; sourceTree = ""; }; + F288DCCE29BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Publishers.CombineMiniTasksTuple4.swift; sourceTree = ""; }; + F288DCD329BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Publishers.CombineMiniTasksTuple3.swift; sourceTree = ""; }; + F288DCD829BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Publishers.CombineMiniTasksArray.swift; sourceTree = ""; }; + F297D268286A02E200323F24 /* KeyedPayloadAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedPayloadAction.swift; sourceTree = ""; }; + F297D27D286A0C6900323F24 /* Dispatcher+Combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dispatcher+Combine.swift"; sourceTree = ""; }; + F2AD8248286B6AD9005C024F /* TaskExpiration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskExpiration.swift; sourceTree = ""; }; + F2AD824D286B7065005C024F /* PublishersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublishersTests.swift; sourceTree = ""; }; + F2C09DAB286B1490009C9C8E /* TestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = ""; }; + F2C09DAF286B14B7009C9C8E /* TestActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestActions.swift; sourceTree = ""; }; + F2C09DB3286B1598009C9C8E /* TestServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestServices.swift; sourceTree = ""; }; + F2C09DB7286B2672009C9C8E /* TestState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestState.swift; sourceTree = ""; }; + F2C09DBB286B2698009C9C8E /* TestStoreController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestStoreController.swift; sourceTree = ""; }; + F2C09DBF286B530D009C9C8E /* ActionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionTests.swift; sourceTree = ""; }; + F2C09DC3286B57EA009C9C8E /* TaskStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskStatus.swift; sourceTree = ""; }; F2DF4A2A26C2B69A00C082CF /* SharedDictionaryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedDictionaryTests.swift; sourceTree = ""; }; - F2DF4A2E26C2C29300C082CF /* Dispatcher+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dispatcher+Rx.swift"; sourceTree = ""; }; - F2DF4A3626C2DAC300C082CF /* DemandBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemandBuffer.swift; sourceTree = ""; }; - F2DF4A3F26C2DBDC00C082CF /* Store+Combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Store+Combine.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -196,7 +244,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F20396B125E7C7D1002DBD08 /* RxSwift.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -212,7 +259,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F20396B225E7C7D1002DBD08 /* RxSwift.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -228,7 +274,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F20396B325E7C7D1002DBD08 /* RxSwift.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -244,7 +289,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F20396B425E7C7D1002DBD08 /* RxSwift.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -256,7 +300,6 @@ children = ( E26053F02121880000D013B5 /* Sources */, E26053FB2121880000D013B5 /* Tests */, - E28124592122CD2500E4F6D0 /* Frameworks */, E26053EF212187FF00D013B5 /* Products */, ); sourceTree = ""; @@ -278,20 +321,23 @@ E26053F02121880000D013B5 /* Sources */ = { isa = PBXGroup; children = ( - F222D4DC25249B9A00672E7B /* Utils */, + E26053F12121880000D013B5 /* Mini.h */, + E26053F22121880000D013B5 /* Info.plist */, F222D4AB25249B7D00672E7B /* Action.swift */, F222D4AA25249B7D00672E7B /* ActionReducer.swift */, F222D4A625249B7D00672E7B /* Dispatcher.swift */, + F222D7692525361700672E7B /* KeyedTask.swift */, F222D4A825249B7D00672E7B /* Middleware.swift */, + F26C3AF722537A4600189D28 /* Mini.swift */, + F288DCC829BA8BF600FBFED1 /* None.swift */, F222D4A925249B7D00672E7B /* ReducerGroup.swift */, F222D4AC25249B7D00672E7B /* ServiceType.swift */, F222D4AD25249B7D00672E7B /* StateType.swift */, F222D4AE25249B7D00672E7B /* Store.swift */, F222D4A725249B7D00672E7B /* Task.swift */, - F222D7692525361700672E7B /* KeyedTask.swift */, - F26C3AF722537A4600189D28 /* Mini.swift */, - E26053F12121880000D013B5 /* Mini.h */, - E26053F22121880000D013B5 /* Info.plist */, + F2AD8248286B6AD9005C024F /* TaskExpiration.swift */, + F2C09DC3286B57EA009C9C8E /* TaskStatus.swift */, + F222D4DC25249B9A00672E7B /* Utils */, ); path = Sources; sourceTree = ""; @@ -300,62 +346,63 @@ isa = PBXGroup; children = ( E26053FE2121880000D013B5 /* Info.plist */, + F2C09DBF286B530D009C9C8E /* ActionTests.swift */, F222D52025249BD900672E7B /* ChainTests.swift */, F222D52525249BD900672E7B /* DictionaryExtensionsTests.swift */, F222D52225249BD900672E7B /* DispatcherTests.swift */, - F222D52125249BD900672E7B /* ReducerTests.swift */, - F222D52325249BD900672E7B /* TaskTests.swift */, F222D7752525373B00672E7B /* KeyedTaskTests.swift */, + F2AD824D286B7065005C024F /* PublishersTests.swift */, + F222D52125249BD900672E7B /* ReducerTests.swift */, F2DF4A2A26C2B69A00C082CF /* SharedDictionaryTests.swift */, + F222D52325249BD900672E7B /* TaskTests.swift */, + F2C09DAA286B147F009C9C8E /* Helpers */, ); path = Tests; sourceTree = ""; }; - E28124592122CD2500E4F6D0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - F20396B025E7C7D1002DBD08 /* RxSwift.xcframework */, - ); - name = Frameworks; - sourceTree = ""; - }; F222D4DC25249B9A00672E7B /* Utils */ = { isa = PBXGroup; children = ( - F2DF4A3E26C2DBCB00C082CF /* Combine */, - F222D4DD25249B9A00672E7B /* RxSwift */, - F222D4E025249B9A00672E7B /* Foundation */, - F222D4E425249B9A00672E7B /* PayloadAction.swift */, + F297D268286A02E200323F24 /* KeyedPayloadAction.swift */, F222D4E525249B9A00672E7B /* OrderedSet.swift */, + F222D4E425249B9A00672E7B /* PayloadAction.swift */, F222D4E625249B9A00672E7B /* SharedDictionary.swift */, + F2DF4A3E26C2DBCB00C082CF /* Combine */, + F222D4E025249B9A00672E7B /* Foundation */, ); path = Utils; sourceTree = ""; }; - F222D4DD25249B9A00672E7B /* RxSwift */ = { + F222D4E025249B9A00672E7B /* Foundation */ = { isa = PBXGroup; children = ( - F222D4DE25249B9A00672E7B /* ObservableTypeExtensions.swift */, - F222D4DF25249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift */, - F2DF4A2E26C2C29300C082CF /* Dispatcher+Rx.swift */, + F222D4E125249B9A00672E7B /* DispatchQueueExtensions.swift */, + F222D4E325249B9A00672E7B /* DictionaryExtensions.swift */, ); - path = RxSwift; + path = Foundation; sourceTree = ""; }; - F222D4E025249B9A00672E7B /* Foundation */ = { + F2C09DAA286B147F009C9C8E /* Helpers */ = { isa = PBXGroup; children = ( - F222D4E125249B9A00672E7B /* DispatchQueueExtensions.swift */, - F222D4E325249B9A00672E7B /* DictionaryExtensions.swift */, + F2C09DAF286B14B7009C9C8E /* TestActions.swift */, + F2C09DAB286B1490009C9C8E /* TestError.swift */, + F2C09DB3286B1598009C9C8E /* TestServices.swift */, + F2C09DB7286B2672009C9C8E /* TestState.swift */, + F2C09DBB286B2698009C9C8E /* TestStoreController.swift */, ); - path = Foundation; + path = Helpers; sourceTree = ""; }; F2DF4A3E26C2DBCB00C082CF /* Combine */ = { isa = PBXGroup; children = ( - F2DF4A3F26C2DBDC00C082CF /* Store+Combine.swift */, - F2DF4A3626C2DAC300C082CF /* DemandBuffer.swift */, + F288760F28649AFE0069790E /* AnyPublisherExtensions.swift */, + F288761428649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift */, + F288DCD329BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift */, + F288DCCE29BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift */, + F288DCD829BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift */, + F297D27D286A0C6900323F24 /* Dispatcher+Combine.swift */, ); path = Combine; sourceTree = ""; @@ -403,9 +450,9 @@ buildConfigurationList = E26054022121880000D013B5 /* Build configuration list for PBXNativeTarget "Mini-iOS" */; buildPhases = ( E260540821218AF400D013B5 /* Swiftlint */, + E26053EB212187FF00D013B5 /* Headers */, E26053E9212187FF00D013B5 /* Sources */, E26053EA212187FF00D013B5 /* Frameworks */, - E26053EB212187FF00D013B5 /* Headers */, E26053EC212187FF00D013B5 /* Resources */, ); buildRules = ( @@ -440,9 +487,9 @@ buildConfigurationList = E28123EC2122C2CB00E4F6D0 /* Build configuration list for PBXNativeTarget "Mini-macOS" */; buildPhases = ( E20BA60521389BE0005BA13B /* Swiftlint */, + E28123D42122C2CB00E4F6D0 /* Headers */, E28123D22122C2CB00E4F6D0 /* Sources */, E28123D32122C2CB00E4F6D0 /* Frameworks */, - E28123D42122C2CB00E4F6D0 /* Headers */, E28123D52122C2CB00E4F6D0 /* Resources */, ); buildRules = ( @@ -477,9 +524,9 @@ buildConfigurationList = E28124432122CC4C00E4F6D0 /* Build configuration list for PBXNativeTarget "Mini-tvOS" */; buildPhases = ( E20BA60621389BF7005BA13B /* Swiftlint */, + E281242F2122CC4C00E4F6D0 /* Headers */, E281242D2122CC4C00E4F6D0 /* Sources */, E281242E2122CC4C00E4F6D0 /* Frameworks */, - E281242F2122CC4C00E4F6D0 /* Headers */, E28124302122CC4C00E4F6D0 /* Resources */, ); buildRules = ( @@ -513,9 +560,9 @@ isa = PBXNativeTarget; buildConfigurationList = E28124532122CC5C00E4F6D0 /* Build configuration list for PBXNativeTarget "Mini-watchOS" */; buildPhases = ( + E281244B2122CC5B00E4F6D0 /* Headers */, E28124492122CC5B00E4F6D0 /* Sources */, E281244A2122CC5B00E4F6D0 /* Frameworks */, - E281244B2122CC5B00E4F6D0 /* Headers */, E281244C2122CC5B00E4F6D0 /* Resources */, ); buildRules = ( @@ -534,7 +581,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0940; - LastUpgradeCheck = 1250; + LastUpgradeCheck = 1420; ORGANIZATIONNAME = S3BA; TargetAttributes = { E26053ED212187FF00D013B5 = { @@ -646,6 +693,7 @@ /* Begin PBXShellScriptBuildPhase section */ E20BA60521389BE0005BA13B /* Swiftlint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -656,10 +704,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\nfi\n\nexport PATH\n\nif which swiftlint >/dev/null; then\nswiftlint autocorrect\nswiftlint\nelse\necho \"warning: SwiftLint not installed, run: brew install swiftlint\"\nfi\n"; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nmint run swiftlint --fix\nmint run swiftlint\n"; }; E20BA60621389BF7005BA13B /* Swiftlint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -670,10 +719,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\nfi\n\nexport PATH\n\nif which swiftlint >/dev/null; then\nswiftlint autocorrect\nswiftlint\nelse\necho \"warning: SwiftLint not installed, run: brew install swiftlint\"\nfi\n"; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nmint run swiftlint --fix\nmint run swiftlint\n"; }; E260540821218AF400D013B5 /* Swiftlint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -684,7 +734,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\nfi\n\nexport PATH\n\nif which swiftlint >/dev/null; then\nswiftlint autocorrect\nswiftlint\nelse\necho \"warning: SwiftLint not installed, run: brew install swiftlint\"\nfi\n"; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nmint run swiftlint --fix\nmint run swiftlint\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -693,26 +743,31 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F2DF4A3A26C2DACC00C082CF /* DemandBuffer.swift in Sources */, F222D50525249B9B00672E7B /* OrderedSet.swift in Sources */, F222D4CB25249B7E00672E7B /* StateType.swift in Sources */, F222D76A2525361700672E7B /* KeyedTask.swift in Sources */, F222D4AF25249B7E00672E7B /* Dispatcher.swift in Sources */, F222D4BB25249B7E00672E7B /* ReducerGroup.swift in Sources */, F222D4B725249B7E00672E7B /* Middleware.swift in Sources */, + F297D27E286A0C6900323F24 /* Dispatcher+Combine.swift in Sources */, + F288761528649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift in Sources */, + F297D269286A02E200323F24 /* KeyedPayloadAction.swift in Sources */, F222D4B325249B7E00672E7B /* Task.swift in Sources */, + F288761028649AFE0069790E /* AnyPublisherExtensions.swift in Sources */, + F288DCC929BA8BF600FBFED1 /* None.swift in Sources */, F222D4CF25249B7E00672E7B /* Store.swift in Sources */, + F288DCCF29BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift in Sources */, + F288DCD429BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift in Sources */, F222D4FD25249B9B00672E7B /* DictionaryExtensions.swift in Sources */, - F2DF4A4026C2DBDC00C082CF /* Store+Combine.swift in Sources */, F222D4C325249B7E00672E7B /* Action.swift in Sources */, - F222D4ED25249B9A00672E7B /* ObservableTypeExtensions.swift in Sources */, F222D4F525249B9B00672E7B /* DispatchQueueExtensions.swift in Sources */, F222D4BF25249B7E00672E7B /* ActionReducer.swift in Sources */, F26C3AF822537A4600189D28 /* Mini.swift in Sources */, + F2AD8249286B6AD9005C024F /* TaskExpiration.swift in Sources */, F222D4C725249B7E00672E7B /* ServiceType.swift in Sources */, F222D50125249B9B00672E7B /* PayloadAction.swift in Sources */, - F222D4F125249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift in Sources */, - F2DF4A3226C2C31400C082CF /* Dispatcher+Rx.swift in Sources */, + F288DCD929BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift in Sources */, + F2C09DC4286B57EA009C9C8E /* TaskStatus.swift in Sources */, F222D50925249B9B00672E7B /* SharedDictionary.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -721,11 +776,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F2C09DB0286B14B7009C9C8E /* TestActions.swift in Sources */, + F2C09DBC286B2698009C9C8E /* TestStoreController.swift in Sources */, + F2AD8252286B7087005C024F /* PublishersTests.swift in Sources */, F222D52C25249BD900672E7B /* ReducerTests.swift in Sources */, F222D53225249BD900672E7B /* TaskTests.swift in Sources */, + F2C09DB8286B2672009C9C8E /* TestState.swift in Sources */, F222D52F25249BD900672E7B /* DispatcherTests.swift in Sources */, + F2C09DC0286B530D009C9C8E /* ActionTests.swift in Sources */, F222D7762525373B00672E7B /* KeyedTaskTests.swift in Sources */, + F2C09DAC286B1490009C9C8E /* TestError.swift in Sources */, F2DF4A2B26C2B69A00C082CF /* SharedDictionaryTests.swift in Sources */, + F2C09DB4286B1598009C9C8E /* TestServices.swift in Sources */, F222D53825249BD900672E7B /* DictionaryExtensionsTests.swift in Sources */, F222D52925249BD900672E7B /* ChainTests.swift in Sources */, ); @@ -735,26 +797,31 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F2DF4A3B26C2DACD00C082CF /* DemandBuffer.swift in Sources */, F222D50625249B9B00672E7B /* OrderedSet.swift in Sources */, F222D4CC25249B7E00672E7B /* StateType.swift in Sources */, F222D76B2525361700672E7B /* KeyedTask.swift in Sources */, F222D4B025249B7E00672E7B /* Dispatcher.swift in Sources */, F222D4BC25249B7E00672E7B /* ReducerGroup.swift in Sources */, F222D4B825249B7E00672E7B /* Middleware.swift in Sources */, + F297D27F286A0C6900323F24 /* Dispatcher+Combine.swift in Sources */, + F288761628649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift in Sources */, + F297D26A286A02E200323F24 /* KeyedPayloadAction.swift in Sources */, F222D4B425249B7E00672E7B /* Task.swift in Sources */, + F288761128649AFE0069790E /* AnyPublisherExtensions.swift in Sources */, + F288DCCA29BA8BF600FBFED1 /* None.swift in Sources */, F222D4D025249B7E00672E7B /* Store.swift in Sources */, + F288DCD029BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift in Sources */, + F288DCD529BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift in Sources */, F222D4FE25249B9B00672E7B /* DictionaryExtensions.swift in Sources */, - F2DF4A4126C2DBDC00C082CF /* Store+Combine.swift in Sources */, F222D4C425249B7E00672E7B /* Action.swift in Sources */, - F222D4EE25249B9A00672E7B /* ObservableTypeExtensions.swift in Sources */, F222D4F625249B9B00672E7B /* DispatchQueueExtensions.swift in Sources */, F222D4C025249B7E00672E7B /* ActionReducer.swift in Sources */, F26C3AF922537A4600189D28 /* Mini.swift in Sources */, + F2AD824A286B6AD9005C024F /* TaskExpiration.swift in Sources */, F222D4C825249B7E00672E7B /* ServiceType.swift in Sources */, F222D50225249B9B00672E7B /* PayloadAction.swift in Sources */, - F222D4F225249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift in Sources */, - F2DF4A3326C2C31500C082CF /* Dispatcher+Rx.swift in Sources */, + F288DCDA29BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift in Sources */, + F2C09DC5286B57EA009C9C8E /* TaskStatus.swift in Sources */, F222D50A25249B9B00672E7B /* SharedDictionary.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -763,11 +830,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F2C09DB1286B14B7009C9C8E /* TestActions.swift in Sources */, + F2C09DBD286B2698009C9C8E /* TestStoreController.swift in Sources */, + F288DCCD29BA906800FBFED1 /* PublishersTests.swift in Sources */, F222D52D25249BD900672E7B /* ReducerTests.swift in Sources */, F222D53325249BD900672E7B /* TaskTests.swift in Sources */, + F2C09DB9286B2672009C9C8E /* TestState.swift in Sources */, F222D53025249BD900672E7B /* DispatcherTests.swift in Sources */, + F2C09DC1286B530D009C9C8E /* ActionTests.swift in Sources */, F222D7772525373B00672E7B /* KeyedTaskTests.swift in Sources */, + F2C09DAD286B1490009C9C8E /* TestError.swift in Sources */, F2DF4A2C26C2B69A00C082CF /* SharedDictionaryTests.swift in Sources */, + F2C09DB5286B1598009C9C8E /* TestServices.swift in Sources */, F222D53925249BD900672E7B /* DictionaryExtensionsTests.swift in Sources */, F222D52A25249BD900672E7B /* ChainTests.swift in Sources */, ); @@ -777,26 +851,31 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F2DF4A3C26C2DACE00C082CF /* DemandBuffer.swift in Sources */, F222D50725249B9B00672E7B /* OrderedSet.swift in Sources */, F222D4CD25249B7E00672E7B /* StateType.swift in Sources */, F222D76C2525361700672E7B /* KeyedTask.swift in Sources */, F222D4B125249B7E00672E7B /* Dispatcher.swift in Sources */, F222D4BD25249B7E00672E7B /* ReducerGroup.swift in Sources */, F222D4B925249B7E00672E7B /* Middleware.swift in Sources */, + F297D280286A0C6900323F24 /* Dispatcher+Combine.swift in Sources */, + F288761728649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift in Sources */, + F297D26B286A02E200323F24 /* KeyedPayloadAction.swift in Sources */, F222D4B525249B7E00672E7B /* Task.swift in Sources */, + F288761228649AFE0069790E /* AnyPublisherExtensions.swift in Sources */, + F288DCCB29BA8BF600FBFED1 /* None.swift in Sources */, F222D4D125249B7E00672E7B /* Store.swift in Sources */, + F288DCD129BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift in Sources */, + F288DCD629BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift in Sources */, F222D4FF25249B9B00672E7B /* DictionaryExtensions.swift in Sources */, - F2DF4A4226C2DBDC00C082CF /* Store+Combine.swift in Sources */, F222D4C525249B7E00672E7B /* Action.swift in Sources */, - F222D4EF25249B9A00672E7B /* ObservableTypeExtensions.swift in Sources */, F222D4F725249B9B00672E7B /* DispatchQueueExtensions.swift in Sources */, F222D4C125249B7E00672E7B /* ActionReducer.swift in Sources */, F26C3AFA22537A4600189D28 /* Mini.swift in Sources */, + F2AD824B286B6AD9005C024F /* TaskExpiration.swift in Sources */, F222D4C925249B7E00672E7B /* ServiceType.swift in Sources */, F222D50325249B9B00672E7B /* PayloadAction.swift in Sources */, - F222D4F325249B9A00672E7B /* PrimitiveSequenceTypeExtensions.swift in Sources */, - F2DF4A3426C2C31600C082CF /* Dispatcher+Rx.swift in Sources */, + F288DCDB29BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift in Sources */, + F2C09DC6286B57EA009C9C8E /* TaskStatus.swift in Sources */, F222D50B25249B9B00672E7B /* SharedDictionary.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -805,11 +884,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F2C09DB2286B14B7009C9C8E /* TestActions.swift in Sources */, + F2C09DBE286B2698009C9C8E /* TestStoreController.swift in Sources */, + F2AD8254286B7088005C024F /* PublishersTests.swift in Sources */, F222D52E25249BD900672E7B /* ReducerTests.swift in Sources */, F222D53425249BD900672E7B /* TaskTests.swift in Sources */, + F2C09DBA286B2672009C9C8E /* TestState.swift in Sources */, F222D53125249BD900672E7B /* DispatcherTests.swift in Sources */, + F2C09DC2286B530D009C9C8E /* ActionTests.swift in Sources */, F222D7782525373B00672E7B /* KeyedTaskTests.swift in Sources */, + F2C09DAE286B1490009C9C8E /* TestError.swift in Sources */, F2DF4A2D26C2B69A00C082CF /* SharedDictionaryTests.swift in Sources */, + F2C09DB6286B1598009C9C8E /* TestServices.swift in Sources */, F222D53A25249BD900672E7B /* DictionaryExtensionsTests.swift in Sources */, F222D52B25249BD900672E7B /* ChainTests.swift in Sources */, ); @@ -819,26 +905,31 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F2DF4A3D26C2DACE00C082CF /* DemandBuffer.swift in Sources */, F222D50825249B9B00672E7B /* OrderedSet.swift in Sources */, F222D4CE25249B7E00672E7B /* StateType.swift in Sources */, F222D76D2525361700672E7B /* KeyedTask.swift in Sources */, F222D4B225249B7E00672E7B /* Dispatcher.swift in Sources */, F222D4BE25249B7E00672E7B /* ReducerGroup.swift in Sources */, F222D4BA25249B7E00672E7B /* Middleware.swift in Sources */, + F297D281286A0C6900323F24 /* Dispatcher+Combine.swift in Sources */, + F288761828649B1C0069790E /* Publishers.CombineMiniTasksTuple2.swift in Sources */, + F297D26C286A02E200323F24 /* KeyedPayloadAction.swift in Sources */, F222D4B625249B7E00672E7B /* Task.swift in Sources */, + F288761328649AFE0069790E /* AnyPublisherExtensions.swift in Sources */, + F288DCCC29BA8BF600FBFED1 /* None.swift in Sources */, F222D4D225249B7E00672E7B /* Store.swift in Sources */, + F288DCD229BB922700FBFED1 /* Publishers.CombineMiniTasksTuple4.swift in Sources */, + F288DCD729BB922F00FBFED1 /* Publishers.CombineMiniTasksTuple3.swift in Sources */, F222D50025249B9B00672E7B /* DictionaryExtensions.swift in Sources */, - F2DF4A4326C2DBDC00C082CF /* Store+Combine.swift in Sources */, F222D4C625249B7E00672E7B /* Action.swift in Sources */, - F222D4F025249B9A00672E7B /* ObservableTypeExtensions.swift in Sources */, F222D4F825249B9B00672E7B /* DispatchQueueExtensions.swift in Sources */, F222D4C225249B7E00672E7B /* ActionReducer.swift in Sources */, F26C3AFB22537A4600189D28 /* Mini.swift in Sources */, + F2AD824C286B6AD9005C024F /* TaskExpiration.swift in Sources */, F222D4CA25249B7E00672E7B /* ServiceType.swift in Sources */, F222D50425249B9B00672E7B /* PayloadAction.swift in Sources */, - F222D4F425249B9B00672E7B /* PrimitiveSequenceTypeExtensions.swift in Sources */, - F2DF4A3526C2C31700C082CF /* Dispatcher+Rx.swift in Sources */, + F288DCDC29BB942100FBFED1 /* Publishers.CombineMiniTasksArray.swift in Sources */, + F2C09DC7286B57EA009C9C8E /* TaskStatus.swift in Sources */, F222D50C25249B9B00672E7B /* SharedDictionary.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1007,10 +1098,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( @@ -1038,10 +1126,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( @@ -1064,16 +1149,12 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", - "$(PROJECT_DIR)/Carthage/Build/", ); PRODUCT_BUNDLE_IDENTIFIER = com.masmovil.MiniTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1090,16 +1171,12 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", - "$(PROJECT_DIR)/Carthage/Build/", ); PRODUCT_BUNDLE_IDENTIFIER = com.masmovil.MiniTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1115,14 +1192,12 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -1146,14 +1221,12 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -1177,16 +1250,13 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + DEAD_CODE_STRIPPING = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks", - "$(PROJECT_DIR)/Carthage/Build/", ); PRODUCT_BUNDLE_IDENTIFIER = com.masmovil.MiniTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1204,16 +1274,13 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + DEAD_CODE_STRIPPING = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks", - "$(PROJECT_DIR)/Carthage/Build/", ); PRODUCT_BUNDLE_IDENTIFIER = com.masmovil.MiniTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1233,10 +1300,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( @@ -1265,10 +1329,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( @@ -1293,16 +1354,12 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", - "$(PROJECT_DIR)/Carthage/Build/", ); PRODUCT_BUNDLE_IDENTIFIER = com.masmovil.MiniTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1321,16 +1378,12 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", - "$(PROJECT_DIR)/Carthage/Build/", ); PRODUCT_BUNDLE_IDENTIFIER = com.masmovil.MiniTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1352,10 +1405,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( @@ -1383,10 +1433,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mini.xcodeproj/xcshareddata/xcschemes/Mini-iOS.xcscheme b/Mini.xcodeproj/xcshareddata/xcschemes/Mini-iOS.xcscheme index cde07af..dcebe45 100644 --- a/Mini.xcodeproj/xcshareddata/xcschemes/Mini-iOS.xcscheme +++ b/Mini.xcodeproj/xcshareddata/xcschemes/Mini-iOS.xcscheme @@ -1,6 +1,6 @@ : Disposable { +public class Reducer: Cancellable { public let action: A.Type public let dispatcher: Dispatcher public let reducer: (A) -> Void - private var disposable: Disposable! + private var cancellable: Cancellable! public init(of action: A.Type, on dispatcher: Dispatcher, reducer: @escaping (A) -> Void) { self.action = action self.dispatcher = dispatcher self.reducer = reducer - self.disposable = build() + self.cancellable = build() } - private func build() -> Disposable { - let disposable = dispatcher.subscribe(tag: action.tag) { + private func build() -> Cancellable { + let cancelable = dispatcher.subscribe(tag: action.tag) { self.reducer($0) } - return disposable + return cancelable } - public func dispose() { - disposable.dispose() + public func cancel() { + cancellable.cancel() } } diff --git a/Sources/Dispatcher.swift b/Sources/Dispatcher.swift index b19bbc3..14aca50 100644 --- a/Sources/Dispatcher.swift +++ b/Sources/Dispatcher.swift @@ -4,7 +4,7 @@ public typealias SubscriptionMap = SharedDictionary Chain { middleware.reduce(root) { (chain: Chain, middleware: Middleware) -> Chain in - return ForwardingChain { action in + ForwardingChain { action in middleware.perform(action, chain) } } @@ -181,7 +181,7 @@ public final class Dispatcher { } } -public final class DispatcherSubscription: Comparable { +public final class DispatcherSubscription: Comparable, Equatable, Hashable { internal let dispatcher: Dispatcher public let id: Int @@ -206,6 +206,10 @@ public final class DispatcherSubscription: Comparable { completion(action) } + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + public static func == (lhs: DispatcherSubscription, rhs: DispatcherSubscription) -> Bool { lhs.id == rhs.id } diff --git a/Sources/KeyedTask.swift b/Sources/KeyedTask.swift index 7ef2386..72dde4c 100644 --- a/Sources/KeyedTask.swift +++ b/Sources/KeyedTask.swift @@ -1,8 +1,8 @@ import Foundation -public typealias KeyedTask = [K: Task] +public typealias KeyedTask = [Key: Task] -extension KeyedTask where Value: Task { +extension KeyedTask where Key: Hashable, Value: TaskType { public subscript(task key: Key) -> Value? { self[key] } diff --git a/Sources/Middleware.swift b/Sources/Middleware.swift index cad7ab3..8f71fb7 100644 --- a/Sources/Middleware.swift +++ b/Sources/Middleware.swift @@ -16,9 +16,7 @@ public final class ForwardingChain: Chain { private let next: Next public var proceed: Next { - { action in - return self.next(action) - } + { self.next($0) } } public init(next: @escaping Next) { diff --git a/Sources/None.swift b/Sources/None.swift new file mode 100644 index 0000000..05f1e20 --- /dev/null +++ b/Sources/None.swift @@ -0,0 +1,9 @@ +import Foundation + +public struct None: Equatable { + internal init() { + } + public static var none: None { + None() + } +} diff --git a/Sources/ReducerGroup.swift b/Sources/ReducerGroup.swift index fa65829..3c17edd 100644 --- a/Sources/ReducerGroup.swift +++ b/Sources/ReducerGroup.swift @@ -1,19 +1,20 @@ +import Combine import Foundation -import RxSwift -public protocol Group: Disposable { - var disposeBag: CompositeDisposable { get } +public protocol Group: Cancellable { + var cancellables: Set { get } } public class ReducerGroup: Group { - public let disposeBag = CompositeDisposable() + public var cancellables = Set() - public init(_ builder: () -> [Disposable]) { + public init(_ builder: () -> [Cancellable]) { let disposable = builder() - disposable.forEach { _ = disposeBag.insert($0) } + disposable.forEach { _ = cancellables.insert(AnyCancellable($0)) } } - public func dispose() { - disposeBag.dispose() + public func cancel() { + cancellables.removeAll() + cancellables = Set() } } diff --git a/Sources/Store.swift b/Sources/Store.swift index 4710e83..992e53f 100644 --- a/Sources/Store.swift +++ b/Sources/Store.swift @@ -1,9 +1,9 @@ +import Combine import Foundation -import RxSwift public protocol StoreType { associatedtype State: StateType - associatedtype StoreController: Disposable + associatedtype StoreController: Cancellable var state: State { get set } var dispatcher: Dispatcher { get } @@ -35,13 +35,12 @@ extension StoreType { } } -public class Store: ObservableType, StoreType { - public typealias Element = State +public class Store: Publisher, StoreType { + public typealias Output = State + public typealias Failure = Never public typealias State = State public typealias StoreController = StoreController - public typealias ObjectWillChangePublisher = BehaviorSubject - public var objectWillChange: ObjectWillChangePublisher public let dispatcher: Dispatcher public var storeController: StoreController public var state: State { @@ -52,7 +51,7 @@ public class Store: ObservableTyp queue.sync { if !newValue.isEqual(to: _state) { _state = newValue - objectWillChange.onNext(state) + objectWillChange.send(state) } } } @@ -67,7 +66,7 @@ public class Store: ObservableTyp self._initialState = state self._state = state self.dispatcher = dispatcher - self.objectWillChange = ObjectWillChangePublisher(value: state) + self.objectWillChange = .init(state) self.storeController = storeController self.state = _initialState } @@ -79,7 +78,7 @@ public class Store: ObservableTyp } public func replayOnce() { - objectWillChange.onNext(state) + objectWillChange.send(state) dispatcher.stateWasReplayed(state: state) } @@ -88,10 +87,11 @@ public class Store: ObservableTyp state = initialState } - public func subscribe(_ observer: Observer) -> Disposable where Observer.Element == Store.Element { - objectWillChange.subscribe(observer) + public func receive(subscriber: S) where Failure == S.Failure, Output == S.Input { + objectWillChange.subscribe(subscriber) } + private var objectWillChange: CurrentValueSubject private let queue = DispatchQueue(label: "atomic state") private var _initialState: State private var _state: State diff --git a/Sources/Task.swift b/Sources/Task.swift index e18e046..40c07b5 100644 --- a/Sources/Task.swift +++ b/Sources/Task.swift @@ -1,60 +1,63 @@ import Foundation -public typealias Task = TypedTask - -public class TypedTask: Equatable, CustomDebugStringConvertible { - public enum Status { - case idle - case running - case success - case failure - } - - public enum Expiration { - case immediately - case short - case long - case custom(TimeInterval) - - public var value: TimeInterval { - switch self { - case .immediately: - return 0 - - case .short: - return 60 +public typealias EmptyTask = Task + +public protocol TaskType { + associatedtype Payload: Equatable + associatedtype Failure: Error + + var isIdle: Bool { get } + var isRunning: Bool { get } + var isRecentlySucceeded: Bool { get } + var isTerminal: Bool { get } + var isSuccessful: Bool { get } + var isFailure: Bool { get } + + var status: TaskStatus { get } + var payload: Payload? { get } + var error: Failure? { get } +} - case .long: - return 180 +public class Task: TaskType, Equatable, CustomDebugStringConvertible { + public typealias Payload = T + public typealias Failure = E - case .custom(let value): - return value - } - } - } - - public let status: Status + public let status: TaskStatus public let started: Date - public let expiration: Expiration - public let data: T? + public let expiration: TaskExpiration public let tag: String? public let progress: Decimal? - public let error: Error? - public required init(status: Status = .idle, + public required init(status: TaskStatus = .idle, started: Date = Date(), - expiration: Expiration = .immediately, - data: T? = nil, + expiration: TaskExpiration = .immediately, tag: String? = nil, - progress: Decimal? = nil, - error: Error? = nil) { + progress: Decimal? = nil) { self.status = status self.started = started self.expiration = expiration - self.data = data self.tag = tag self.progress = progress - self.error = error + } + + public var payload: Payload? { + switch status { + case .success(let payload): + return payload + + default: + return nil + } + } + + public var error: Failure? { + switch status { + case .failure(let error): + return error + + default: + return nil + } } public var isIdle: Bool { @@ -66,31 +69,59 @@ public class TypedTask: Equatable, CustomDebugStringConvertible { } public var isRecentlySucceeded: Bool { - status == .success && started.timeIntervalSinceNow + expiration.value >= 0 + switch status { + case .success where started.timeIntervalSinceNow + expiration.value >= 0: + return true + + default: + return false + } } public var isTerminal: Bool { - status == .success || status == .failure + switch status { + case .success, .failure: + return true + + default: + return false + } } public var isSuccessful: Bool { - status == .success + switch status { + case .success: + return true + + default: + return false + } } public var isFailure: Bool { - status == .failure + switch status { + case .failure: + return true + + default: + return false + } + } + + public static func requestIdle(tag: String? = nil) -> Self { + .init(status: .idle, tag: tag) } - public static func requestRunning(tag: String? = nil) -> Task { - Task(status: .running, tag: tag) + public static func requestRunning(tag: String? = nil) -> Self { + .init(status: .running, tag: tag) } - public static func requestSuccess(_ expiration: Task.Expiration = .immediately, tag: String? = nil) -> Task { - Task(status: .success, expiration: expiration, tag: tag) + public static func requestFailure(_ error: Failure, tag: String? = nil) -> Self { + .init(status: .failure(error: error), tag: tag) } - public static func requestFailure(_ error: Error, tag: String? = nil) -> Task { - Task(status: .failure, tag: tag, error: error) + public static func requestSuccess(_ payload: Payload, expiration: TaskExpiration = .immediately, tag: String? = nil) -> Self { + .init(status: .success(payload: payload), expiration: expiration, tag: tag) } // MARK: - CustomDebugStringConvertible @@ -104,13 +135,18 @@ public class TypedTask: Equatable, CustomDebugStringConvertible { return """ 🚀 Task: status: \(status), started: \(started), tag: \(tagPrint) - data: \(String(describing: data)), progress: \(String(describing: progress)) error: \(String(describing: error)) + payload: \(String(describing: payload)), progress: \(String(describing: progress)) error: \(String(describing: error)) """ } + + // MARK: Equatable + public static func == (lhs: Task, rhs: Task) -> Bool { + lhs.status == rhs.status + } } -public func == (lhs: TypedTask, rhs: TypedTask) -> Bool { - lhs.status == rhs.status && - lhs.started == rhs.started && - lhs.progress == rhs.progress +public extension Task where T == None { + static func requestSuccess(expiration: TaskExpiration = .immediately, tag: String? = nil) -> Self { + .init(status: .success(payload: .none), expiration: expiration, tag: tag) + } } diff --git a/Sources/TaskExpiration.swift b/Sources/TaskExpiration.swift new file mode 100644 index 0000000..e56ef7c --- /dev/null +++ b/Sources/TaskExpiration.swift @@ -0,0 +1,24 @@ +import Foundation + +public enum TaskExpiration { + case immediately + case short + case long + case custom(TimeInterval) + + public var value: TimeInterval { + switch self { + case .immediately: + return 0 + + case .short: + return 60 + + case .long: + return 180 + + case .custom(let value): + return value + } + } +} diff --git a/Sources/TaskStatus.swift b/Sources/TaskStatus.swift new file mode 100644 index 0000000..fe137ff --- /dev/null +++ b/Sources/TaskStatus.swift @@ -0,0 +1,23 @@ +public enum TaskStatus: Equatable { + case idle + case running + case success(payload: Payload) + case failure(error: Failure) + + public static func == (lhs: Self, rhs: Self) -> Bool { + switch (lhs, rhs) { + case (.idle, .idle), (.running, .running): + return true + + case (.success(let lhsSuccess), .success(let rhsSuccess)): + return lhsSuccess == rhsSuccess + + // All error must be treated as different. + case (.failure, .failure): + return false + + default: + return false + } + } +} diff --git a/Sources/Utils/Combine/AnyPublisherExtensions.swift b/Sources/Utils/Combine/AnyPublisherExtensions.swift new file mode 100644 index 0000000..a6391d8 --- /dev/null +++ b/Sources/Utils/Combine/AnyPublisherExtensions.swift @@ -0,0 +1,80 @@ +import Combine +import Foundation + +public extension AnyPublisher where Failure: Error { + func dispatch(action: A.Type, + expiration: TaskExpiration = .immediately, + on dispatcher: Dispatcher) + -> Cancellable where A.TaskPayload == Output, A.TaskError == Failure { + sink { completion in + switch completion { + case .failure(let error): + let action = A(task: .requestFailure(error)) + dispatcher.dispatch(action) + + case .finished: + break + } + } receiveValue: { payload in + let action = A(task: .requestSuccess(payload, expiration: expiration)) + dispatcher.dispatch(action) + } + } + + func dispatch(action: A.Type, + expiration: TaskExpiration = .immediately, + key: A.Key, + on dispatcher: Dispatcher) + -> Cancellable where A.TaskPayload == Output, A.TaskError == Failure { + sink { completion in + switch completion { + case .failure(let error): + let action = A(task: .requestFailure(error, tag: "\(key)"), key: key) + dispatcher.dispatch(action) + + case .finished: + break + } + } receiveValue: { payload in + let action = A(task: .requestSuccess(payload, tag: "\(key)"), key: key) + dispatcher.dispatch(action) + } + } + + func dispatch(action: A.Type, + expiration: TaskExpiration = .immediately, + key: A.Key, + on dispatcher: Dispatcher) + -> Cancellable where A.TaskPayload == Output, A.TaskError == Failure, Output == None { + sink { completion in + switch completion { + case .failure(let error): + let action = A(task: .requestFailure(error), key: key) + dispatcher.dispatch(action) + + case .finished: + let action = A(task: .requestSuccess(expiration: expiration, tag: "\(key)"), key: key) + dispatcher.dispatch(action) + } + } receiveValue: { _ in + } + } + + func dispatch(action: A.Type, + expiration: TaskExpiration = .immediately, + on dispatcher: Dispatcher) + -> Cancellable where A.TaskPayload == Output, A.TaskError == Failure, Output == None { + sink { completion in + switch completion { + case .failure(let error): + let action = A(task: .requestFailure(error)) + dispatcher.dispatch(action) + + case .finished: + let action = A(task: .requestSuccess(expiration: expiration)) + dispatcher.dispatch(action) + } + } receiveValue: { _ in + } + } +} diff --git a/Sources/Utils/Combine/DemandBuffer.swift b/Sources/Utils/Combine/DemandBuffer.swift deleted file mode 100644 index 51beb45..0000000 --- a/Sources/Utils/Combine/DemandBuffer.swift +++ /dev/null @@ -1,105 +0,0 @@ -import Combine -import Foundation - -public class DemandBuffer { - public struct Demand { - var processed: Subscribers.Demand = .none - var requested: Subscribers.Demand = .none - var sent: Subscribers.Demand = .none - } - - private let lock = NSRecursiveLock() - private var buffer = [S.Input]() - private let subscriber: S - private var completion: Subscribers.Completion? - private var demandState = Demand() - - /// Initialize a new demand buffer for a provided downstream subscriber - /// - /// - parameter subscriber: The downstream subscriber demanding events - public init(subscriber: S) { - self.subscriber = subscriber - } - - /// Buffer an upstream value to later be forwarded to - /// the downstream subscriber, once it demands it - /// - /// - parameter value: Upstream value to buffer - /// - /// - returns: The demand fulfilled by the bufferr - func buffer(value: S.Input) -> Subscribers.Demand { - precondition(self.completion == nil, - "How could a completed publisher sent values?! Beats me 🤷‍♂️") - - switch demandState.requested { - case .unlimited: - return subscriber.receive(value) - - default: - buffer.append(value) - return flush() - } - } - - /// Complete the demand buffer with an upstream completion event - /// - /// This method will deplete the buffer immediately, - /// based on the currently accumulated demand, and relay the - /// completion event down as soon as demand is fulfilled - /// - /// - parameter completion: Completion event - func complete(completion: Subscribers.Completion) { - precondition(self.completion == nil, - "Completion have already occured, which is quite awkward 🥺") - - self.completion = completion - _ = flush() - } - - /// Signal to the buffer that the downstream requested new demand - /// - /// - note: The buffer will attempt to flush as many events rqeuested - /// by the downstream at this point - func demand(_ demand: Subscribers.Demand) -> Subscribers.Demand { - flush(adding: demand) - } - - /// Flush buffered events to the downstream based on the current - /// state of the downstream's demand - /// - /// - parameter newDemand: The new demand to add. If `nil`, the flush isn't the - /// result of an explicit demand change - /// - /// - note: After fulfilling the downstream's request, if completion - /// has already occured, the buffer will be cleared and the - /// completion event will be sent to the downstream subscriber - private func flush(adding newDemand: Subscribers.Demand? = nil) -> Subscribers.Demand { - lock.lock() - defer { lock.unlock() } - - if let newDemand = newDemand { - demandState.requested += newDemand - } - - // If buffer isn't ready for flushing, return immediately - guard demandState.requested > 0 || newDemand == Subscribers.Demand.none else { return .none } - - while !buffer.isEmpty && demandState.processed < demandState.requested { - demandState.requested += subscriber.receive(buffer.remove(at: 0)) - demandState.processed += 1 - } - - if let completion = completion { - // Completion event was already sent - buffer = [] - demandState = .init() - self.completion = nil - subscriber.receive(completion: completion) - return .none - } - - let sentDemand = demandState.requested - demandState.sent - demandState.sent += sentDemand - return sentDemand - } -} diff --git a/Sources/Utils/Combine/Dispatcher+Combine.swift b/Sources/Utils/Combine/Dispatcher+Combine.swift new file mode 100644 index 0000000..181bd52 --- /dev/null +++ b/Sources/Utils/Combine/Dispatcher+Combine.swift @@ -0,0 +1,8 @@ +import Combine +import Foundation + +extension DispatcherSubscription: Cancellable { + public func cancel() { + dispatcher.unregisterInternal(subscription: self) + } +} diff --git a/Sources/Utils/Combine/Publishers.CombineMiniTasksArray.swift b/Sources/Utils/Combine/Publishers.CombineMiniTasksArray.swift new file mode 100644 index 0000000..101cb3a --- /dev/null +++ b/Sources/Utils/Combine/Publishers.CombineMiniTasksArray.swift @@ -0,0 +1,69 @@ +import Combine +import Foundation + +public extension Publisher where Failure == Never { + func combineMiniTasks() + -> Publishers.CombineMiniTasksArray + where Output == [T] { + Publishers.CombineMiniTasksArray(upstream: self) + } +} + +public extension Publishers { + struct CombineMiniTasksArray: Publisher { + public typealias Output = Task + public typealias Failure = Upstream.Failure + + public let upstream: Upstream + + public init(upstream: Upstream) { + self.upstream = upstream + } + + public func receive(subscriber: S) where Upstream.Failure == S.Failure, Output == S.Input { + upstream.subscribe(Inner(downstream: subscriber)) + } + } +} + +extension Publishers.CombineMiniTasksArray { + private struct Inner: Subscriber + where Downstream.Input == Output, Downstream.Failure == Upstream.Failure { + let combineIdentifier = CombineIdentifier() + private let downstream: Downstream + + fileprivate init(downstream: Downstream) { + self.downstream = downstream + } + + func receive(subscription: Subscription) { + downstream.receive(subscription: subscription) + } + + func receive(_ input: Upstream.Output) -> Subscribers.Demand { + guard let tasks = input as? [any TaskType] else { + fatalError("Imposible!") + } + + if tasks.map({ $0.isRunning }).contains(true) { + return downstream.receive(.requestRunning()) + } + + if let failureTask = tasks.first(where: { $0.isFailure }), let failure = failureTask.error as? Output.Failure { + return downstream.receive(.requestFailure(failure)) + } + + if + !tasks.map({ $0.isSuccessful }).contains(false), + let payload = tasks.compactMap({ $0.payload }) as? TaskPayload { + return downstream.receive(.requestSuccess(payload)) + } + + return downstream.receive(.requestIdle()) + } + + func receive(completion: Subscribers.Completion) { + downstream.receive(completion: completion) + } + } +} diff --git a/Sources/Utils/Combine/Publishers.CombineMiniTasksTuple2.swift b/Sources/Utils/Combine/Publishers.CombineMiniTasksTuple2.swift new file mode 100644 index 0000000..710deaf --- /dev/null +++ b/Sources/Utils/Combine/Publishers.CombineMiniTasksTuple2.swift @@ -0,0 +1,96 @@ +import Combine +import Foundation + +public protocol TaskTuple2PayloadType: Equatable { + associatedtype T1Payload: Equatable + associatedtype T2Payload: Equatable +} + +public struct TaskTuple2Payload: TaskTuple2PayloadType { + public typealias T1Payload = T1P + public typealias T2Payload = T2P + + public let value1: T1Payload + public let value2: T2Payload + + public init(_ value1: T1Payload, _ value2: T2Payload) { + self.value1 = value1 + self.value2 = value2 + } +} + +public extension Publisher where Failure == Never { + func combineMiniTasks() + -> Publishers.CombineMiniTasksTuple2, T1.Failure> + where Output == (T1, T2), T1.Failure == T2.Failure { + Publishers.CombineMiniTasksTuple2(upstream: self) + } +} + +public extension Publishers { + /// Create a `Publisher` that connect an Upstream (Another publisher) that emits `Task` (Array or Tuples) + /// The Output of this Publisher always is a combined `Task` + struct CombineMiniTasksTuple2: Publisher { + public typealias Output = Task + public typealias Failure = Upstream.Failure + + public let upstream: Upstream + + public init(upstream: Upstream) { + self.upstream = upstream + } + + public func receive(subscriber: S) where Upstream.Failure == S.Failure, Output == S.Input { + upstream.subscribe(Inner(downstream: subscriber)) + } + } +} + +extension Publishers.CombineMiniTasksTuple2 { + private struct Inner: Subscriber + where Downstream.Input == Output, Downstream.Failure == Upstream.Failure, TaskPayload: TaskTuple2PayloadType { + let combineIdentifier = CombineIdentifier() + private let downstream: Downstream + + fileprivate init(downstream: Downstream) { + self.downstream = downstream + } + + func receive(subscription: Subscription) { + downstream.receive(subscription: subscription) + } + + func receive(_ input: Upstream.Output) -> Subscribers.Demand { + guard let tuple = input as? (any TaskType, any TaskType) else { + fatalError("Imposible!") + } + + if tuple.0.isRunning || tuple.1.isRunning { + return downstream.receive(.requestRunning()) + } + + if + tuple.0.isFailure || tuple.1.isFailure, + let failure = tuple.0.error as? Output.Failure ?? + tuple.1.error as? Output.Failure + { + return downstream.receive(.requestFailure(failure)) + } + + if + tuple.0.isSuccessful && tuple.1.isSuccessful, + let payload1 = tuple.0.payload as? TaskPayload.T1Payload, + let payload2 = tuple.1.payload as? TaskPayload.T2Payload, + let payload = TaskTuple2Payload(payload1, payload2) as? TaskPayload + { + return downstream.receive(.requestSuccess(payload)) + } + + return downstream.receive(.requestIdle()) + } + + func receive(completion: Subscribers.Completion) { + downstream.receive(completion: completion) + } + } +} diff --git a/Sources/Utils/Combine/Publishers.CombineMiniTasksTuple3.swift b/Sources/Utils/Combine/Publishers.CombineMiniTasksTuple3.swift new file mode 100644 index 0000000..6097685 --- /dev/null +++ b/Sources/Utils/Combine/Publishers.CombineMiniTasksTuple3.swift @@ -0,0 +1,102 @@ +import Combine +import Foundation + +public protocol TaskTuple3PayloadType: Equatable { + associatedtype T1Payload: Equatable + associatedtype T2Payload: Equatable + associatedtype T3Payload: Equatable +} + +public struct TaskTuple3Payload: TaskTuple3PayloadType { + public typealias T1Payload = T1P + public typealias T2Payload = T2P + public typealias T3Payload = T3P + + public let value1: T1Payload + public let value2: T2Payload + public let value3: T3Payload + + public init(_ value1: T1Payload, _ value2: T2Payload, _ value3: T3Payload) { + self.value1 = value1 + self.value2 = value2 + self.value3 = value3 + } +} + +public extension Publisher where Failure == Never { + func combineMiniTasks() + -> Publishers.CombineMiniTasksTuple3, T1.Failure> + where Output == (T1, T2, T3), T1.Failure == T2.Failure, T1.Failure == T2.Failure { + Publishers.CombineMiniTasksTuple3(upstream: self) + } +} + +public extension Publishers { + /// Create a `Publisher` that connect an Upstream (Another publisher) that emits `Task` (Array or Tuples) + /// The Output of this Publisher always is a combined `Task` + struct CombineMiniTasksTuple3: Publisher { + public typealias Output = Task + public typealias Failure = Upstream.Failure + + public let upstream: Upstream + + public init(upstream: Upstream) { + self.upstream = upstream + } + + public func receive(subscriber: S) where Upstream.Failure == S.Failure, Output == S.Input { + upstream.subscribe(Inner(downstream: subscriber)) + } + } +} + +extension Publishers.CombineMiniTasksTuple3 { + private struct Inner: Subscriber + where Downstream.Input == Output, Downstream.Failure == Upstream.Failure, TaskPayload: TaskTuple3PayloadType { + let combineIdentifier = CombineIdentifier() + private let downstream: Downstream + + fileprivate init(downstream: Downstream) { + self.downstream = downstream + } + + func receive(subscription: Subscription) { + downstream.receive(subscription: subscription) + } + + func receive(_ input: Upstream.Output) -> Subscribers.Demand { + guard let tuple = input as? (any TaskType, any TaskType, any TaskType) else { + fatalError("Imposible!") + } + + if tuple.0.isRunning || tuple.1.isRunning || tuple.2.isRunning { + return downstream.receive(.requestRunning()) + } + + if + tuple.0.isFailure || tuple.1.isFailure || tuple.2.isFailure, + let failure = tuple.0.error as? Output.Failure ?? + tuple.1.error as? Output.Failure ?? + tuple.2.error as? Output.Failure + { + return downstream.receive(.requestFailure(failure)) + } + + if + tuple.0.isSuccessful && tuple.1.isSuccessful && tuple.2.isSuccessful, + let payload1 = tuple.0.payload as? TaskPayload.T1Payload, + let payload2 = tuple.1.payload as? TaskPayload.T2Payload, + let payload3 = tuple.2.payload as? TaskPayload.T3Payload, + let payload = TaskTuple3Payload(payload1, payload2, payload3) as? TaskPayload + { + return downstream.receive(.requestSuccess(payload)) + } + + return downstream.receive(.requestIdle()) + } + + func receive(completion: Subscribers.Completion) { + downstream.receive(completion: completion) + } + } +} diff --git a/Sources/Utils/Combine/Publishers.CombineMiniTasksTuple4.swift b/Sources/Utils/Combine/Publishers.CombineMiniTasksTuple4.swift new file mode 100644 index 0000000..fd8a65a --- /dev/null +++ b/Sources/Utils/Combine/Publishers.CombineMiniTasksTuple4.swift @@ -0,0 +1,108 @@ +import Combine +import Foundation + +public protocol TaskTuple4PayloadType: Equatable { + associatedtype T1Payload: Equatable + associatedtype T2Payload: Equatable + associatedtype T3Payload: Equatable + associatedtype T4Payload: Equatable +} + +public struct TaskTuple4Payload: TaskTuple4PayloadType { + public typealias T1Payload = T1P + public typealias T2Payload = T2P + public typealias T3Payload = T3P + public typealias T4Payload = T4P + + public let value1: T1Payload + public let value2: T2Payload + public let value3: T3Payload + public let value4: T4Payload + + public init(_ value1: T1Payload, _ value2: T2Payload, _ value3: T3Payload, _ value4: T4Payload) { + self.value1 = value1 + self.value2 = value2 + self.value3 = value3 + self.value4 = value4 + } +} + +public extension Publisher where Failure == Never { + func combineMiniTasks() + -> Publishers.CombineMiniTasksTuple4, T1.Failure> + where Output == (T1, T2, T3, T4), T1.Failure == T2.Failure, T1.Failure == T3.Failure, T1.Failure == T4.Failure { + Publishers.CombineMiniTasksTuple4(upstream: self) + } +} + +public extension Publishers { + /// Create a `Publisher` that connect an Upstream (Another publisher) that emits `Task` (Array or Tuples) + /// The Output of this Publisher always is a combined `Task` + struct CombineMiniTasksTuple4: Publisher { + public typealias Output = Task + public typealias Failure = Upstream.Failure + + public let upstream: Upstream + + public init(upstream: Upstream) { + self.upstream = upstream + } + + public func receive(subscriber: S) where Upstream.Failure == S.Failure, Output == S.Input { + upstream.subscribe(Inner(downstream: subscriber)) + } + } +} + +extension Publishers.CombineMiniTasksTuple4 { + private struct Inner: Subscriber + where Downstream.Input == Output, Downstream.Failure == Upstream.Failure, TaskPayload: TaskTuple4PayloadType { + let combineIdentifier = CombineIdentifier() + private let downstream: Downstream + + fileprivate init(downstream: Downstream) { + self.downstream = downstream + } + + func receive(subscription: Subscription) { + downstream.receive(subscription: subscription) + } + + func receive(_ input: Upstream.Output) -> Subscribers.Demand { + guard let tuple = input as? (any TaskType, any TaskType, any TaskType, any TaskType) else { + fatalError("Imposible!") + } + + if tuple.0.isRunning || tuple.1.isRunning || tuple.2.isRunning || tuple.3.isRunning { + return downstream.receive(.requestRunning()) + } + + if + tuple.0.isFailure || tuple.1.isFailure || tuple.2.isFailure || tuple.3.isFailure, + let failure = tuple.0.error as? Output.Failure ?? + tuple.1.error as? Output.Failure ?? + tuple.2.error as? Output.Failure ?? + tuple.3.error as? Output.Failure + { + return downstream.receive(.requestFailure(failure)) + } + + if + tuple.0.isSuccessful && tuple.1.isSuccessful, tuple.2.isSuccessful && tuple.3.isSuccessful, + let payload1 = tuple.0.payload as? TaskPayload.T1Payload, + let payload2 = tuple.1.payload as? TaskPayload.T2Payload, + let payload3 = tuple.2.payload as? TaskPayload.T3Payload, + let payload4 = tuple.3.payload as? TaskPayload.T4Payload, + let payload = TaskTuple4Payload(payload1, payload2, payload3, payload4) as? TaskPayload + { + return downstream.receive(.requestSuccess(payload)) + } + + return downstream.receive(.requestIdle()) + } + + func receive(completion: Subscribers.Completion) { + downstream.receive(completion: completion) + } + } +} diff --git a/Sources/Utils/Combine/Store+Combine.swift b/Sources/Utils/Combine/Store+Combine.swift deleted file mode 100644 index 18d1b09..0000000 --- a/Sources/Utils/Combine/Store+Combine.swift +++ /dev/null @@ -1,45 +0,0 @@ -import Combine -import Foundation -import RxSwift - -extension Store: Publisher { - public typealias Output = State - public typealias Failure = Never - - public class StoreSubscription: Subscription where Target.Input == State, Target.Failure == Never { - private var disposable: Disposable? - private let buffer: DemandBuffer - - public init(store: Store, target: Target) { - buffer = DemandBuffer(subscriber: target) - disposable = store.subscribe(bufferRxEvents) - } - - public func request(_ demand: Subscribers.Demand) { - _ = buffer.demand(demand) - } - - public func cancel() { - disposable?.dispose() - disposable = nil - } - - private func bufferRxEvents(_ event: RxSwift.Event) { - switch event { - case .next(let element): - _ = buffer.buffer(value: element) - - case .error(_): - fatalError("This subscription never emit an error") - - case .completed: - buffer.complete(completion: .finished) - } - } - } - - public func receive(subscriber: S) where Failure == S.Failure, Output == S.Input { - let subscription = StoreSubscription(store: self, target: subscriber) - subscriber.receive(subscription: subscription) - } -} diff --git a/Sources/Utils/KeyedPayloadAction.swift b/Sources/Utils/KeyedPayloadAction.swift new file mode 100644 index 0000000..0b6cc7b --- /dev/null +++ b/Sources/Utils/KeyedPayloadAction.swift @@ -0,0 +1,20 @@ +import Foundation + +public protocol KeyedPayloadAction { + associatedtype TaskPayload: Equatable + associatedtype TaskError: Error + associatedtype Key: Hashable + + var task: Task { get } + var key: Key { get } + + init(task: Task, key: Key) +} + +public protocol KeyedCompletableAction: Action & KeyedPayloadAction { } + +public protocol KeyedEmptyAction: Action & KeyedPayloadAction { + associatedtype TaskPayload = None + + init(task: Task, key: Key) +} diff --git a/Sources/Utils/PayloadAction.swift b/Sources/Utils/PayloadAction.swift index 2de959b..12e63f5 100644 --- a/Sources/Utils/PayloadAction.swift +++ b/Sources/Utils/PayloadAction.swift @@ -1,40 +1,18 @@ import Foundation public protocol PayloadAction { - associatedtype Payload + associatedtype TaskPayload: Equatable + associatedtype TaskError: Error - init(task: Task, payload: Payload?) -} - -public protocol CompletableAction: Action & PayloadAction { } + var task: Task { get } -public protocol EmptyAction: Action & PayloadAction where Payload == Swift.Never { - init(task: Task) + init(task: Task) } -public extension EmptyAction { - init(task: Task, payload: Payload?) { - fatalError("Never call this method from a EmptyAction") - } -} - -public protocol KeyedPayloadAction { - associatedtype Payload - associatedtype Key: Hashable - - init(task: Task, payload: Payload?, key: Key) -} - -public protocol KeyedCompletableAction: Action & KeyedPayloadAction { } - -public protocol KeyedEmptyAction: Action & PayloadAction where Payload == Swift.Never { - associatedtype Key: Hashable +public protocol CompletableAction: Action & PayloadAction { } - init(task: Task, key: Key) -} +public protocol EmptyAction: Action & PayloadAction { + associatedtype TaskPayload = None -public extension KeyedEmptyAction { - init(task: Task, payload: Payload?) { - fatalError("Never call this method from a EmptyAction") - } + init(task: EmptyTask) } diff --git a/Sources/Utils/RxSwift/Dispatcher+Rx.swift b/Sources/Utils/RxSwift/Dispatcher+Rx.swift deleted file mode 100644 index dd0af02..0000000 --- a/Sources/Utils/RxSwift/Dispatcher+Rx.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation -import RxSwift - -extension DispatcherSubscription: Disposable { - public func dispose() { - dispatcher.unregisterInternal(subscription: self) - } -} diff --git a/Sources/Utils/RxSwift/ObservableTypeExtensions.swift b/Sources/Utils/RxSwift/ObservableTypeExtensions.swift deleted file mode 100644 index 1dd165c..0000000 --- a/Sources/Utils/RxSwift/ObservableTypeExtensions.swift +++ /dev/null @@ -1,169 +0,0 @@ -import Foundation -import RxSwift - -extension Task { - public enum Lifetime { - case once - case forever(ignoringOld: Bool) - } -} - -extension ObservableType { - /// Take the first element that matches the filter function. - /// - /// - Parameter fn: Filter closure. - /// - Returns: The first element that matches the filter. - public func filterOne(_ condition: @escaping (Element) -> Bool) -> Observable { - filter { - return condition($0) - }.take(1) - } -} - -extension ObservableType where Self.Element: StateType { - private func filterForLifetime> ( - taskMap: @escaping ((Self.Element) -> T?), - lifetime: Task.Lifetime) -> Observable { - switch lifetime { - case .once: - return self - .filterOne { taskMap($0)?.isTerminal ?? true } - - case .forever(let ignoreOld): - let date = Date() - return self - .skip { - if ignoreOld { - if let task = taskMap($0) { - return task.started < date - } - return false - } else { - return false - } - } - .filter { taskMap($0)?.isTerminal ?? true } - } - } - - private func filterForKeyedLifetime ( - key: K, - taskMap: @escaping ((Self.Element) -> KeyedTask), - lifetime: Task.Lifetime) -> Observable { - switch lifetime { - case .once: - return self - .filter { taskMap($0).hasValue(for: key) } - .filter { taskMap($0).isTerminal(key: key) } - - case .forever: - return self - .skip { taskMap($0).isIdle(key: key) || taskMap($0).isTerminal(key: key) } - .filter { taskMap($0).hasValue(for: key) } - .filter { taskMap($0).isTerminal(key: key) } - .take(1) - } - } - - private func subscribe> ( - taskMap: @escaping ((Self.Element) -> T?), - lifetime: Task.Lifetime = .once, - success: @escaping (Self.Element) -> Void = { _ in }, - error: @escaping (Self.Element) -> Void = { _ in }) - -> Disposable { - self - .filterForLifetime(taskMap: taskMap, lifetime: lifetime) - .subscribe(onNext: { state in - if let task = taskMap(state) { - if task.isSuccessful { - success(state) - } else if task.isFailure { - error(state) - } else { - success(state) - } - } - }) - } - - private func subscribe ( - key: K, - taskMap: @escaping ((Self.Element) -> KeyedTask), - lifetime: Task.Lifetime = .once, - success: @escaping (Self.Element) -> Void = { _ in }, - error: @escaping (Self.Element) -> Void = { _ in }) - -> Disposable { - self - .filterForKeyedLifetime(key: key, taskMap: taskMap, lifetime: lifetime) - .subscribe(onNext: { state in - switch taskMap(state)[task: key]?.status { - case .success: - success(state) - - case .failure: - error(state) - - default: - success(state) - } - }) - } -} - -extension ObservableType where Element: StoreType & ObservableType, Self.Element.State == Self.Element.Element { - public static func dispatch ( - using dispatcher: Dispatcher, - factory action: @autoclosure @escaping () -> A, - taskMap: @escaping (Self.Element.State) -> T?, - on store: Self.Element, - lifetime: Task.Lifetime = .once) - -> Observable { - let observable: Observable = Observable.create { observer in - let action = action() - dispatcher.dispatch(action) - let subscription = store.subscribe( - taskMap: taskMap, - lifetime: lifetime, - success: { state in - observer.on(.next(state)) - }, - error: { state in - if let task = taskMap(state), let error = task.error { - observer.on(.error(error)) - } - } - ) - return Disposables.create([subscription]) - } - return observable - } - - public static func dispatch ( - using dispatcher: Dispatcher, - factory action: @autoclosure @escaping () -> A, - key: K, - taskMap: @escaping (Self.Element.State) -> KeyedTask, - on store: Self.Element, - lifetime: Task.Lifetime = .once) - -> Observable { - let observable: Observable = Observable.create { observer in - let action = action() - dispatcher.dispatch(action) - let subscription = store.subscribe( - key: key, - taskMap: taskMap, - lifetime: lifetime, - success: { state in - observer.on(.next(state)) - }, - error: { state in - if let task = taskMap(state)[key], let error = task.error { - observer.on(.error(error)) - } - } - ) - return Disposables.create([subscription]) - } - return observable - } -} diff --git a/Sources/Utils/RxSwift/PrimitiveSequenceTypeExtensions.swift b/Sources/Utils/RxSwift/PrimitiveSequenceTypeExtensions.swift deleted file mode 100644 index 73f22fc..0000000 --- a/Sources/Utils/RxSwift/PrimitiveSequenceTypeExtensions.swift +++ /dev/null @@ -1,136 +0,0 @@ -import Foundation -import RxSwift - -public extension PrimitiveSequenceType where Self: ObservableConvertibleType, Self.Trait == SingleTrait { - func dispatch(action: A.Type, - expiration: Task.Expiration = .immediately, - on dispatcher: Dispatcher, - fillOnError errorPayload: A.Payload? = nil) - -> Disposable where A.Payload == Self.Element { - let subscription = self.subscribe( - onSuccess: { payload in - let successTask = Task(status: .success, expiration: expiration, data: payload) - - // swiftlint:disable:next explicit_init - let action = A.init(task: successTask, payload: payload) - dispatcher.dispatch(action) - }, - onFailure: { error in - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestFailure(error), payload: errorPayload) - dispatcher.dispatch(action) - } - ) - return subscription - } - - func dispatch(action: A.Type, - expiration: Task.Expiration = .immediately, - key: A.Key, - on dispatcher: Dispatcher, - fillOnError errorPayload: A.Payload? = nil) - -> Disposable where A.Payload == Self.Element { - let subscription = self.subscribe( - onSuccess: { payload in - let successTask = Task(status: .success, expiration: expiration, data: payload, tag: "\(key)") - - // swiftlint:disable:next explicit_init - let action = A.init(task: successTask, payload: payload, key: key) - dispatcher.dispatch(action) - }, - onFailure: { error in - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestFailure(error, tag: "\(key)"), payload: errorPayload, key: key) - dispatcher.dispatch(action) - } - ) - return subscription - } - - func action(_ action: A.Type, - expiration: Task.Expiration = .immediately, - fillOnError errorPayload: A.Payload? = nil) - -> Single where A.Payload == Self.Element { - Single.create { single in - let subscription = self.subscribe( - onSuccess: { payload in - let successTask = Task(status: .success, expiration: expiration, data: payload) - - // swiftlint:disable:next explicit_init - let action = A.init(task: successTask, payload: payload) - single(.success(action)) - }, - onFailure: { error in - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestFailure(error), payload: errorPayload) - single(.success(action)) - } - ) - return Disposables.create([subscription]) - } - } -} - -public extension PrimitiveSequenceType where Trait == CompletableTrait, Element == Swift.Never { - func dispatch(action: A.Type, - expiration: Task.Expiration = .immediately, - on dispatcher: Dispatcher) - -> Disposable { - let subscription = self.subscribe { completable in - switch completable { - case .completed: - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestSuccess(expiration)) - dispatcher.dispatch(action) - - case .error(let error): - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestFailure(error)) - dispatcher.dispatch(action) - } - } - return subscription - } - - func dispatch(action: A.Type, - expiration: Task.Expiration = .immediately, - key: A.Key, - on dispatcher: Dispatcher) - -> Disposable { - let subscription = self.subscribe { completable in - switch completable { - case .completed: - let successTask = Task(status: .success, expiration: expiration, tag: "\(key)") - - // swiftlint:disable:next explicit_init - let action = A.init(task: successTask, key: key) - dispatcher.dispatch(action) - - case .error(let error): - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestFailure(error), key: key) - dispatcher.dispatch(action) - } - } - return subscription - } - - func action(_ action: A.Type, - expiration: Task.Expiration = .immediately) - -> Single { - Single.create { single in - let subscription = self.subscribe { event in - switch event { - case .completed: - let action = A(task: Task.requestSuccess(expiration), payload: nil) - single(.success(action)) - - case .error(let error): - let action = A(task: Task.requestFailure(error), payload: nil) - single(.success(action)) - } - } - return Disposables.create([subscription]) - } - } -} diff --git a/Tests/ActionTests.swift b/Tests/ActionTests.swift new file mode 100644 index 0000000..048d2a3 --- /dev/null +++ b/Tests/ActionTests.swift @@ -0,0 +1,23 @@ +@testable import Mini +import XCTest + +final class ActionTests: XCTestCase { + func test_check_computed_vars() { + let payloadTask = Task.requestIdle() + let emptyTask = EmptyTask.requestRunning() + + let emptyAction = TestEmptyAction(task: emptyTask) + XCTAssertEqual(emptyAction.task, emptyTask) + + let completableAction = TestCompletableAction(task: payloadTask) + XCTAssertEqual(completableAction.task, payloadTask) + + let keyedCompletableAction = TestKeyedCompletableAction(task: payloadTask, key: "1") + XCTAssertTrue(keyedCompletableAction.task == payloadTask) + XCTAssertTrue(keyedCompletableAction.key == "1") + + let keyedEmptyAction = TestKeyedEmptyAction(task: emptyTask, key: "1") + XCTAssertTrue(keyedEmptyAction.task == emptyTask) + XCTAssertTrue(keyedEmptyAction.key == "1") + } +} diff --git a/Tests/ChainTests.swift b/Tests/ChainTests.swift index e6d5c0d..9927c01 100644 --- a/Tests/ChainTests.swift +++ b/Tests/ChainTests.swift @@ -3,31 +3,17 @@ import XCTest final class ChainTests: XCTestCase { func test_forwarding_chain_forwards_action() { - class TestAction: Action { - var mutableProperty: Int - - init(property: Int) { - self.mutableProperty = property - } - - func isEqual(to other: Action) -> Bool { - guard let action = other as? TestAction else { return false } - return mutableProperty == action.mutableProperty - } - } - let forwardingChain = ForwardingChain { action in guard let action = action as? TestAction else { fatalError() } - action.mutableProperty = 1 - return action + return TestAction(counter: action.counter + 1) } - let testAction = TestAction(property: 0) + let testAction = TestAction(counter: 0) - XCTAssert(testAction.mutableProperty == 0) + XCTAssert(testAction.counter == 0) let newAction = forwardingChain.proceed(testAction) as? TestAction - XCTAssert(newAction?.mutableProperty == 1) + XCTAssert(newAction?.counter == 1) } } diff --git a/Tests/DispatcherTests.swift b/Tests/DispatcherTests.swift index 11b80ec..6f5bc09 100644 --- a/Tests/DispatcherTests.swift +++ b/Tests/DispatcherTests.swift @@ -1,108 +1,249 @@ +import Combine @testable import Mini -import RxSwift import XCTest final class DispatcherTests: XCTestCase { func test_subscription_count() { let dispatcher = Dispatcher() - let disposable = CompositeDisposable() + var cancellables = Set() XCTAssert(dispatcher.subscriptionCount == 0) - _ = disposable.insert(dispatcher.subscribe { (_: OneTestAction) -> Void in }) - _ = disposable.insert(dispatcher.subscribe { (_: OneTestAction) -> Void in }) - - print(dispatcher.subscriptionCount) + dispatcher.subscribe { (_: TestAction) -> Void in }.store(in: &cancellables) + dispatcher.subscribe { (_: TestAction) -> Void in }.store(in: &cancellables) XCTAssert(dispatcher.subscriptionCount == 2) - disposable.dispose() + cancellables.removeAll() XCTAssert(dispatcher.subscriptionCount == 0) } func test_add_remove_service() { - class TestService: ServiceType { - func stateWasReplayed(state: StateType) { - } - - var id = UUID() - - var actions = [Action]() - - private let expectation: XCTestExpectation - - init(_ expectation: XCTestExpectation) { - self.expectation = expectation - } - - var perform: ServiceChain { - { action, _ -> Void in - self.actions.append(action) - self.expectation.fulfill() - } - } - } - - let expectation = XCTestExpectation(description: "Service") - + let expectation = XCTestExpectation(description: "Perform Action check") let dispatcher = Dispatcher() - - let service = TestService(expectation) - + let service = TestService(onPerfomAction: { expectation.fulfill() }) dispatcher.register(service: service) XCTAssert(service.actions.isEmpty == true) - dispatcher.dispatch(OneTestAction(counter: 1)) + dispatcher.dispatch(TestAction(counter: 1)) wait(for: [expectation], timeout: 5.0) XCTAssert(service.actions.count == 1) - - XCTAssert(service.actions.contains { $0 is OneTestAction } == true) + XCTAssert(service.actions.contains { $0 is TestAction } == true) dispatcher.unregister(service: service) - service.actions.removeAll() - dispatcher.dispatch(OneTestAction(counter: 1)) + dispatcher.dispatch(TestAction(counter: 1)) XCTAssert(service.actions.isEmpty == true) } func test_replay_state() { - class TestService: ServiceType { - func stateWasReplayed(state: StateType) { - self.expectation.fulfill() - } + let expectation = XCTestExpectation(description: "Replay state check") + let dispatcher = Dispatcher() + let initialState = TestState() + let store = Store(initialState, dispatcher: dispatcher, storeController: TestStoreController()) + let service = TestService(onStateReplayed: { expectation.fulfill() }) + dispatcher.register(service: service) - var id = UUID() + store.replayOnce() + + wait(for: [expectation], timeout: 5.0) + } - private let expectation: XCTestExpectation + func test_send_completableaction_to_dispatcher_from_future() { + let dispatcher = Dispatcher() + let expectedPayload = "hi!" + let expectedError = TestError.berenjenaError + var cancellables = Set() + + let futureSuccess = Future { promise in + promise(.success(expectedPayload)) + } + let futureFailure = Future { promise in + promise(.failure(.berenjenaError)) + } - init(_ expectation: XCTestExpectation) { - self.expectation = expectation + // CHECK: + let expectationSuccess = expectation(description: "wait for action dispatched with task success") + let expectationFailure = expectation(description: "wait for action dispatched with task error") + + dispatcher + .subscribe { (action: TestCompletableAction) in + switch action.task.status { + case .success(let payload): + if payload == expectedPayload { + expectationSuccess.fulfill() + } + + case .failure(let error): + if error == expectedError { + expectationFailure.fulfill() + } + + default: + XCTFail("bad action received: \(action)") + } } + .store(in: &cancellables) + + // SEND! + futureSuccess + .eraseToAnyPublisher() + .dispatch(action: TestCompletableAction.self, on: dispatcher) + .store(in: &cancellables) + futureFailure + .eraseToAnyPublisher() + .dispatch(action: TestCompletableAction.self, on: dispatcher) + .store(in: &cancellables) + + waitForExpectations(timeout: 10) + } + + func test_send_keyedcompletableaction_to_dispatcher_from_future() { + let dispatcher = Dispatcher() + let expectedPayload = "hi!" + let expectedKey = "wawa" + let expectedError = TestError.berenjenaError + var cancellables = Set() + + let futureSuccess = Future { promise in + promise(.success(expectedPayload)) + } + let futureFailure = Future { promise in + promise(.failure(.berenjenaError)) + } - var perform: ServiceChain { - { _, _ -> Void in + // CHECK: + let expectationSuccess = expectation(description: "wait for action dispatched with task success") + let expectationFailure = expectation(description: "wait for action dispatched with task error") + + dispatcher + .subscribe { (action: TestKeyedCompletableAction) in + switch action.task.status { + case .success(let payload) where action.key == expectedKey: + if payload == expectedPayload { + expectationSuccess.fulfill() + } + + case .failure(let error) where action.key == expectedKey: + if error == expectedError { + expectationFailure.fulfill() + } + + default: + XCTFail("bad action received: \(action)") } } + .store(in: &cancellables) + + // SEND! + futureSuccess + .eraseToAnyPublisher() + .dispatch(action: TestKeyedCompletableAction.self, key: expectedKey, on: dispatcher) + .store(in: &cancellables) + futureFailure + .eraseToAnyPublisher() + .dispatch(action: TestKeyedCompletableAction.self, key: expectedKey, on: dispatcher) + .store(in: &cancellables) + + waitForExpectations(timeout: 10) + } + + func test_send_emptyaction_to_dispatcher_from_future() { + let dispatcher = Dispatcher() + let expectedError = TestError.berenjenaError + var cancellables = Set() + + let futureSuccess = Future { promise in + promise(.success(.none)) } + let futureFailure = Future { promise in + promise(.failure(.berenjenaError)) + } + + // CHECK: + let expectationSuccess = expectation(description: "wait for action dispatched with task success") + let expectationFailure = expectation(description: "wait for action dispatched with task error") + + dispatcher + .subscribe { (action: TestEmptyAction) in + switch action.task.status { + case .success: + expectationSuccess.fulfill() - let expectation = XCTestExpectation(description: "Service") + case .failure(let error): + if error == expectedError { + expectationFailure.fulfill() + } + default: + XCTFail("bad action received: \(action)") + } + } + .store(in: &cancellables) + + // SEND! + futureSuccess + .eraseToAnyPublisher() + .dispatch(action: TestEmptyAction.self, on: dispatcher) + .store(in: &cancellables) + futureFailure + .eraseToAnyPublisher() + .dispatch(action: TestEmptyAction.self, on: dispatcher) + .store(in: &cancellables) + + waitForExpectations(timeout: 10) + } + + func test_send_keyedemptyaction_to_dispatcher_from_future() { let dispatcher = Dispatcher() - let initialState = TestState() - let store = Store(initialState, dispatcher: dispatcher, storeController: TestStoreController()) + let expectedError = TestError.berenjenaError + let expectedKey = "wawa" + var cancellables = Set() + + let futureSuccess = Future { promise in + promise(.success(.none)) + } + let futureFailure = Future { promise in + promise(.failure(.berenjenaError)) + } - let service = TestService(expectation) + // CHECK: + let expectationSuccess = expectation(description: "wait for action dispatched with task success") + let expectationFailure = expectation(description: "wait for action dispatched with task error") - dispatcher.register(service: service) + dispatcher + .subscribe { (action: TestKeyedEmptyAction) in + switch action.task.status { + case .success where action.key == expectedKey: + expectationSuccess.fulfill() - store.replayOnce() + case .failure(let error) where action.key == expectedKey: + if error == expectedError { + expectationFailure.fulfill() + } - wait(for: [expectation], timeout: 5.0) + default: + XCTFail("bad action received: \(action)") + } + } + .store(in: &cancellables) + + // SEND! + futureSuccess + .eraseToAnyPublisher() + .dispatch(action: TestKeyedEmptyAction.self, key: expectedKey, on: dispatcher) + .store(in: &cancellables) + futureFailure + .eraseToAnyPublisher() + .dispatch(action: TestKeyedEmptyAction.self, key: expectedKey, on: dispatcher) + .store(in: &cancellables) + + waitForExpectations(timeout: 10) } } diff --git a/Tests/Helpers/TestActions.swift b/Tests/Helpers/TestActions.swift new file mode 100644 index 0000000..fa44251 --- /dev/null +++ b/Tests/Helpers/TestActions.swift @@ -0,0 +1,88 @@ +import Foundation +import Mini + +class TestAction: Action { + let counter: Int + + init(counter: Int) { + self.counter = counter + } + + public func isEqual(to other: Action) -> Bool { + guard let action = other as? TestAction else { return false } + guard counter == action.counter else { return false } + return true + } +} + +class TestCompletableAction: CompletableAction { + typealias TaskPayload = String + typealias TaskError = TestError + + let task: Task + + required init(task: Task) { + self.task = task + } + + func isEqual(to other: Action) -> Bool { + guard let action = other as? TestCompletableAction else { return false } + guard task == action.task else { return false } + return true + } +} + +class TestEmptyAction: EmptyAction { + typealias TaskError = TestError + + let task: EmptyTask + + required init(task: EmptyTask) { + self.task = task + } + + func isEqual(to other: Action) -> Bool { + guard let action = other as? TestEmptyAction else { return false } + guard task == action.task else { return false } + return true + } +} + +class TestKeyedCompletableAction: KeyedCompletableAction { + typealias TaskPayload = String + typealias TaskError = TestError + typealias Key = String + + let task: Task + let key: Key + + required init(task: Task, key: String) { + self.task = task + self.key = key + } + + func isEqual(to other: Action) -> Bool { + guard let action = other as? TestKeyedCompletableAction else { return false } + guard task == action.task else { return false } + return true + } +} + +class TestKeyedEmptyAction: KeyedEmptyAction { + typealias TaskError = TestError + typealias Key = String + + let task: EmptyTask + let key: Key + + required init(task: EmptyTask, key: String) { + self.task = task + self.key = key + } + + func isEqual(to other: Action) -> Bool { + guard let action = other as? TestKeyedEmptyAction else { return false } + guard task == action.task else { return false } + return true + } +} diff --git a/Tests/Helpers/TestError.swift b/Tests/Helpers/TestError.swift new file mode 100644 index 0000000..62bf00a --- /dev/null +++ b/Tests/Helpers/TestError.swift @@ -0,0 +1,6 @@ +import Foundation + +enum TestError: Error { + case berenjenaError + case bigBerenjenaError +} diff --git a/Tests/Helpers/TestServices.swift b/Tests/Helpers/TestServices.swift new file mode 100644 index 0000000..fde0c13 --- /dev/null +++ b/Tests/Helpers/TestServices.swift @@ -0,0 +1,31 @@ +import Foundation +import Mini +import XCTest + +class TestService: ServiceType { + typealias TestServiceCallBack = () -> Void + + func stateWasReplayed(state: StateType) { + onStateReplayed?() + } + + var id = UUID() + + var actions = [Action]() + + private let onStateReplayed: TestServiceCallBack? + private let onPerfomAction: TestServiceCallBack? + + init(onStateReplayed: TestServiceCallBack? = nil, + onPerfomAction: TestServiceCallBack? = nil) { + self.onStateReplayed = onStateReplayed + self.onPerfomAction = onPerfomAction + } + + var perform: ServiceChain { + { action, _ -> Void in + self.actions.append(action) + self.onPerfomAction?() + } + } +} diff --git a/Tests/Helpers/TestState.swift b/Tests/Helpers/TestState.swift new file mode 100644 index 0000000..703908c --- /dev/null +++ b/Tests/Helpers/TestState.swift @@ -0,0 +1,19 @@ +import Foundation +import Mini + +struct TestState: StateType { + public let testTask: Task + public let counter: Int + + public init(testTask: Task = .requestIdle(), + counter: Int = 0) { + self.testTask = testTask + self.counter = counter + } + + public func isEqual(to other: StateType) -> Bool { + guard let state = other as? TestState else { return false } + guard counter == state.counter else { return false } + return true + } +} diff --git a/Tests/Helpers/TestStoreController.swift b/Tests/Helpers/TestStoreController.swift new file mode 100644 index 0000000..9d90b9a --- /dev/null +++ b/Tests/Helpers/TestStoreController.swift @@ -0,0 +1,22 @@ +import Combine +import Foundation +import Mini +import XCTest + +class TestStoreController: Cancellable { + public func cancel() { + // NO-OP + } +} + +extension Store where State == TestState, StoreController == TestStoreController { + func reducerGroup(expectation: XCTestExpectation? = nil) -> ReducerGroup { + ReducerGroup { [ + Reducer(of: TestAction.self, on: self.dispatcher) { action in + self.state = TestState(testTask: .requestSuccess(), counter: action.counter) + expectation?.fulfill() + } + ] + } + } +} diff --git a/Tests/KeyedTaskTests.swift b/Tests/KeyedTaskTests.swift index 0ff1662..f6190fc 100644 --- a/Tests/KeyedTaskTests.swift +++ b/Tests/KeyedTaskTests.swift @@ -2,10 +2,32 @@ import XCTest class KeyedTaskTests: XCTestCase { - var tasks: KeyedTask { - [0: Task.requestRunning(), - 1: Task.requestSuccess(), - 2: Task.requestFailure(NSError(domain: "domain", code: 44, userInfo: nil))] + var tasks: KeyedTask { + [0: .requestRunning(), + 1: .requestSuccess("hi"), + 2: .requestFailure(NSError(domain: "domain", code: 44, userInfo: nil)), + 3: .requestIdle()] + } + + func test_subscript() { + XCTAssertTrue(tasks[task: 1]?.payload == "hi") + XCTAssertEqual(tasks[task: 4], nil) + } + + func test_hasValue_inside_a_keyedtask() { + XCTAssertTrue(tasks.hasValue(for: 0)) + XCTAssertTrue(tasks.hasValue(for: 1)) + XCTAssertTrue(tasks.hasValue(for: 2)) + XCTAssertTrue(tasks.hasValue(for: 3)) + XCTAssertFalse(tasks.hasValue(for: 4)) + } + + func test_isIdle_inside_a_keyedtask() { + XCTAssertFalse(tasks.isIdle(key: 0)) + XCTAssertFalse(tasks.isIdle(key: 1)) + XCTAssertFalse(tasks.isIdle(key: 2)) + XCTAssertTrue(tasks.isIdle(key: 3)) + XCTAssertFalse(tasks.isIdle(key: 4)) } func test_isRunning_inside_a_keyedtask() { @@ -13,6 +35,7 @@ class KeyedTaskTests: XCTestCase { XCTAssertFalse(tasks.isRunning(key: 1)) XCTAssertFalse(tasks.isRunning(key: 2)) XCTAssertFalse(tasks.isRunning(key: 3)) + XCTAssertFalse(tasks.isRunning(key: 4)) } func test_isRecentlySucceeded_inside_a_keyedtask() { @@ -20,6 +43,7 @@ class KeyedTaskTests: XCTestCase { XCTAssertFalse(tasks.isRecentlySucceeded(key: 1)) XCTAssertFalse(tasks.isRecentlySucceeded(key: 2)) XCTAssertFalse(tasks.isRecentlySucceeded(key: 3)) + XCTAssertFalse(tasks.isRecentlySucceeded(key: 4)) } func test_isTerminal_inside_a_keyedtask() { @@ -27,6 +51,7 @@ class KeyedTaskTests: XCTestCase { XCTAssertTrue(tasks.isTerminal(key: 1)) XCTAssertTrue(tasks.isTerminal(key: 2)) XCTAssertFalse(tasks.isTerminal(key: 3)) + XCTAssertFalse(tasks.isTerminal(key: 4)) } func test_isSuccessful_inside_a_keyedtask() { @@ -34,6 +59,7 @@ class KeyedTaskTests: XCTestCase { XCTAssertTrue(tasks.isSuccessful(key: 1)) XCTAssertFalse(tasks.isSuccessful(key: 2)) XCTAssertFalse(tasks.isSuccessful(key: 3)) + XCTAssertFalse(tasks.isSuccessful(key: 4)) } func test_isFailure_inside_a_keyedtask() { @@ -41,5 +67,6 @@ class KeyedTaskTests: XCTestCase { XCTAssertFalse(tasks.isFailure(key: 1)) XCTAssertTrue(tasks.isFailure(key: 2)) XCTAssertFalse(tasks.isFailure(key: 3)) + XCTAssertFalse(tasks.isFailure(key: 4)) } } diff --git a/Tests/PublishersTests.swift b/Tests/PublishersTests.swift new file mode 100644 index 0000000..4ad1a5b --- /dev/null +++ b/Tests/PublishersTests.swift @@ -0,0 +1,231 @@ +import Combine +@testable import Mini +import XCTest + +class PublishersTests: XCTestCase { + var taskSuccess1: Task = .requestSuccess("hola") + var taskSuccess2: Task = .requestSuccess("chau") + var taskFailure1: Task = .requestFailure(.berenjenaError) + var taskFailure2: Task = .requestFailure(.bigBerenjenaError) + var taskRunning1: Task = .requestRunning() + var taskIdle1: Task = .requestIdle() + + // Tuple2 + + func test_combining_tuple_of_2_with_2_idle() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Publishers + .CombineLatest(Just(taskIdle1), Just(taskIdle1)) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isIdle) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_combining_tuple_of_2_with_2_success() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Publishers + .CombineLatest(Just(taskSuccess1), Just(taskSuccess2)) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isSuccessful) + XCTAssertEqual(combinedTask.payload, .init("hola", "chau")) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_combining_tuple_of_2_with_1_success_1_running() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Publishers + .CombineLatest(Just(taskSuccess1), Just(taskRunning1)) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isRunning) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_combining_tuple_of_2_with_1_success_1_failure() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Publishers + .CombineLatest(Just(taskSuccess1), Just(taskFailure1)) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isFailure) + XCTAssertEqual(combinedTask.error, .berenjenaError) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + // Tuple 3 + + func test_combining_tuple_of_3_with_2_success_1_failure() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Publishers + .CombineLatest3(Just(taskSuccess1), Just(taskSuccess2), Just(taskFailure1)) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isFailure) + XCTAssertEqual(combinedTask.error, .berenjenaError) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_combining_tuple_of_3_with_2_success_1_running() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Publishers + .CombineLatest3(Just(taskSuccess1), Just(taskSuccess2), Just(taskRunning1)) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isRunning) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + // Tuple 4 + + func test_combining_tuple_of_4_with_2_success_1_failure_1_running() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Publishers + .CombineLatest4(Just(taskSuccess1), Just(taskSuccess2), Just(taskFailure1), Just(taskRunning1)) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isRunning) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_combining_tuple_of_4_with_2_success_2_failure() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Publishers + .CombineLatest4(Just(taskSuccess1), Just(taskSuccess2), Just(taskFailure1), Just(taskFailure2)) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isFailure) + XCTAssertEqual(combinedTask.error, .berenjenaError) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_combining_tuple_of_4_with_4_success() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Publishers + .CombineLatest4(Just(taskSuccess1), Just(taskSuccess2), Just(taskSuccess1), Just(taskSuccess2)) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isSuccessful) + XCTAssertEqual(combinedTask.payload, .init("hola", "chau", "hola", "chau")) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + // Array + + func test_combining_two_success_in_array() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Just([taskSuccess1, taskSuccess2]) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isSuccessful) + XCTAssertEqual(combinedTask.payload, ["hola", "chau"]) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_combining_one_success_one_failure_in_array() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Just([taskSuccess1, taskFailure1]) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isFailure) + XCTAssertEqual(combinedTask.error, .berenjenaError) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_combining_one_success_one_running_in_array() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Just([taskSuccess1, taskRunning1]) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isRunning) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_combining_idle_tasks_in_array() { + var cancellables = Set() + let expectation = expectation(description: "wait for async process") + + Just([taskIdle1, taskIdle1, taskIdle1, taskIdle1, taskIdle1]) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isIdle) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } +} diff --git a/Tests/ReducerTests.swift b/Tests/ReducerTests.swift index 78c6aaf..a7ebe66 100644 --- a/Tests/ReducerTests.swift +++ b/Tests/ReducerTests.swift @@ -1,69 +1,20 @@ import Combine @testable import Mini -import RxSwift import XCTest -class OneTestAction: Action { - let counter: Int - - init(counter: Int) { - self.counter = counter - } - - public func isEqual(to other: Action) -> Bool { - guard let action = other as? OneTestAction else { return false } - guard counter == action.counter else { return false } - return true - } -} - -struct TestState: StateType { - public let testTask: Task - public let counter: Int - - public init(testTask: Task = Task(), counter: Int = 0) { - self.testTask = testTask - self.counter = counter - } - - public func isEqual(to other: StateType) -> Bool { - guard let state = other as? TestState else { return false } - guard counter == state.counter else { return false } - return true - } -} - -class TestStoreController: Disposable { - public func dispose() { - // NO-OP - } -} - -extension Store where State == TestState, StoreController == TestStoreController { - func reducerGroup(expectation: XCTestExpectation? = nil) -> ReducerGroup { - ReducerGroup { [ - Reducer(of: OneTestAction.self, on: self.dispatcher) { action in - self.state = TestState(testTask: .requestSuccess(), counter: action.counter) - expectation?.fulfill() - } - ] - } - } -} - final class ReducerTests: XCTestCase { func test_dispatcher_triggers_action_in_reducer_group_reducer() { - let dBag = DisposeBag() + var cancellables = Set() let dispatcher = Dispatcher() let store = Store(TestState(), dispatcher: dispatcher, storeController: TestStoreController()) let expectation = XCTestExpectation(description: "Reducer") store .reducerGroup(expectation: expectation) - .disposed(by: dBag) + .store(in: &cancellables) XCTAssertTrue(store.state.counter == 0) - dispatcher.dispatch(OneTestAction(counter: 1)) + dispatcher.dispatch(TestAction(counter: 1)) wait(for: [expectation], timeout: 5.0) XCTAssertTrue(store.state.counter == 1) @@ -75,41 +26,41 @@ final class ReducerTests: XCTestCase { XCTAssertTrue(store.state.counter == 0) - dispatcher.dispatch(OneTestAction(counter: 2)) + dispatcher.dispatch(TestAction(counter: 2)) XCTAssertTrue(store.state.counter == 0) } func test_subscribe_to_store_receive_actions() { - let dBag = DisposeBag() + var cancellables = Set() let dispatcher = Dispatcher() let store = Store(TestState(), dispatcher: dispatcher, storeController: TestStoreController()) let expectation = XCTestExpectation(description: "Reducer") store .reducerGroup(expectation: expectation) - .disposed(by: dBag) + .store(in: &cancellables) XCTAssertTrue(store.state.counter == 0) - dispatcher.dispatch(OneTestAction(counter: 2)) + dispatcher.dispatch(TestAction(counter: 2)) wait(for: [expectation], timeout: 5.0) XCTAssertTrue(store.state.counter == 2) } func test_reset_state() { - let dBag = DisposeBag() + var cancellables = Set() let dispatcher = Dispatcher() let initialState = TestState() let store = Store(initialState, dispatcher: dispatcher, storeController: TestStoreController()) let expectation = XCTestExpectation(description: "Reducer") store .reducerGroup(expectation: expectation) - .disposed(by: dBag) + .store(in: &cancellables) XCTAssertTrue(store.state.counter == 0) - dispatcher.dispatch(OneTestAction(counter: 3)) + dispatcher.dispatch(TestAction(counter: 3)) wait(for: [expectation], timeout: 5.0) XCTAssertTrue(store.state.counter == 3) @@ -118,32 +69,8 @@ final class ReducerTests: XCTestCase { XCTAssert(store.state.isEqual(to: initialState)) } - func test_subscribe_state_changes_with_rx() { - let dBag = DisposeBag() - let dispatcher = Dispatcher() - let initialState = TestState() - let store = Store(initialState, dispatcher: dispatcher, storeController: TestStoreController()) - let expectation = XCTestExpectation(description: "Subscription Emits 1") - - store - .reducerGroup() - .disposed(by: dBag) - store - .map { $0.counter } - .subscribe(onNext: { counter in - if counter == 1 { - expectation.fulfill() - } - }) - .disposed(by: dBag) - - dispatcher.dispatch(OneTestAction(counter: 1)) - wait(for: [expectation], timeout: 5.0) - } - - func test_subscribe_state_changes_with_combine() { - let dBag = DisposeBag() - var bag = Set() + func test_subscribe_state_changes() { + var cancellables = Set() let dispatcher = Dispatcher() let initialState = TestState() let store = Store(initialState, dispatcher: dispatcher, storeController: TestStoreController()) @@ -152,7 +79,8 @@ final class ReducerTests: XCTestCase { store .reducerGroup() - .disposed(by: dBag) + .store(in: &cancellables) + store .map(\.counter) .sink { counter in @@ -163,10 +91,10 @@ final class ReducerTests: XCTestCase { expectation2.fulfill() } } - .store(in: &bag) + .store(in: &cancellables) - dispatcher.dispatch(OneTestAction(counter: 1)) - dispatcher.dispatch(OneTestAction(counter: 2)) + dispatcher.dispatch(TestAction(counter: 1)) + dispatcher.dispatch(TestAction(counter: 2)) wait(for: [expectation1, expectation2], timeout: 5.0) } } diff --git a/Tests/TaskTests.swift b/Tests/TaskTests.swift index b53e4b2..b4bf01e 100644 --- a/Tests/TaskTests.swift +++ b/Tests/TaskTests.swift @@ -5,7 +5,7 @@ class TaskTests: XCTestCase { let error = NSError(domain: "wawa", code: 69, userInfo: nil) func test_check_states_for_running_task() { - let task = Task.requestRunning() + let task = Task.requestRunning() XCTAssertEqual(task.status, .running) XCTAssertNil(task.error) @@ -18,9 +18,10 @@ class TaskTests: XCTestCase { } func test_check_states_for_success_task() { - let task = Task.requestSuccess() + let task = Task(status: .success(payload: 5)) - XCTAssertEqual(task.status, .success) + XCTAssertTrue(task.isSuccessful) + XCTAssertEqual(task.payload, 5) XCTAssertNil(task.error) XCTAssertFalse(task.isRunning) @@ -30,10 +31,11 @@ class TaskTests: XCTestCase { } func test_check_states_for_failure_task() { - let task = Task.requestFailure(error) + let task = Task.requestFailure(error) - XCTAssertEqual(task.status, .failure) - XCTAssertEqual(task.error as NSError?, error) + XCTAssertNotEqual(task.status, .failure(error: error)) + XCTAssertNil(task.payload) + XCTAssertEqual(task.error, error) XCTAssertFalse(task.isRunning) XCTAssertTrue(task.isFailure) @@ -43,26 +45,32 @@ class TaskTests: XCTestCase { } func test_data_and_progress() { - let data = "comiendo perritos calientes" + let payload = "comiendo perritos calientes" let progress: Decimal = 0.5 - let task = TypedTask(data: data, progress: progress) + let task = Task(status: .success(payload: payload), progress: progress) - XCTAssertEqual(task.data, data) + XCTAssertEqual(task.payload, payload) XCTAssertEqual(task.progress, progress) } + func test_success_task_with_payload() { + let task = Task(status: .success(payload: "hola")) + + XCTAssertEqual(task.payload, "hola") + } + func test_expiration_of_task_created_with_past_date() { - let task = Task(status: .success, started: Date.distantPast) + let task = Task(status: .success(payload: "55"), started: Date.distantPast) XCTAssertFalse(task.isRecentlySucceeded) } func test_success_task_with_expiration_setted_to_immediately() { - let task = Task.requestSuccess(.immediately) + let task = Task.requestSuccess(6, expiration: .immediately) XCTAssertFalse(task.isRecentlySucceeded) } func test_success_task_with_expiration_setted() { - let task = Task.requestSuccess(.custom(2)) + let task = Task.requestSuccess(66, expiration: .custom(2)) XCTAssertTrue(task.isRecentlySucceeded) Thread.sleep(forTimeInterval: 3) diff --git a/bin/pre-commit.sh b/bin/pre-commit.sh index 96b4229..648066d 100755 --- a/bin/pre-commit.sh +++ b/bin/pre-commit.sh @@ -1,7 +1,7 @@ #!/bin/bash # -LINT=$(which swiftlint) +LINT=`mint which swiftlint` EXITSTATUS=0