diff --git a/.bundle/config b/.bundle/config deleted file mode 100644 index 9c096a78..00000000 --- a/.bundle/config +++ /dev/null @@ -1,2 +0,0 @@ ---- -BUNDLE_PATH: ".gems" \ No newline at end of file diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 388e79d1..00000000 --- a/.codecov.yml +++ /dev/null @@ -1,13 +0,0 @@ -coverage: - range: 50..100 - round: down - precision: 2 - ignore: - - Tests/* - status: - project: - default: false - framework: - target: auto - paths: "Sources/" - patch: off \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1715f043..0ceae726 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,7 +4,7 @@ # Current code owners # - @sebastianvarela -> Sebastián Varela # - @r-pedraza -> Raul Pedraza -# - @minuscorp -> Jorge Revuelta # - @EdiLT -> Edilberto López +# - @josemanuelmartin -> Jose Manuel Martín -* @sebastianvarela @r-pedraza @minuscorp @EdiLT \ No newline at end of file +* @EdiLT @r-pedraza @sebastianvarela @josemanuelmartin @Gabriel-V-Radu @adrianrl \ No newline at end of file diff --git a/.github/workflows/pr_validator.yml b/.github/workflows/pr_validator.yml new file mode 100644 index 00000000..62d089d9 --- /dev/null +++ b/.github/workflows/pr_validator.yml @@ -0,0 +1,69 @@ +name: "pr-validator" +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + swiftlint: + name: Swiftlint + runs-on: apps-ci + timeout-minutes: 120 + steps: + + - name: Checkout 🔎 + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Rake ⚙️ + run: rake + + - name: Swiftlint 👀 + run: | + set -o pipefail && mint run swiftlint --strict \ + | sed -E 's/^(.*):([0-9]+):([0-9]+): (warning|error|[^:]+): (.*)/::\4 file=\1,line=\2,col=\3::\5/' + + build: + name: Test + runs-on: apps-ci + timeout-minutes: 120 + steps: + + - name: Checkout 🔎 + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Rake ⚙️ + run: rake + + - name: Run tests ⚙️ + run: rake test + + - name: Generate lcov file 📈 + run: | + xcrun llvm-cov export \ + -format="lcov" \ + --ignore-filename-regex=".build|.test-bundle|Tests/" \ + .build/debug/MiniPackageTests.xctest/Contents/MacOS/MiniPackageTests \ + -instr-profile .build/debug/codecov/default.profdata \ + > report.lcov + + - name: Upload package tests coverage to Codecov 📋 + uses: codecov/codecov-action@v3.1.1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: report.lcov + flags: package + fail_ci_if_error: true + name: codecov-package + gcov_ignore: Tests/* + + - name: Save Output 📦 + uses: actions/upload-artifact@v4 + with: + name: output + path: ${{ github.workspace }}/report.lcov diff --git a/.gitignore b/.gitignore index abd00a4f..cf669748 100755 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,27 @@ .gems +output/ +.DS_Store +Brewfile.lock.json -# Created by https://www.gitignore.io/api/swift,xcode,carthage +# Created by https://www.toptal.com/developers/gitignore/api/swift +# Edit at https://www.toptal.com/developers/gitignore?templates=swift ### Swift ### # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore -## Build generated +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ - -## Various settings +*.moved-aside *.pbxuser !default.pbxuser *.mode1v3 @@ -20,14 +30,11 @@ DerivedData/ !default.mode2v3 *.perspectivev3 !default.perspectivev3 -xcuserdata/ - -## Other -*.moved-aside -*.xcuserstate ## Obj-C/Swift specific *.hmap + +## App packaging *.ipa *.dSYM.zip *.dSYM @@ -37,67 +44,50 @@ timeline.xctimeline playground.xcworkspace # Swift Package Manager -# # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +.swiftpm + .build/ # CocoaPods -# # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# # Pods/ +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace # Carthage -# # Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts -Carthage/ +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ # fastlane -# -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: -# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md +# https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html -fastlane/screenshots +fastlane/screenshots/**/*.png fastlane/test_output +# Code Injection +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode -### Xcode ### -# Xcode -# -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - -## Build generated -build/ -DerivedData/ - -## Various settings -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata/ - -## Other -*.moved-aside -*.xccheckout -*.xcscmblueprint - +iOSInjectionProject/ -### Carthage ### -# Carthage - A simple, decentralized dependency manager for Cocoa -Carthage/Checkouts/ -Carthage/Build/ - -.DS_Store +# End of https://www.toptal.com/developers/gitignore/api/swift \ No newline at end of file diff --git a/.overcommit.yml b/.overcommit.yml deleted file mode 100644 index 95399a74..00000000 --- a/.overcommit.yml +++ /dev/null @@ -1,11 +0,0 @@ -gemfile: Gemfile - -concurrency: '1' - -PreCommit: - BundleCheck: - enabled: true - - CustomScript: - enabled: true - command: ["./bin/pre-commit.sh"] \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml index 56e646d2..266f2605 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,14 +1,20 @@ disabled_rules: + - inclusive_language - todo - conditional_returns_on_newline - unused_optional_binding - - file_header + - unused_setter_value opt_in_rules: #lint - prohibited_super_call - overridden_super_call - yoda_condition + - modifier_order + #- explicit_acl + - cyclomatic_complexity #style + - sorted_imports + - duplicate_imports - unneeded_parentheses_in_closure_argument - operator_usage_whitespace - number_separator @@ -16,19 +22,32 @@ opt_in_rules: - closure_end_indentation - attributes - file_header + - conditional_returns_on_newline + - implicit_return + - multiline_function_chains + - switch_case_on_newline + - prefer_self_type_over_type_of_self + - vertical_whitespace_between_cases + - vertical_whitespace_closing_braces + - vertical_whitespace_opening_braces + - vertical_parameter_alignment + - vertical_parameter_alignment_on_call #idiomatic + - redundant_type_annotation - redundant_nil_coalescing - nimble_operator - explicit_init + - empty_parentheses_with_trailing_closure + - prefer_zero_over_explicit_init #performance - empty_count - contains_over_first_not_nil - empty_string - first_where - sorted_first_last + - flatmap_over_map_reduce included: - Sources -excluded: - Tests closure_end_indentation: severity: error @@ -36,36 +55,67 @@ force_cast: severity: error force_try: severity: error +function_parameter_count: + warning: 6 + error: 6 function_body_length: - warning: 60 - error: 100 + warning: 120 + error: 200 cyclomatic_complexity: warning: 60 error: 60 type_body_length: - warning: 400 - error: 500 + warning: 1500 + error: 1500 line_length: warning: 250 error: 400 +large_tuple: + warning: 5 + error: 5 file_length: - warning: 700 - error: 1200 + warning: 2000 + error: 2000 +nesting: + type_level: + warning: 2 + function_level: + warning: 2 type_name: min_length: 3 - max_length: 40 - severity: error + max_length: 50 + excluded: + - iPhone + - my + - BO + - bo identifier_name: min_length: 3 - max_length: 40 - severity: error - excluded: - - id + max_length: 45 + excluded: - i + - to + - x + - y + - z + - a + - b + - rx + - map + - app + - id + - URL + - GlobalAPIKey + - bo + - BO attributes: + always_on_line_above: + - "@discardableResult" + - "@available" always_on_same_line: + - "@autoclosure" - "@IBOutlet" - "@IBAction" - "@NSManaged" - "@testable" -reporter: "xcode" \ No newline at end of file +reporter: "xcode" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ccb60073..00000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: swift -osx_image: xcode10.3 - -addons: - homebrew: - brewfile: true - -script: -- set -o pipefail -- swift --version -- rake -- rake test -- bundle exec fastlane pass_tests -- bundle exec pod repo update -- bundle exec pod lib lint --allow-warnings -- bundle exec danger \ No newline at end of file diff --git a/Brewfile b/Brewfile deleted file mode 100644 index 7ddb8339..00000000 --- a/Brewfile +++ /dev/null @@ -1 +0,0 @@ -brew "swiftlint" \ No newline at end of file diff --git a/DangerFile b/DangerFile deleted file mode 100644 index a0c06b6e..00000000 --- a/DangerFile +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -# FILE HELPERS -gitfiles = (git.modified_files + git.added_files).uniq -has_code_changes = !gitfiles.grep(/^Source/).empty? -has_tests_changes = !gitfiles.grep(/^Tests/).empty? - -# bq_helpers.scan_files - -# BASIC CHECKS: -warn 'Big PR, try to keep changes smaller if you can 😜' if git.lines_of_code > 500 - -# BUILD PARSE: -# bq_helpers.build_reports.each do |path| -# path = Pathname(path) -# xcode_summary.report path.to_s -# end -# -# #JUNIT PARSE: -# bq_helpers.test_reports.each do |path| -# path = Pathname(path) -# junit.parse path.to_s -# junit.report -# -# all_test = junit.tests.map(&:attributes) -# slowest_test = all_test.sort_by { |attributes| attributes[:time].to_f }.last -# message "⌛️ **[#{bq_helpers.read_platform_from_file(path: path)}]** Slowest test: #{slowest_test[:name]} took #{'%.3f' % slowest_test[:time]} seconds" -# end - -# SWIFTLINT -swiftlint.lint_all_files = true -swiftlint.lint_files fail_on_error: true - -# TEST EVOLUTION CHECK: -if has_code_changes - warn('You have changes in code but there is no changes in any test... do you sleep well at night? 🤨') unless has_tests_changes -end diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 200d73e3..00000000 --- a/Gemfile +++ /dev/null @@ -1,17 +0,0 @@ -source "https://rubygems.org" - -gem "fastlane" -gem "trainer" -gem "overcommit" -gem "xcpretty-json-formatter" -gem "cocoapods" -gem "danger" -gem "danger-xcodebuild" -gem "danger-swiftlint" -gem "danger-xcov" -gem "danger-junit" -gem "danger-xcode_summary" -gem "danger-bq_helpers", git: "https://github.com/bq/danger-bq_helpers" - -plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') -eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index c7e7dff9..00000000 --- a/Gemfile.lock +++ /dev/null @@ -1,308 +0,0 @@ -GIT - remote: https://github.com/bq/danger-bq_helpers - revision: 43e47a3d54b74409633f60883ef3138a04ee0d78 - specs: - danger-bq_helpers (1.0.1) - danger-plugin-api (~> 1.0) - -GIT - remote: https://github.com/bq/fastlane-plugin-test_scheme - revision: c0f5ab47e0e5686f9c92bdd9ccb2c1205d612b94 - specs: - fastlane-plugin-test_scheme (1.2.0) - -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.1) - activesupport (4.2.11.1) - i18n (~> 0.7) - minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) - tzinfo (~> 1.1) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) - atomos (0.1.3) - babosa (1.0.2) - childprocess (0.9.0) - ffi (~> 1.0, >= 1.0.11) - claide (1.0.3) - claide-plugins (0.9.2) - cork - nap - open4 (~> 1.3) - cocoapods (1.7.5) - activesupport (>= 4.0.2, < 5) - claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.7.5) - cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 1.2.2, < 2.0) - cocoapods-plugins (>= 1.0.0, < 2.0) - cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-stats (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.3.1, < 2.0) - cocoapods-try (>= 1.1.0, < 2.0) - colored2 (~> 3.1) - escape (~> 0.0.4) - fourflusher (>= 2.3.0, < 3.0) - gh_inspector (~> 1.0) - molinillo (~> 0.6.6) - nap (~> 1.0) - ruby-macho (~> 1.4) - xcodeproj (>= 1.10.0, < 2.0) - cocoapods-core (1.7.5) - activesupport (>= 4.0.2, < 6) - fuzzy_match (~> 2.0.4) - nap (~> 1.0) - cocoapods-deintegrate (1.0.4) - cocoapods-downloader (1.2.2) - cocoapods-plugins (1.0.0) - nap - cocoapods-search (1.0.0) - cocoapods-stats (1.1.0) - cocoapods-trunk (1.4.0) - nap (>= 0.8, < 2.0) - netrc (~> 0.11) - cocoapods-try (1.1.0) - colored (1.2) - colored2 (3.1.2) - commander-fastlane (4.4.6) - highline (~> 1.7.2) - concurrent-ruby (1.1.5) - cork (0.3.0) - colored2 (~> 3.1) - danger (6.0.9) - claide (~> 1.0) - claide-plugins (>= 0.9.2) - colored2 (~> 3.1) - cork (~> 0.1) - faraday (~> 0.9) - faraday-http-cache (~> 2.0) - git (~> 1.5) - kramdown (~> 2.0) - kramdown-parser-gfm (~> 1.0) - no_proxy_fix - octokit (~> 4.7) - terminal-table (~> 1) - danger-junit (1.0.0) - danger (> 2.0) - ox (~> 2.0) - danger-plugin-api (1.0.0) - danger (> 2.0) - danger-swiftlint (0.23.0) - danger - rake (> 10) - thor (~> 0.19) - danger-xcode_summary (0.5.1) - danger-plugin-api (~> 1.0) - danger-xcodebuild (0.0.6) - danger-plugin-api (~> 1.0) - danger-xcov (0.4.1) - danger (>= 2.1) - xcov (>= 1.1.2) - declarative (0.0.10) - declarative-option (0.1.0) - digest-crc (0.4.1) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.5) - emoji_regex (1.0.1) - escape (0.0.4) - excon (0.66.0) - faraday (0.15.4) - multipart-post (>= 1.2, < 3) - faraday-cookie_jar (0.0.6) - faraday (>= 0.7.4) - http-cookie (~> 1.0.0) - faraday-http-cache (2.0.0) - faraday (~> 0.8) - faraday_middleware (0.13.1) - faraday (>= 0.7.4, < 1.0) - fastimage (2.1.5) - fastlane (2.130.0) - CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.3, < 3.0.0) - babosa (>= 1.0.2, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) - colored - commander-fastlane (>= 4.4.6, < 5.0.0) - dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (>= 0.1, < 2.0) - excon (>= 0.45.0, < 1.0.0) - faraday (~> 0.9) - faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 0.9) - fastimage (>= 2.1.0, < 3.0.0) - gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.21.2, < 0.24.0) - google-cloud-storage (>= 1.15.0, < 2.0.0) - highline (>= 1.7.2, < 2.0.0) - json (< 3.0.0) - jwt (~> 2.1.0) - mini_magick (>= 4.9.4, < 5.0.0) - multi_xml (~> 0.5) - multipart-post (~> 2.0.0) - plist (>= 3.1.0, < 4.0.0) - public_suffix (~> 2.0.0) - rubyzip (>= 1.2.2, < 2.0.0) - security (= 0.1.3) - simctl (~> 1.6.3) - slack-notifier (>= 2.0.0, < 3.0.0) - terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) - tty-screen (>= 0.6.3, < 1.0.0) - tty-spinner (>= 0.8.0, < 1.0.0) - word_wrap (~> 1.0.0) - xcodeproj (>= 1.8.1, < 2.0.0) - xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3) - fastlane-plugin-trainer (0.4.1) - trainer (>= 0.7.0) - ffi (1.11.1) - fourflusher (2.3.1) - fuzzy_match (2.0.4) - gh_inspector (1.1.3) - git (1.5.0) - google-api-client (0.23.9) - addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.5, < 0.7.0) - httpclient (>= 2.8.1, < 3.0) - mime-types (~> 3.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.0) - signet (~> 0.9) - google-cloud-core (1.3.1) - google-cloud-env (~> 1.0) - google-cloud-env (1.2.1) - faraday (~> 0.11) - google-cloud-storage (1.16.0) - digest-crc (~> 0.4) - google-api-client (~> 0.23) - google-cloud-core (~> 1.2) - googleauth (>= 0.6.2, < 0.10.0) - googleauth (0.6.7) - faraday (~> 0.12) - jwt (>= 1.4, < 3.0) - memoist (~> 0.16) - multi_json (~> 1.11) - os (>= 0.9, < 2.0) - signet (~> 0.7) - highline (1.7.10) - http-cookie (1.0.3) - domain_name (~> 0.5) - httpclient (2.8.3) - i18n (0.9.5) - concurrent-ruby (~> 1.0) - iniparse (1.4.4) - json (2.2.0) - jwt (2.1.0) - kramdown (2.1.0) - kramdown-parser-gfm (1.1.0) - kramdown (~> 2.0) - memoist (0.16.0) - mime-types (3.3) - mime-types-data (~> 3.2015) - mime-types-data (3.2019.0904) - mini_magick (4.9.5) - minitest (5.11.3) - molinillo (0.6.6) - multi_json (1.13.1) - multi_xml (0.6.0) - multipart-post (2.0.0) - nanaimo (0.2.6) - nap (1.1.0) - naturally (2.2.0) - netrc (0.11.0) - no_proxy_fix (0.1.2) - octokit (4.14.0) - sawyer (~> 0.8.0, >= 0.5.3) - open4 (1.3.4) - os (1.0.1) - overcommit (0.47.0) - childprocess (~> 0.6, >= 0.6.3) - iniparse (~> 1.4) - ox (2.11.0) - plist (3.5.0) - public_suffix (2.0.5) - rake (12.3.3) - representable (3.0.4) - declarative (< 0.1.0) - declarative-option (< 0.2.0) - uber (< 0.2.0) - retriable (3.1.2) - rouge (2.0.7) - ruby-macho (1.4.0) - rubyzip (1.2.3) - sawyer (0.8.2) - addressable (>= 2.3.5) - faraday (> 0.8, < 2.0) - security (0.1.3) - signet (0.11.0) - addressable (~> 2.3) - faraday (~> 0.9) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) - simctl (1.6.5) - CFPropertyList - naturally - slack-notifier (2.3.2) - terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - thor (0.20.3) - thread_safe (0.3.6) - trainer (0.8.2) - fastlane (>= 2.25.0) - plist (>= 3.1.0, < 4.0.0) - tty-cursor (0.7.0) - tty-screen (0.7.0) - tty-spinner (0.9.1) - tty-cursor (~> 0.7) - tzinfo (1.2.5) - thread_safe (~> 0.1) - uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.6) - unicode-display_width (1.6.0) - word_wrap (1.0.0) - xcodeproj (1.12.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.2.6) - xcov (1.5.1) - fastlane (>= 2.82.0, < 3.0.0) - multipart-post - slack-notifier - terminal-table - xcodeproj - xcpretty (0.3.0) - rouge (~> 2.0.7) - xcpretty-json-formatter (0.1.1) - xcpretty (~> 0.2, >= 0.0.7) - xcpretty-travis-formatter (1.0.0) - xcpretty (~> 0.2, >= 0.0.7) - -PLATFORMS - ruby - -DEPENDENCIES - cocoapods - danger - danger-bq_helpers! - danger-junit - danger-swiftlint - danger-xcode_summary - danger-xcodebuild - danger-xcov - fastlane - fastlane-plugin-test_scheme! - fastlane-plugin-trainer - overcommit - trainer - xcpretty-json-formatter - -BUNDLED WITH - 1.17.1 diff --git a/Mini-Swift.podspec b/Mini-Swift.podspec deleted file mode 100644 index 37cd33de..00000000 --- a/Mini-Swift.podspec +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true - -Pod::Spec.new do |s| - s.name = 'Mini-Swift' - s.version = '1.1.2' - s.swift_version = '5.0' - s.summary = 'The minimal expression of a Flux architecture in Swift.' - - s.description = <<~DESC - The minimal expression of a Flux architecture in Swift. - - Mini is built with be a first class citizen in Swift applications: macOS, iOS and tvOS applications. - With Mini, you can create a thread-safe application with a predictable unidirectional data flow, - focusing on what really matters: build awesome applications. - DESC - - s.homepage = 'https://github.com/bq/Mini-Swift' - s.license = { type: 'APACHE', file: 'LICENSE' } - s.author = { 'bq' => 'info@bq.com' } - s.source = { git: 'https://github.com/bq/mini-swift.git', tag: "v#{s.version}" } - s.social_media_url = 'https://twitter.com/bqreaders' - - s.ios.deployment_target = '11.0' - - s.osx.deployment_target = '10.13' - - s.tvos.deployment_target = '11.0' - - s.frameworks = 'Foundation' - - s.dependency('RxSwift', '~> 5') - s.dependency('SwiftNIOConcurrencyHelpers', '~> 2.0.0') - - s.default_subspec = 'Core' - - s.module_name = 'Mini' - - s.subspec('Core') do |ss| - ss.ios.source_files = ['Sources/MiniSwift/*.swift', 'Sources/MiniSwift/Utils/**/*.swift'] - - ss.osx.source_files = ['Sources/MiniSwift/*.swift', 'Sources/MiniSwift/Utils/**/*.swift'] - - ss.tvos.source_files = ['Sources/MiniSwift/*.swift', 'Sources/MiniSwift/Utils/**/*.swift'] - end - - s.subspec('Log') do |ss| - ss.ios.dependency('Mini-Swift/Core') - ss.ios.source_files = 'Sources/MiniSwift/LoggingService/*.swift' - - ss.osx.dependency('Mini-Swift/Core') - ss.osx.source_files = 'Sources/MiniSwift/LoggingService/*.swift' - - ss.tvos.dependency('Mini-Swift/Core') - ss.tvos.source_files = 'Sources/MiniSwift/LoggingService/*.swift' - end - - s.subspec('Test') do |ss| - ss.ios.dependency('Mini-Swift/Core') - ss.ios.source_files = 'Sources/MiniSwift/TestMiddleware/*.swift' - - ss.osx.dependency('Mini-Swift/Core') - ss.osx.source_files = 'Sources/MiniSwift/TestMiddleware/*.swift' - - ss.tvos.dependency('Mini-Swift/Core') - ss.tvos.source_files = 'Sources/MiniSwift/TestMiddleware/*.swift' - end - - s.preserve_paths = ['Templates/*.stencil'] -end diff --git a/Mini.xcodeproj/CNIOAtomics_Info.plist b/Mini.xcodeproj/CNIOAtomics_Info.plist deleted file mode 100644 index 57ada9f9..00000000 --- a/Mini.xcodeproj/CNIOAtomics_Info.plist +++ /dev/null @@ -1,25 +0,0 @@ - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/Mini.xcodeproj/MiniSwiftTests_Info.plist b/Mini.xcodeproj/MiniSwiftTests_Info.plist deleted file mode 100644 index 7c23420d..00000000 --- a/Mini.xcodeproj/MiniSwiftTests_Info.plist +++ /dev/null @@ -1,25 +0,0 @@ - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/Mini.xcodeproj/MiniSwift_Info.plist b/Mini.xcodeproj/MiniSwift_Info.plist deleted file mode 100644 index 57ada9f9..00000000 --- a/Mini.xcodeproj/MiniSwift_Info.plist +++ /dev/null @@ -1,25 +0,0 @@ - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/Mini.xcodeproj/NIOConcurrencyHelpers_Info.plist b/Mini.xcodeproj/NIOConcurrencyHelpers_Info.plist deleted file mode 100644 index 57ada9f9..00000000 --- a/Mini.xcodeproj/NIOConcurrencyHelpers_Info.plist +++ /dev/null @@ -1,25 +0,0 @@ - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/Mini.xcodeproj/RxSwift_Info.plist b/Mini.xcodeproj/RxSwift_Info.plist deleted file mode 100644 index 57ada9f9..00000000 --- a/Mini.xcodeproj/RxSwift_Info.plist +++ /dev/null @@ -1,25 +0,0 @@ - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/Mini.xcodeproj/project.pbxproj b/Mini.xcodeproj/project.pbxproj deleted file mode 100644 index 2883ae61..00000000 --- a/Mini.xcodeproj/project.pbxproj +++ /dev/null @@ -1,2206 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXAggregateTarget section */ - "Mini::Mini::ProductTarget" /* Mini */ = { - isa = PBXAggregateTarget; - buildConfigurationList = OBJ_268 /* Build configuration list for PBXAggregateTarget "Mini" */; - buildPhases = ( - ); - dependencies = ( - OBJ_271 /* PBXTargetDependency */, - ); - name = Mini; - productName = Mini; - }; - "Mini::MiniPackageTests::ProductTarget" /* MiniPackageTests */ = { - isa = PBXAggregateTarget; - buildConfigurationList = OBJ_280 /* Build configuration list for PBXAggregateTarget "MiniPackageTests" */; - buildPhases = ( - ); - dependencies = ( - OBJ_283 /* PBXTargetDependency */, - ); - name = MiniPackageTests; - productName = MiniPackageTests; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 12A3ADEF2328D3D100019F49 /* PrimitiveSequenceType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A3ADED2328D33400019F49 /* PrimitiveSequenceType+Extensions.swift */; }; - OBJ_262 /* c-atomics.c in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_44 /* c-atomics.c */; }; - OBJ_264 /* CNIOAtomics.h in Headers */ = {isa = PBXBuildFile; fileRef = OBJ_46 /* CNIOAtomics.h */; settings = {ATTRIBUTES = (Public, ); }; }; - OBJ_265 /* cpp_magic.h in Headers */ = {isa = PBXBuildFile; fileRef = OBJ_47 /* cpp_magic.h */; settings = {ATTRIBUTES = (Public, ); }; }; - OBJ_278 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; - OBJ_289 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Action.swift */; }; - OBJ_290 /* ActionReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* ActionReducer.swift */; }; - OBJ_291 /* Dispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* Dispatcher.swift */; }; - OBJ_292 /* Middleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Middleware.swift */; }; - OBJ_293 /* ReducerGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* ReducerGroup.swift */; }; - OBJ_294 /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* Service.swift */; }; - OBJ_295 /* StateType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* StateType.swift */; }; - OBJ_296 /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* Store.swift */; }; - OBJ_297 /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* Task.swift */; }; - OBJ_298 /* Dictionary+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_20 /* Dictionary+Extensions.swift */; }; - OBJ_299 /* DispatchQueue+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_21 /* DispatchQueue+Extensions.swift */; }; - OBJ_300 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_22 /* String+Extensions.swift */; }; - OBJ_301 /* TestMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_24 /* TestMiddleware.swift */; }; - OBJ_302 /* OrderedSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_25 /* OrderedSet.swift */; }; - OBJ_303 /* PayloadAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_26 /* PayloadAction.swift */; }; - OBJ_304 /* ObservableType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_28 /* ObservableType+Extensions.swift */; }; - OBJ_305 /* LoggingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_30 /* LoggingService.swift */; }; - OBJ_306 /* SharedDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* SharedDictionary.swift */; }; - OBJ_308 /* NIOConcurrencyHelpers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "swift-nio::NIOConcurrencyHelpers::Product" /* NIOConcurrencyHelpers.framework */; }; - OBJ_309 /* CNIOAtomics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "swift-nio::CNIOAtomics::Product" /* CNIOAtomics.framework */; }; - OBJ_310 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "RxSwift::RxSwift::Product" /* RxSwift.framework */; }; - OBJ_320 /* ChainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_34 /* ChainTests.swift */; }; - OBJ_321 /* Dictionary+ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_35 /* Dictionary+ExtensionsTests.swift */; }; - OBJ_322 /* DispatcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_36 /* DispatcherTests.swift */; }; - OBJ_323 /* ReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_37 /* ReducerTests.swift */; }; - OBJ_324 /* TaskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_38 /* TaskTests.swift */; }; - OBJ_325 /* XCTestManifests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_39 /* XCTestManifests.swift */; }; - OBJ_327 /* MiniSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "Mini::MiniSwift::Product" /* MiniSwift.framework */; }; - OBJ_328 /* NIOConcurrencyHelpers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "swift-nio::NIOConcurrencyHelpers::Product" /* NIOConcurrencyHelpers.framework */; }; - OBJ_329 /* CNIOAtomics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "swift-nio::CNIOAtomics::Product" /* CNIOAtomics.framework */; }; - OBJ_330 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "RxSwift::RxSwift::Product" /* RxSwift.framework */; }; - OBJ_339 /* atomics.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_56 /* atomics.swift */; }; - OBJ_340 /* lock.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_57 /* lock.swift */; }; - OBJ_342 /* CNIOAtomics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "swift-nio::CNIOAtomics::Product" /* CNIOAtomics.framework */; }; - OBJ_348 /* AddRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_81 /* AddRef.swift */; }; - OBJ_349 /* Amb.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_82 /* Amb.swift */; }; - OBJ_350 /* AnonymousDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_83 /* AnonymousDisposable.swift */; }; - OBJ_351 /* AnonymousObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_84 /* AnonymousObserver.swift */; }; - OBJ_352 /* AnyObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_85 /* AnyObserver.swift */; }; - OBJ_353 /* AsMaybe.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_86 /* AsMaybe.swift */; }; - OBJ_354 /* AsSingle.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_87 /* AsSingle.swift */; }; - OBJ_355 /* AsyncLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_88 /* AsyncLock.swift */; }; - OBJ_356 /* AsyncSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_89 /* AsyncSubject.swift */; }; - OBJ_357 /* AtomicInt.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_90 /* AtomicInt.swift */; }; - OBJ_358 /* Bag+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_91 /* Bag+Rx.swift */; }; - OBJ_359 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_92 /* Bag.swift */; }; - OBJ_360 /* BehaviorSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_93 /* BehaviorSubject.swift */; }; - OBJ_361 /* BinaryDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_94 /* BinaryDisposable.swift */; }; - OBJ_362 /* BooleanDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_95 /* BooleanDisposable.swift */; }; - OBJ_363 /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_96 /* Buffer.swift */; }; - OBJ_364 /* Cancelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_97 /* Cancelable.swift */; }; - OBJ_365 /* Catch.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_98 /* Catch.swift */; }; - OBJ_366 /* CombineLatest+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_99 /* CombineLatest+Collection.swift */; }; - OBJ_367 /* CombineLatest+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_100 /* CombineLatest+arity.swift */; }; - OBJ_368 /* CombineLatest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_101 /* CombineLatest.swift */; }; - OBJ_369 /* CompactMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_102 /* CompactMap.swift */; }; - OBJ_370 /* Completable+AndThen.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_103 /* Completable+AndThen.swift */; }; - OBJ_371 /* Completable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_104 /* Completable.swift */; }; - OBJ_372 /* CompositeDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_105 /* CompositeDisposable.swift */; }; - OBJ_373 /* Concat.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_106 /* Concat.swift */; }; - OBJ_374 /* ConcurrentDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_107 /* ConcurrentDispatchQueueScheduler.swift */; }; - OBJ_375 /* ConcurrentMainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_108 /* ConcurrentMainScheduler.swift */; }; - OBJ_376 /* ConnectableObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_109 /* ConnectableObservableType.swift */; }; - OBJ_377 /* Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_110 /* Create.swift */; }; - OBJ_378 /* CurrentThreadScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_111 /* CurrentThreadScheduler.swift */; }; - OBJ_379 /* Date+Dispatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_112 /* Date+Dispatch.swift */; }; - OBJ_380 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_113 /* Debounce.swift */; }; - OBJ_381 /* Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_114 /* Debug.swift */; }; - OBJ_382 /* DefaultIfEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_115 /* DefaultIfEmpty.swift */; }; - OBJ_383 /* Deferred.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_116 /* Deferred.swift */; }; - OBJ_384 /* Delay.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_117 /* Delay.swift */; }; - OBJ_385 /* DelaySubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_118 /* DelaySubscription.swift */; }; - OBJ_386 /* Dematerialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_119 /* Dematerialize.swift */; }; - OBJ_387 /* Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_120 /* Deprecated.swift */; }; - OBJ_388 /* DispatchQueue+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_121 /* DispatchQueue+Extensions.swift */; }; - OBJ_389 /* DispatchQueueConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_122 /* DispatchQueueConfiguration.swift */; }; - OBJ_390 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_123 /* Disposable.swift */; }; - OBJ_391 /* Disposables.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_124 /* Disposables.swift */; }; - OBJ_392 /* DisposeBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_125 /* DisposeBag.swift */; }; - OBJ_393 /* DisposeBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_126 /* DisposeBase.swift */; }; - OBJ_394 /* DistinctUntilChanged.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_127 /* DistinctUntilChanged.swift */; }; - OBJ_395 /* Do.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_128 /* Do.swift */; }; - OBJ_396 /* ElementAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_129 /* ElementAt.swift */; }; - OBJ_397 /* Empty.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_130 /* Empty.swift */; }; - OBJ_398 /* Enumerated.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_131 /* Enumerated.swift */; }; - OBJ_399 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_132 /* Error.swift */; }; - OBJ_400 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_133 /* Errors.swift */; }; - OBJ_401 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_134 /* Event.swift */; }; - OBJ_402 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_135 /* Filter.swift */; }; - OBJ_403 /* First.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_136 /* First.swift */; }; - OBJ_404 /* Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_137 /* Generate.swift */; }; - OBJ_405 /* GroupBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_138 /* GroupBy.swift */; }; - OBJ_406 /* GroupedObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_139 /* GroupedObservable.swift */; }; - OBJ_407 /* HistoricalScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_140 /* HistoricalScheduler.swift */; }; - OBJ_408 /* HistoricalSchedulerTimeConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_141 /* HistoricalSchedulerTimeConverter.swift */; }; - OBJ_409 /* ImmediateSchedulerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_142 /* ImmediateSchedulerType.swift */; }; - OBJ_410 /* InfiniteSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_143 /* InfiniteSequence.swift */; }; - OBJ_411 /* InvocableScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_144 /* InvocableScheduledItem.swift */; }; - OBJ_412 /* InvocableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_145 /* InvocableType.swift */; }; - OBJ_413 /* Just.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_146 /* Just.swift */; }; - OBJ_414 /* Lock.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_147 /* Lock.swift */; }; - OBJ_415 /* LockOwnerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_148 /* LockOwnerType.swift */; }; - OBJ_416 /* MainScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_149 /* MainScheduler.swift */; }; - OBJ_417 /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_150 /* Map.swift */; }; - OBJ_418 /* Materialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_151 /* Materialize.swift */; }; - OBJ_419 /* Maybe.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_152 /* Maybe.swift */; }; - OBJ_420 /* Merge.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_153 /* Merge.swift */; }; - OBJ_421 /* Multicast.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_154 /* Multicast.swift */; }; - OBJ_422 /* Never.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_155 /* Never.swift */; }; - OBJ_423 /* NopDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_156 /* NopDisposable.swift */; }; - OBJ_424 /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_157 /* Observable.swift */; }; - OBJ_425 /* ObservableConvertibleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_158 /* ObservableConvertibleType.swift */; }; - OBJ_426 /* ObservableType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_159 /* ObservableType+Extensions.swift */; }; - OBJ_427 /* ObservableType+PrimitiveSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_160 /* ObservableType+PrimitiveSequence.swift */; }; - OBJ_428 /* ObservableType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_161 /* ObservableType.swift */; }; - OBJ_429 /* ObserveOn.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_162 /* ObserveOn.swift */; }; - OBJ_430 /* ObserverBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_163 /* ObserverBase.swift */; }; - OBJ_431 /* ObserverType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_164 /* ObserverType.swift */; }; - OBJ_432 /* OperationQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_165 /* OperationQueueScheduler.swift */; }; - OBJ_433 /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_166 /* Optional.swift */; }; - OBJ_434 /* Platform.Darwin.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_167 /* Platform.Darwin.swift */; }; - OBJ_435 /* Platform.Linux.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_168 /* Platform.Linux.swift */; }; - OBJ_436 /* PrimitiveSequence+Zip+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_169 /* PrimitiveSequence+Zip+arity.swift */; }; - OBJ_437 /* PrimitiveSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_170 /* PrimitiveSequence.swift */; }; - OBJ_438 /* PriorityQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_171 /* PriorityQueue.swift */; }; - OBJ_439 /* Producer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_172 /* Producer.swift */; }; - OBJ_440 /* PublishSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_173 /* PublishSubject.swift */; }; - OBJ_441 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_174 /* Queue.swift */; }; - OBJ_442 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_175 /* Range.swift */; }; - OBJ_443 /* Reactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_176 /* Reactive.swift */; }; - OBJ_444 /* RecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_177 /* RecursiveLock.swift */; }; - OBJ_445 /* RecursiveScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_178 /* RecursiveScheduler.swift */; }; - OBJ_446 /* Reduce.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_179 /* Reduce.swift */; }; - OBJ_447 /* RefCountDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_180 /* RefCountDisposable.swift */; }; - OBJ_448 /* Repeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_181 /* Repeat.swift */; }; - OBJ_449 /* ReplaySubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_182 /* ReplaySubject.swift */; }; - OBJ_450 /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_183 /* RetryWhen.swift */; }; - OBJ_451 /* Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_184 /* Rx.swift */; }; - OBJ_452 /* RxMutableBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_185 /* RxMutableBox.swift */; }; - OBJ_453 /* Sample.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_186 /* Sample.swift */; }; - OBJ_454 /* Scan.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_187 /* Scan.swift */; }; - OBJ_455 /* ScheduledDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_188 /* ScheduledDisposable.swift */; }; - OBJ_456 /* ScheduledItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_189 /* ScheduledItem.swift */; }; - OBJ_457 /* ScheduledItemType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_190 /* ScheduledItemType.swift */; }; - OBJ_458 /* SchedulerServices+Emulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_191 /* SchedulerServices+Emulation.swift */; }; - OBJ_459 /* SchedulerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_192 /* SchedulerType.swift */; }; - OBJ_460 /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_193 /* Sequence.swift */; }; - OBJ_461 /* SerialDispatchQueueScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_194 /* SerialDispatchQueueScheduler.swift */; }; - OBJ_462 /* SerialDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_195 /* SerialDisposable.swift */; }; - OBJ_463 /* ShareReplayScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_196 /* ShareReplayScope.swift */; }; - OBJ_464 /* Single.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_197 /* Single.swift */; }; - OBJ_465 /* SingleAssignmentDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_198 /* SingleAssignmentDisposable.swift */; }; - OBJ_466 /* SingleAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_199 /* SingleAsync.swift */; }; - OBJ_467 /* Sink.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_200 /* Sink.swift */; }; - OBJ_468 /* Skip.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_201 /* Skip.swift */; }; - OBJ_469 /* SkipUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_202 /* SkipUntil.swift */; }; - OBJ_470 /* SkipWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_203 /* SkipWhile.swift */; }; - OBJ_471 /* StartWith.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_204 /* StartWith.swift */; }; - OBJ_472 /* String+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_205 /* String+Rx.swift */; }; - OBJ_473 /* SubjectType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_206 /* SubjectType.swift */; }; - OBJ_474 /* SubscribeOn.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_207 /* SubscribeOn.swift */; }; - OBJ_475 /* SubscriptionDisposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_208 /* SubscriptionDisposable.swift */; }; - OBJ_476 /* SwiftSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_209 /* SwiftSupport.swift */; }; - OBJ_477 /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_210 /* Switch.swift */; }; - OBJ_478 /* SwitchIfEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_211 /* SwitchIfEmpty.swift */; }; - OBJ_479 /* SynchronizedDisposeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_212 /* SynchronizedDisposeType.swift */; }; - OBJ_480 /* SynchronizedOnType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_213 /* SynchronizedOnType.swift */; }; - OBJ_481 /* SynchronizedUnsubscribeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_214 /* SynchronizedUnsubscribeType.swift */; }; - OBJ_482 /* TailRecursiveSink.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_215 /* TailRecursiveSink.swift */; }; - OBJ_483 /* Take.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_216 /* Take.swift */; }; - OBJ_484 /* TakeLast.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_217 /* TakeLast.swift */; }; - OBJ_485 /* TakeUntil.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_218 /* TakeUntil.swift */; }; - OBJ_486 /* TakeWhile.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_219 /* TakeWhile.swift */; }; - OBJ_487 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_220 /* Throttle.swift */; }; - OBJ_488 /* Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_221 /* Timeout.swift */; }; - OBJ_489 /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_222 /* Timer.swift */; }; - OBJ_490 /* ToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_223 /* ToArray.swift */; }; - OBJ_491 /* Using.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_224 /* Using.swift */; }; - OBJ_492 /* VirtualTimeConverterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_225 /* VirtualTimeConverterType.swift */; }; - OBJ_493 /* VirtualTimeScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_226 /* VirtualTimeScheduler.swift */; }; - OBJ_494 /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_227 /* Window.swift */; }; - OBJ_495 /* WithLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_228 /* WithLatestFrom.swift */; }; - OBJ_496 /* Zip+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_229 /* Zip+Collection.swift */; }; - OBJ_497 /* Zip+arity.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_230 /* Zip+arity.swift */; }; - OBJ_498 /* Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_231 /* Zip.swift */; }; - OBJ_505 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_233 /* Package.swift */; }; - OBJ_511 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_74 /* Package.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - F2F0AA122321256C00BC7CD9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "swift-nio::NIOConcurrencyHelpers"; - remoteInfo = NIOConcurrencyHelpers; - }; - F2F0AA132321256C00BC7CD9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "swift-nio::CNIOAtomics"; - remoteInfo = CNIOAtomics; - }; - F2F0AA142321256C00BC7CD9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "swift-nio::CNIOAtomics"; - remoteInfo = CNIOAtomics; - }; - F2F0AA152321256C00BC7CD9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "RxSwift::RxSwift"; - remoteInfo = RxSwift; - }; - F2F0AA162321256C00BC7CD9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "Mini::MiniSwift"; - remoteInfo = MiniSwift; - }; - F2F0AA172321256C00BC7CD9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "swift-nio::NIOConcurrencyHelpers"; - remoteInfo = NIOConcurrencyHelpers; - }; - F2F0AA182321256C00BC7CD9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "swift-nio::CNIOAtomics"; - remoteInfo = CNIOAtomics; - }; - F2F0AA192321256C00BC7CD9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "RxSwift::RxSwift"; - remoteInfo = RxSwift; - }; - F2F0AA1A2321256E00BC7CD9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "Mini::MiniSwift"; - remoteInfo = MiniSwift; - }; - F2F0AA1B2321256E00BC7CD9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = "Mini::MiniSwiftTests"; - remoteInfo = MiniSwiftTests; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 12A3ADED2328D33400019F49 /* PrimitiveSequenceType+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrimitiveSequenceType+Extensions.swift"; sourceTree = ""; }; - "Mini::MiniSwift::Product" /* MiniSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MiniSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - "Mini::MiniSwiftTests::Product" /* MiniSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; path = MiniSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - OBJ_10 /* ActionReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionReducer.swift; sourceTree = ""; }; - OBJ_100 /* CombineLatest+arity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CombineLatest+arity.swift"; sourceTree = ""; }; - OBJ_101 /* CombineLatest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineLatest.swift; sourceTree = ""; }; - OBJ_102 /* CompactMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactMap.swift; sourceTree = ""; }; - OBJ_103 /* Completable+AndThen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Completable+AndThen.swift"; sourceTree = ""; }; - OBJ_104 /* Completable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Completable.swift; sourceTree = ""; }; - OBJ_105 /* CompositeDisposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositeDisposable.swift; sourceTree = ""; }; - OBJ_106 /* Concat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Concat.swift; sourceTree = ""; }; - OBJ_107 /* ConcurrentDispatchQueueScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurrentDispatchQueueScheduler.swift; sourceTree = ""; }; - OBJ_108 /* ConcurrentMainScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurrentMainScheduler.swift; sourceTree = ""; }; - OBJ_109 /* ConnectableObservableType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectableObservableType.swift; sourceTree = ""; }; - OBJ_11 /* Dispatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dispatcher.swift; sourceTree = ""; }; - OBJ_110 /* Create.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Create.swift; sourceTree = ""; }; - OBJ_111 /* CurrentThreadScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentThreadScheduler.swift; sourceTree = ""; }; - OBJ_112 /* Date+Dispatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Dispatch.swift"; sourceTree = ""; }; - OBJ_113 /* Debounce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debounce.swift; sourceTree = ""; }; - OBJ_114 /* Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debug.swift; sourceTree = ""; }; - OBJ_115 /* DefaultIfEmpty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultIfEmpty.swift; sourceTree = ""; }; - OBJ_116 /* Deferred.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deferred.swift; sourceTree = ""; }; - OBJ_117 /* Delay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delay.swift; sourceTree = ""; }; - OBJ_118 /* DelaySubscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelaySubscription.swift; sourceTree = ""; }; - OBJ_119 /* Dematerialize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dematerialize.swift; sourceTree = ""; }; - OBJ_12 /* Middleware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Middleware.swift; sourceTree = ""; }; - OBJ_120 /* Deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deprecated.swift; sourceTree = ""; }; - OBJ_121 /* DispatchQueue+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+Extensions.swift"; sourceTree = ""; }; - OBJ_122 /* DispatchQueueConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DispatchQueueConfiguration.swift; sourceTree = ""; }; - OBJ_123 /* Disposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Disposable.swift; sourceTree = ""; }; - OBJ_124 /* Disposables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Disposables.swift; sourceTree = ""; }; - OBJ_125 /* DisposeBag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisposeBag.swift; sourceTree = ""; }; - OBJ_126 /* DisposeBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisposeBase.swift; sourceTree = ""; }; - OBJ_127 /* DistinctUntilChanged.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistinctUntilChanged.swift; sourceTree = ""; }; - OBJ_128 /* Do.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Do.swift; sourceTree = ""; }; - OBJ_129 /* ElementAt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementAt.swift; sourceTree = ""; }; - OBJ_13 /* ReducerGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReducerGroup.swift; sourceTree = ""; }; - OBJ_130 /* Empty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Empty.swift; sourceTree = ""; }; - OBJ_131 /* Enumerated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Enumerated.swift; sourceTree = ""; }; - OBJ_132 /* Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; - OBJ_133 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; - OBJ_134 /* Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; - OBJ_135 /* Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = ""; }; - OBJ_136 /* First.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = First.swift; sourceTree = ""; }; - OBJ_137 /* Generate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = ""; }; - OBJ_138 /* GroupBy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupBy.swift; sourceTree = ""; }; - OBJ_139 /* GroupedObservable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupedObservable.swift; sourceTree = ""; }; - OBJ_14 /* Service.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Service.swift; sourceTree = ""; }; - OBJ_140 /* HistoricalScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoricalScheduler.swift; sourceTree = ""; }; - OBJ_141 /* HistoricalSchedulerTimeConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoricalSchedulerTimeConverter.swift; sourceTree = ""; }; - OBJ_142 /* ImmediateSchedulerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImmediateSchedulerType.swift; sourceTree = ""; }; - OBJ_143 /* InfiniteSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfiniteSequence.swift; sourceTree = ""; }; - OBJ_144 /* InvocableScheduledItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvocableScheduledItem.swift; sourceTree = ""; }; - OBJ_145 /* InvocableType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvocableType.swift; sourceTree = ""; }; - OBJ_146 /* Just.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Just.swift; sourceTree = ""; }; - OBJ_147 /* Lock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lock.swift; sourceTree = ""; }; - OBJ_148 /* LockOwnerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockOwnerType.swift; sourceTree = ""; }; - OBJ_149 /* MainScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainScheduler.swift; sourceTree = ""; }; - OBJ_15 /* StateType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateType.swift; sourceTree = ""; }; - OBJ_150 /* Map.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Map.swift; sourceTree = ""; }; - OBJ_151 /* Materialize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Materialize.swift; sourceTree = ""; }; - OBJ_152 /* Maybe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Maybe.swift; sourceTree = ""; }; - OBJ_153 /* Merge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Merge.swift; sourceTree = ""; }; - OBJ_154 /* Multicast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Multicast.swift; sourceTree = ""; }; - OBJ_155 /* Never.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Never.swift; sourceTree = ""; }; - OBJ_156 /* NopDisposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NopDisposable.swift; sourceTree = ""; }; - OBJ_157 /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; - OBJ_158 /* ObservableConvertibleType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableConvertibleType.swift; sourceTree = ""; }; - OBJ_159 /* ObservableType+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ObservableType+Extensions.swift"; sourceTree = ""; }; - OBJ_16 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = ""; }; - OBJ_160 /* ObservableType+PrimitiveSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ObservableType+PrimitiveSequence.swift"; sourceTree = ""; }; - OBJ_161 /* ObservableType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableType.swift; sourceTree = ""; }; - OBJ_162 /* ObserveOn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObserveOn.swift; sourceTree = ""; }; - OBJ_163 /* ObserverBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObserverBase.swift; sourceTree = ""; }; - OBJ_164 /* ObserverType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObserverType.swift; sourceTree = ""; }; - OBJ_165 /* OperationQueueScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationQueueScheduler.swift; sourceTree = ""; }; - OBJ_166 /* Optional.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = ""; }; - OBJ_167 /* Platform.Darwin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Platform.Darwin.swift; sourceTree = ""; }; - OBJ_168 /* Platform.Linux.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Platform.Linux.swift; sourceTree = ""; }; - OBJ_169 /* PrimitiveSequence+Zip+arity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrimitiveSequence+Zip+arity.swift"; sourceTree = ""; }; - OBJ_17 /* Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = ""; }; - OBJ_170 /* PrimitiveSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimitiveSequence.swift; sourceTree = ""; }; - OBJ_171 /* PriorityQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriorityQueue.swift; sourceTree = ""; }; - OBJ_172 /* Producer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Producer.swift; sourceTree = ""; }; - OBJ_173 /* PublishSubject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublishSubject.swift; sourceTree = ""; }; - OBJ_174 /* Queue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; - OBJ_175 /* Range.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Range.swift; sourceTree = ""; }; - OBJ_176 /* Reactive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reactive.swift; sourceTree = ""; }; - OBJ_177 /* RecursiveLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecursiveLock.swift; sourceTree = ""; }; - OBJ_178 /* RecursiveScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecursiveScheduler.swift; sourceTree = ""; }; - OBJ_179 /* Reduce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reduce.swift; sourceTree = ""; }; - OBJ_180 /* RefCountDisposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefCountDisposable.swift; sourceTree = ""; }; - OBJ_181 /* Repeat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Repeat.swift; sourceTree = ""; }; - OBJ_182 /* ReplaySubject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplaySubject.swift; sourceTree = ""; }; - OBJ_183 /* RetryWhen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryWhen.swift; sourceTree = ""; }; - OBJ_184 /* Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rx.swift; sourceTree = ""; }; - OBJ_185 /* RxMutableBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxMutableBox.swift; sourceTree = ""; }; - OBJ_186 /* Sample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sample.swift; sourceTree = ""; }; - OBJ_187 /* Scan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scan.swift; sourceTree = ""; }; - OBJ_188 /* ScheduledDisposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduledDisposable.swift; sourceTree = ""; }; - OBJ_189 /* ScheduledItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduledItem.swift; sourceTree = ""; }; - OBJ_190 /* ScheduledItemType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduledItemType.swift; sourceTree = ""; }; - OBJ_191 /* SchedulerServices+Emulation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SchedulerServices+Emulation.swift"; sourceTree = ""; }; - OBJ_192 /* SchedulerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchedulerType.swift; sourceTree = ""; }; - OBJ_193 /* Sequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sequence.swift; sourceTree = ""; }; - OBJ_194 /* SerialDispatchQueueScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialDispatchQueueScheduler.swift; sourceTree = ""; }; - OBJ_195 /* SerialDisposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialDisposable.swift; sourceTree = ""; }; - OBJ_196 /* ShareReplayScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareReplayScope.swift; sourceTree = ""; }; - OBJ_197 /* Single.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Single.swift; sourceTree = ""; }; - OBJ_198 /* SingleAssignmentDisposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleAssignmentDisposable.swift; sourceTree = ""; }; - OBJ_199 /* SingleAsync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleAsync.swift; sourceTree = ""; }; - OBJ_20 /* Dictionary+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Extensions.swift"; sourceTree = ""; }; - OBJ_200 /* Sink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sink.swift; sourceTree = ""; }; - OBJ_201 /* Skip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Skip.swift; sourceTree = ""; }; - OBJ_202 /* SkipUntil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkipUntil.swift; sourceTree = ""; }; - OBJ_203 /* SkipWhile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkipWhile.swift; sourceTree = ""; }; - OBJ_204 /* StartWith.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartWith.swift; sourceTree = ""; }; - OBJ_205 /* String+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Rx.swift"; sourceTree = ""; }; - OBJ_206 /* SubjectType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubjectType.swift; sourceTree = ""; }; - OBJ_207 /* SubscribeOn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscribeOn.swift; sourceTree = ""; }; - OBJ_208 /* SubscriptionDisposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionDisposable.swift; sourceTree = ""; }; - OBJ_209 /* SwiftSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftSupport.swift; sourceTree = ""; }; - OBJ_21 /* DispatchQueue+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+Extensions.swift"; sourceTree = ""; }; - OBJ_210 /* Switch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Switch.swift; sourceTree = ""; }; - OBJ_211 /* SwitchIfEmpty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchIfEmpty.swift; sourceTree = ""; }; - OBJ_212 /* SynchronizedDisposeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizedDisposeType.swift; sourceTree = ""; }; - OBJ_213 /* SynchronizedOnType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizedOnType.swift; sourceTree = ""; }; - OBJ_214 /* SynchronizedUnsubscribeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizedUnsubscribeType.swift; sourceTree = ""; }; - OBJ_215 /* TailRecursiveSink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TailRecursiveSink.swift; sourceTree = ""; }; - OBJ_216 /* Take.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Take.swift; sourceTree = ""; }; - OBJ_217 /* TakeLast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeLast.swift; sourceTree = ""; }; - OBJ_218 /* TakeUntil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeUntil.swift; sourceTree = ""; }; - OBJ_219 /* TakeWhile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TakeWhile.swift; sourceTree = ""; }; - OBJ_22 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; - OBJ_220 /* Throttle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Throttle.swift; sourceTree = ""; }; - OBJ_221 /* Timeout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timeout.swift; sourceTree = ""; }; - OBJ_222 /* Timer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = ""; }; - OBJ_223 /* ToArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToArray.swift; sourceTree = ""; }; - OBJ_224 /* Using.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Using.swift; sourceTree = ""; }; - OBJ_225 /* VirtualTimeConverterType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualTimeConverterType.swift; sourceTree = ""; }; - OBJ_226 /* VirtualTimeScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualTimeScheduler.swift; sourceTree = ""; }; - OBJ_227 /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = ""; }; - OBJ_228 /* WithLatestFrom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithLatestFrom.swift; sourceTree = ""; }; - OBJ_229 /* Zip+Collection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Zip+Collection.swift"; sourceTree = ""; }; - OBJ_230 /* Zip+arity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Zip+arity.swift"; sourceTree = ""; }; - OBJ_231 /* Zip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Zip.swift; sourceTree = ""; }; - OBJ_233 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; name = Package.swift; path = "/Users/minuscorp/Documents/GitHub/mini-swift/.build/checkouts/RxSwift/Package.swift"; sourceTree = ""; }; - OBJ_24 /* TestMiddleware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestMiddleware.swift; sourceTree = ""; }; - OBJ_240 /* bin */ = {isa = PBXFileReference; lastKnownFileType = folder; path = bin; sourceTree = SOURCE_ROOT; }; - OBJ_242 /* fastlane */ = {isa = PBXFileReference; lastKnownFileType = folder; path = fastlane; sourceTree = SOURCE_ROOT; }; - OBJ_243 /* Templates */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Templates; sourceTree = SOURCE_ROOT; }; - OBJ_245 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; - OBJ_247 /* MiniSwift.framework.coverage.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = MiniSwift.framework.coverage.txt; sourceTree = ""; }; - OBJ_248 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - OBJ_249 /* Rakefile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Rakefile; sourceTree = ""; }; - OBJ_25 /* OrderedSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedSet.swift; sourceTree = ""; }; - OBJ_250 /* MiniSwiftTests.xctest.coverage.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = MiniSwiftTests.xctest.coverage.txt; sourceTree = ""; }; - OBJ_251 /* _config.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = _config.yml; sourceTree = ""; }; - OBJ_252 /* DangerFile */ = {isa = PBXFileReference; lastKnownFileType = text; path = DangerFile; sourceTree = ""; }; - OBJ_253 /* Gemfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Gemfile; sourceTree = ""; }; - OBJ_254 /* Mini-Swift.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = "Mini-Swift.podspec"; sourceTree = ""; }; - OBJ_255 /* Gemfile.lock */ = {isa = PBXFileReference; lastKnownFileType = text; path = Gemfile.lock; sourceTree = ""; }; - OBJ_256 /* Brewfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Brewfile; sourceTree = ""; }; - OBJ_26 /* PayloadAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayloadAction.swift; sourceTree = ""; }; - OBJ_28 /* ObservableType+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ObservableType+Extensions.swift"; sourceTree = ""; }; - OBJ_30 /* LoggingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingService.swift; sourceTree = ""; }; - OBJ_31 /* SharedDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedDictionary.swift; sourceTree = ""; }; - OBJ_34 /* ChainTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainTests.swift; sourceTree = ""; }; - OBJ_35 /* Dictionary+ExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+ExtensionsTests.swift"; sourceTree = ""; }; - OBJ_36 /* DispatcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DispatcherTests.swift; sourceTree = ""; }; - OBJ_37 /* ReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReducerTests.swift; sourceTree = ""; }; - OBJ_38 /* TaskTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskTests.swift; sourceTree = ""; }; - OBJ_39 /* XCTestManifests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestManifests.swift; sourceTree = ""; }; - OBJ_44 /* c-atomics.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "c-atomics.c"; sourceTree = ""; }; - OBJ_46 /* CNIOAtomics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CNIOAtomics.h; sourceTree = ""; }; - OBJ_47 /* cpp_magic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cpp_magic.h; sourceTree = ""; }; - OBJ_56 /* atomics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = atomics.swift; sourceTree = ""; }; - OBJ_57 /* lock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = lock.swift; sourceTree = ""; }; - OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; - OBJ_74 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; name = Package.swift; path = "/Users/minuscorp/Documents/GitHub/mini-swift/.build/checkouts/swift-nio/Package.swift"; sourceTree = ""; }; - OBJ_81 /* AddRef.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRef.swift; sourceTree = ""; }; - OBJ_82 /* Amb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Amb.swift; sourceTree = ""; }; - OBJ_83 /* AnonymousDisposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnonymousDisposable.swift; sourceTree = ""; }; - OBJ_84 /* AnonymousObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnonymousObserver.swift; sourceTree = ""; }; - OBJ_85 /* AnyObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyObserver.swift; sourceTree = ""; }; - OBJ_86 /* AsMaybe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsMaybe.swift; sourceTree = ""; }; - OBJ_87 /* AsSingle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsSingle.swift; sourceTree = ""; }; - OBJ_88 /* AsyncLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncLock.swift; sourceTree = ""; }; - OBJ_89 /* AsyncSubject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSubject.swift; sourceTree = ""; }; - OBJ_9 /* Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; - OBJ_90 /* AtomicInt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtomicInt.swift; sourceTree = ""; }; - OBJ_91 /* Bag+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bag+Rx.swift"; sourceTree = ""; }; - OBJ_92 /* Bag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = ""; }; - OBJ_93 /* BehaviorSubject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BehaviorSubject.swift; sourceTree = ""; }; - OBJ_94 /* BinaryDisposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryDisposable.swift; sourceTree = ""; }; - OBJ_95 /* BooleanDisposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BooleanDisposable.swift; sourceTree = ""; }; - OBJ_96 /* Buffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buffer.swift; sourceTree = ""; }; - OBJ_97 /* Cancelable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cancelable.swift; sourceTree = ""; }; - OBJ_98 /* Catch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Catch.swift; sourceTree = ""; }; - OBJ_99 /* CombineLatest+Collection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CombineLatest+Collection.swift"; sourceTree = ""; }; - "RxSwift::RxSwift::Product" /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - "swift-nio::CNIOAtomics::Product" /* CNIOAtomics.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CNIOAtomics.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - "swift-nio::NIOConcurrencyHelpers::Product" /* NIOConcurrencyHelpers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = NIOConcurrencyHelpers.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - OBJ_266 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_307 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 0; - files = ( - OBJ_308 /* NIOConcurrencyHelpers.framework in Frameworks */, - OBJ_309 /* CNIOAtomics.framework in Frameworks */, - OBJ_310 /* RxSwift.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_326 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 0; - files = ( - OBJ_327 /* MiniSwift.framework in Frameworks */, - OBJ_328 /* NIOConcurrencyHelpers.framework in Frameworks */, - OBJ_329 /* CNIOAtomics.framework in Frameworks */, - OBJ_330 /* RxSwift.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_341 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 0; - files = ( - OBJ_342 /* CNIOAtomics.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_499 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 12A3ADEB2328D25600019F49 /* LoggingService */ = { - isa = PBXGroup; - children = ( - OBJ_30 /* LoggingService.swift */, - ); - path = LoggingService; - sourceTree = ""; - }; - 12A3ADEC2328D29F00019F49 /* TestMiddleware */ = { - isa = PBXGroup; - children = ( - OBJ_24 /* TestMiddleware.swift */, - ); - path = TestMiddleware; - sourceTree = ""; - }; - OBJ_18 /* Utils */ = { - isa = PBXGroup; - children = ( - OBJ_19 /* Foundation */, - OBJ_25 /* OrderedSet.swift */, - OBJ_26 /* PayloadAction.swift */, - OBJ_27 /* RxSwift */, - OBJ_31 /* SharedDictionary.swift */, - ); - path = Utils; - sourceTree = ""; - }; - OBJ_19 /* Foundation */ = { - isa = PBXGroup; - children = ( - OBJ_20 /* Dictionary+Extensions.swift */, - OBJ_21 /* DispatchQueue+Extensions.swift */, - OBJ_22 /* String+Extensions.swift */, - ); - path = Foundation; - sourceTree = ""; - }; - OBJ_232 /* RxTest */ = { - isa = PBXGroup; - children = ( - ); - name = RxTest; - path = .build/checkouts/RxSwift/Sources/RxTest; - sourceTree = SOURCE_ROOT; - }; - OBJ_234 /* Products */ = { - isa = PBXGroup; - children = ( - "Mini::MiniSwift::Product" /* MiniSwift.framework */, - "swift-nio::CNIOAtomics::Product" /* CNIOAtomics.framework */, - "Mini::MiniSwiftTests::Product" /* MiniSwiftTests.xctest */, - "RxSwift::RxSwift::Product" /* RxSwift.framework */, - "swift-nio::NIOConcurrencyHelpers::Product" /* NIOConcurrencyHelpers.framework */, - ); - name = Products; - sourceTree = BUILT_PRODUCTS_DIR; - }; - OBJ_27 /* RxSwift */ = { - isa = PBXGroup; - children = ( - OBJ_28 /* ObservableType+Extensions.swift */, - 12A3ADED2328D33400019F49 /* PrimitiveSequenceType+Extensions.swift */, - ); - path = RxSwift; - sourceTree = ""; - }; - OBJ_32 /* Tests */ = { - isa = PBXGroup; - children = ( - OBJ_33 /* MiniSwiftTests */, - ); - name = Tests; - sourceTree = SOURCE_ROOT; - }; - OBJ_33 /* MiniSwiftTests */ = { - isa = PBXGroup; - children = ( - OBJ_34 /* ChainTests.swift */, - OBJ_35 /* Dictionary+ExtensionsTests.swift */, - OBJ_36 /* DispatcherTests.swift */, - OBJ_37 /* ReducerTests.swift */, - OBJ_38 /* TaskTests.swift */, - OBJ_39 /* XCTestManifests.swift */, - ); - name = MiniSwiftTests; - path = Tests/MiniSwiftTests; - sourceTree = SOURCE_ROOT; - }; - OBJ_40 /* Dependencies */ = { - isa = PBXGroup; - children = ( - OBJ_41 /* swift-nio 2.6.0 */, - OBJ_75 /* RxSwift 5.0.1 */, - ); - name = Dependencies; - sourceTree = ""; - }; - OBJ_41 /* swift-nio 2.6.0 */ = { - isa = PBXGroup; - children = ( - OBJ_42 /* CNIOAtomics */, - OBJ_48 /* CNIODarwin */, - OBJ_49 /* CNIOHTTPParser */, - OBJ_50 /* CNIOLinux */, - OBJ_51 /* CNIOSHA1 */, - OBJ_52 /* NIO */, - OBJ_53 /* NIOChatClient */, - OBJ_54 /* NIOChatServer */, - OBJ_55 /* NIOConcurrencyHelpers */, - OBJ_58 /* NIOEchoClient */, - OBJ_59 /* NIOEchoServer */, - OBJ_60 /* NIOFoundationCompat */, - OBJ_61 /* NIOHTTP1 */, - OBJ_62 /* NIOHTTP1Client */, - OBJ_63 /* NIOHTTP1Server */, - OBJ_64 /* NIOMulticastChat */, - OBJ_65 /* NIOPerformanceTester */, - OBJ_66 /* NIOTLS */, - OBJ_67 /* NIOTestUtils */, - OBJ_68 /* NIOUDPEchoClient */, - OBJ_69 /* NIOUDPEchoServer */, - OBJ_70 /* NIOWebSocket */, - OBJ_71 /* NIOWebSocketClient */, - OBJ_72 /* NIOWebSocketServer */, - OBJ_73 /* _NIO1APIShims */, - OBJ_74 /* Package.swift */, - ); - name = "swift-nio 2.6.0"; - sourceTree = SOURCE_ROOT; - }; - OBJ_42 /* CNIOAtomics */ = { - isa = PBXGroup; - children = ( - OBJ_43 /* src */, - OBJ_45 /* include */, - ); - name = CNIOAtomics; - path = ".build/checkouts/swift-nio/Sources/CNIOAtomics"; - sourceTree = SOURCE_ROOT; - }; - OBJ_43 /* src */ = { - isa = PBXGroup; - children = ( - OBJ_44 /* c-atomics.c */, - ); - path = src; - sourceTree = ""; - }; - OBJ_45 /* include */ = { - isa = PBXGroup; - children = ( - OBJ_46 /* CNIOAtomics.h */, - OBJ_47 /* cpp_magic.h */, - ); - path = include; - sourceTree = ""; - }; - OBJ_48 /* CNIODarwin */ = { - isa = PBXGroup; - children = ( - ); - name = CNIODarwin; - path = ".build/checkouts/swift-nio/Sources/CNIODarwin"; - sourceTree = SOURCE_ROOT; - }; - OBJ_49 /* CNIOHTTPParser */ = { - isa = PBXGroup; - children = ( - ); - name = CNIOHTTPParser; - path = ".build/checkouts/swift-nio/Sources/CNIOHTTPParser"; - sourceTree = SOURCE_ROOT; - }; - OBJ_5 = { - isa = PBXGroup; - children = ( - OBJ_6 /* Package.swift */, - OBJ_7 /* Sources */, - OBJ_32 /* Tests */, - OBJ_40 /* Dependencies */, - OBJ_234 /* Products */, - OBJ_240 /* bin */, - OBJ_242 /* fastlane */, - OBJ_243 /* Templates */, - OBJ_245 /* LICENSE */, - OBJ_247 /* MiniSwift.framework.coverage.txt */, - OBJ_248 /* README.md */, - OBJ_249 /* Rakefile */, - OBJ_250 /* MiniSwiftTests.xctest.coverage.txt */, - OBJ_251 /* _config.yml */, - OBJ_252 /* DangerFile */, - OBJ_253 /* Gemfile */, - OBJ_254 /* Mini-Swift.podspec */, - OBJ_255 /* Gemfile.lock */, - OBJ_256 /* Brewfile */, - ); - sourceTree = ""; - }; - OBJ_50 /* CNIOLinux */ = { - isa = PBXGroup; - children = ( - ); - name = CNIOLinux; - path = ".build/checkouts/swift-nio/Sources/CNIOLinux"; - sourceTree = SOURCE_ROOT; - }; - OBJ_51 /* CNIOSHA1 */ = { - isa = PBXGroup; - children = ( - ); - name = CNIOSHA1; - path = ".build/checkouts/swift-nio/Sources/CNIOSHA1"; - sourceTree = SOURCE_ROOT; - }; - OBJ_52 /* NIO */ = { - isa = PBXGroup; - children = ( - ); - name = NIO; - path = ".build/checkouts/swift-nio/Sources/NIO"; - sourceTree = SOURCE_ROOT; - }; - OBJ_53 /* NIOChatClient */ = { - isa = PBXGroup; - children = ( - ); - name = NIOChatClient; - path = ".build/checkouts/swift-nio/Sources/NIOChatClient"; - sourceTree = SOURCE_ROOT; - }; - OBJ_54 /* NIOChatServer */ = { - isa = PBXGroup; - children = ( - ); - name = NIOChatServer; - path = ".build/checkouts/swift-nio/Sources/NIOChatServer"; - sourceTree = SOURCE_ROOT; - }; - OBJ_55 /* NIOConcurrencyHelpers */ = { - isa = PBXGroup; - children = ( - OBJ_56 /* atomics.swift */, - OBJ_57 /* lock.swift */, - ); - name = NIOConcurrencyHelpers; - path = ".build/checkouts/swift-nio/Sources/NIOConcurrencyHelpers"; - sourceTree = SOURCE_ROOT; - }; - OBJ_58 /* NIOEchoClient */ = { - isa = PBXGroup; - children = ( - ); - name = NIOEchoClient; - path = ".build/checkouts/swift-nio/Sources/NIOEchoClient"; - sourceTree = SOURCE_ROOT; - }; - OBJ_59 /* NIOEchoServer */ = { - isa = PBXGroup; - children = ( - ); - name = NIOEchoServer; - path = ".build/checkouts/swift-nio/Sources/NIOEchoServer"; - sourceTree = SOURCE_ROOT; - }; - OBJ_60 /* NIOFoundationCompat */ = { - isa = PBXGroup; - children = ( - ); - name = NIOFoundationCompat; - path = ".build/checkouts/swift-nio/Sources/NIOFoundationCompat"; - sourceTree = SOURCE_ROOT; - }; - OBJ_61 /* NIOHTTP1 */ = { - isa = PBXGroup; - children = ( - ); - name = NIOHTTP1; - path = ".build/checkouts/swift-nio/Sources/NIOHTTP1"; - sourceTree = SOURCE_ROOT; - }; - OBJ_62 /* NIOHTTP1Client */ = { - isa = PBXGroup; - children = ( - ); - name = NIOHTTP1Client; - path = ".build/checkouts/swift-nio/Sources/NIOHTTP1Client"; - sourceTree = SOURCE_ROOT; - }; - OBJ_63 /* NIOHTTP1Server */ = { - isa = PBXGroup; - children = ( - ); - name = NIOHTTP1Server; - path = ".build/checkouts/swift-nio/Sources/NIOHTTP1Server"; - sourceTree = SOURCE_ROOT; - }; - OBJ_64 /* NIOMulticastChat */ = { - isa = PBXGroup; - children = ( - ); - name = NIOMulticastChat; - path = ".build/checkouts/swift-nio/Sources/NIOMulticastChat"; - sourceTree = SOURCE_ROOT; - }; - OBJ_65 /* NIOPerformanceTester */ = { - isa = PBXGroup; - children = ( - ); - name = NIOPerformanceTester; - path = ".build/checkouts/swift-nio/Sources/NIOPerformanceTester"; - sourceTree = SOURCE_ROOT; - }; - OBJ_66 /* NIOTLS */ = { - isa = PBXGroup; - children = ( - ); - name = NIOTLS; - path = ".build/checkouts/swift-nio/Sources/NIOTLS"; - sourceTree = SOURCE_ROOT; - }; - OBJ_67 /* NIOTestUtils */ = { - isa = PBXGroup; - children = ( - ); - name = NIOTestUtils; - path = ".build/checkouts/swift-nio/Sources/NIOTestUtils"; - sourceTree = SOURCE_ROOT; - }; - OBJ_68 /* NIOUDPEchoClient */ = { - isa = PBXGroup; - children = ( - ); - name = NIOUDPEchoClient; - path = ".build/checkouts/swift-nio/Sources/NIOUDPEchoClient"; - sourceTree = SOURCE_ROOT; - }; - OBJ_69 /* NIOUDPEchoServer */ = { - isa = PBXGroup; - children = ( - ); - name = NIOUDPEchoServer; - path = ".build/checkouts/swift-nio/Sources/NIOUDPEchoServer"; - sourceTree = SOURCE_ROOT; - }; - OBJ_7 /* Sources */ = { - isa = PBXGroup; - children = ( - OBJ_8 /* MiniSwift */, - ); - name = Sources; - sourceTree = SOURCE_ROOT; - }; - OBJ_70 /* NIOWebSocket */ = { - isa = PBXGroup; - children = ( - ); - name = NIOWebSocket; - path = ".build/checkouts/swift-nio/Sources/NIOWebSocket"; - sourceTree = SOURCE_ROOT; - }; - OBJ_71 /* NIOWebSocketClient */ = { - isa = PBXGroup; - children = ( - ); - name = NIOWebSocketClient; - path = ".build/checkouts/swift-nio/Sources/NIOWebSocketClient"; - sourceTree = SOURCE_ROOT; - }; - OBJ_72 /* NIOWebSocketServer */ = { - isa = PBXGroup; - children = ( - ); - name = NIOWebSocketServer; - path = ".build/checkouts/swift-nio/Sources/NIOWebSocketServer"; - sourceTree = SOURCE_ROOT; - }; - OBJ_73 /* _NIO1APIShims */ = { - isa = PBXGroup; - children = ( - ); - name = _NIO1APIShims; - path = ".build/checkouts/swift-nio/Sources/_NIO1APIShims"; - sourceTree = SOURCE_ROOT; - }; - OBJ_75 /* RxSwift 5.0.1 */ = { - isa = PBXGroup; - children = ( - OBJ_76 /* RxBlocking */, - OBJ_77 /* RxCocoa */, - OBJ_78 /* RxCocoaRuntime */, - OBJ_79 /* RxRelay */, - OBJ_80 /* RxSwift */, - OBJ_232 /* RxTest */, - OBJ_233 /* Package.swift */, - ); - name = "RxSwift 5.0.1"; - sourceTree = SOURCE_ROOT; - }; - OBJ_76 /* RxBlocking */ = { - isa = PBXGroup; - children = ( - ); - name = RxBlocking; - path = .build/checkouts/RxSwift/Sources/RxBlocking; - sourceTree = SOURCE_ROOT; - }; - OBJ_77 /* RxCocoa */ = { - isa = PBXGroup; - children = ( - ); - name = RxCocoa; - path = .build/checkouts/RxSwift/Sources/RxCocoa; - sourceTree = SOURCE_ROOT; - }; - OBJ_78 /* RxCocoaRuntime */ = { - isa = PBXGroup; - children = ( - ); - name = RxCocoaRuntime; - path = .build/checkouts/RxSwift/Sources/RxCocoaRuntime; - sourceTree = SOURCE_ROOT; - }; - OBJ_79 /* RxRelay */ = { - isa = PBXGroup; - children = ( - ); - name = RxRelay; - path = .build/checkouts/RxSwift/Sources/RxRelay; - sourceTree = SOURCE_ROOT; - }; - OBJ_8 /* MiniSwift */ = { - isa = PBXGroup; - children = ( - 12A3ADEC2328D29F00019F49 /* TestMiddleware */, - 12A3ADEB2328D25600019F49 /* LoggingService */, - OBJ_9 /* Action.swift */, - OBJ_10 /* ActionReducer.swift */, - OBJ_11 /* Dispatcher.swift */, - OBJ_12 /* Middleware.swift */, - OBJ_13 /* ReducerGroup.swift */, - OBJ_14 /* Service.swift */, - OBJ_15 /* StateType.swift */, - OBJ_16 /* Store.swift */, - OBJ_17 /* Task.swift */, - OBJ_18 /* Utils */, - ); - name = MiniSwift; - path = Sources/MiniSwift; - sourceTree = SOURCE_ROOT; - }; - OBJ_80 /* RxSwift */ = { - isa = PBXGroup; - children = ( - OBJ_81 /* AddRef.swift */, - OBJ_82 /* Amb.swift */, - OBJ_83 /* AnonymousDisposable.swift */, - OBJ_84 /* AnonymousObserver.swift */, - OBJ_85 /* AnyObserver.swift */, - OBJ_86 /* AsMaybe.swift */, - OBJ_87 /* AsSingle.swift */, - OBJ_88 /* AsyncLock.swift */, - OBJ_89 /* AsyncSubject.swift */, - OBJ_90 /* AtomicInt.swift */, - OBJ_91 /* Bag+Rx.swift */, - OBJ_92 /* Bag.swift */, - OBJ_93 /* BehaviorSubject.swift */, - OBJ_94 /* BinaryDisposable.swift */, - OBJ_95 /* BooleanDisposable.swift */, - OBJ_96 /* Buffer.swift */, - OBJ_97 /* Cancelable.swift */, - OBJ_98 /* Catch.swift */, - OBJ_99 /* CombineLatest+Collection.swift */, - OBJ_100 /* CombineLatest+arity.swift */, - OBJ_101 /* CombineLatest.swift */, - OBJ_102 /* CompactMap.swift */, - OBJ_103 /* Completable+AndThen.swift */, - OBJ_104 /* Completable.swift */, - OBJ_105 /* CompositeDisposable.swift */, - OBJ_106 /* Concat.swift */, - OBJ_107 /* ConcurrentDispatchQueueScheduler.swift */, - OBJ_108 /* ConcurrentMainScheduler.swift */, - OBJ_109 /* ConnectableObservableType.swift */, - OBJ_110 /* Create.swift */, - OBJ_111 /* CurrentThreadScheduler.swift */, - OBJ_112 /* Date+Dispatch.swift */, - OBJ_113 /* Debounce.swift */, - OBJ_114 /* Debug.swift */, - OBJ_115 /* DefaultIfEmpty.swift */, - OBJ_116 /* Deferred.swift */, - OBJ_117 /* Delay.swift */, - OBJ_118 /* DelaySubscription.swift */, - OBJ_119 /* Dematerialize.swift */, - OBJ_120 /* Deprecated.swift */, - OBJ_121 /* DispatchQueue+Extensions.swift */, - OBJ_122 /* DispatchQueueConfiguration.swift */, - OBJ_123 /* Disposable.swift */, - OBJ_124 /* Disposables.swift */, - OBJ_125 /* DisposeBag.swift */, - OBJ_126 /* DisposeBase.swift */, - OBJ_127 /* DistinctUntilChanged.swift */, - OBJ_128 /* Do.swift */, - OBJ_129 /* ElementAt.swift */, - OBJ_130 /* Empty.swift */, - OBJ_131 /* Enumerated.swift */, - OBJ_132 /* Error.swift */, - OBJ_133 /* Errors.swift */, - OBJ_134 /* Event.swift */, - OBJ_135 /* Filter.swift */, - OBJ_136 /* First.swift */, - OBJ_137 /* Generate.swift */, - OBJ_138 /* GroupBy.swift */, - OBJ_139 /* GroupedObservable.swift */, - OBJ_140 /* HistoricalScheduler.swift */, - OBJ_141 /* HistoricalSchedulerTimeConverter.swift */, - OBJ_142 /* ImmediateSchedulerType.swift */, - OBJ_143 /* InfiniteSequence.swift */, - OBJ_144 /* InvocableScheduledItem.swift */, - OBJ_145 /* InvocableType.swift */, - OBJ_146 /* Just.swift */, - OBJ_147 /* Lock.swift */, - OBJ_148 /* LockOwnerType.swift */, - OBJ_149 /* MainScheduler.swift */, - OBJ_150 /* Map.swift */, - OBJ_151 /* Materialize.swift */, - OBJ_152 /* Maybe.swift */, - OBJ_153 /* Merge.swift */, - OBJ_154 /* Multicast.swift */, - OBJ_155 /* Never.swift */, - OBJ_156 /* NopDisposable.swift */, - OBJ_157 /* Observable.swift */, - OBJ_158 /* ObservableConvertibleType.swift */, - OBJ_159 /* ObservableType+Extensions.swift */, - OBJ_160 /* ObservableType+PrimitiveSequence.swift */, - OBJ_161 /* ObservableType.swift */, - OBJ_162 /* ObserveOn.swift */, - OBJ_163 /* ObserverBase.swift */, - OBJ_164 /* ObserverType.swift */, - OBJ_165 /* OperationQueueScheduler.swift */, - OBJ_166 /* Optional.swift */, - OBJ_167 /* Platform.Darwin.swift */, - OBJ_168 /* Platform.Linux.swift */, - OBJ_169 /* PrimitiveSequence+Zip+arity.swift */, - OBJ_170 /* PrimitiveSequence.swift */, - OBJ_171 /* PriorityQueue.swift */, - OBJ_172 /* Producer.swift */, - OBJ_173 /* PublishSubject.swift */, - OBJ_174 /* Queue.swift */, - OBJ_175 /* Range.swift */, - OBJ_176 /* Reactive.swift */, - OBJ_177 /* RecursiveLock.swift */, - OBJ_178 /* RecursiveScheduler.swift */, - OBJ_179 /* Reduce.swift */, - OBJ_180 /* RefCountDisposable.swift */, - OBJ_181 /* Repeat.swift */, - OBJ_182 /* ReplaySubject.swift */, - OBJ_183 /* RetryWhen.swift */, - OBJ_184 /* Rx.swift */, - OBJ_185 /* RxMutableBox.swift */, - OBJ_186 /* Sample.swift */, - OBJ_187 /* Scan.swift */, - OBJ_188 /* ScheduledDisposable.swift */, - OBJ_189 /* ScheduledItem.swift */, - OBJ_190 /* ScheduledItemType.swift */, - OBJ_191 /* SchedulerServices+Emulation.swift */, - OBJ_192 /* SchedulerType.swift */, - OBJ_193 /* Sequence.swift */, - OBJ_194 /* SerialDispatchQueueScheduler.swift */, - OBJ_195 /* SerialDisposable.swift */, - OBJ_196 /* ShareReplayScope.swift */, - OBJ_197 /* Single.swift */, - OBJ_198 /* SingleAssignmentDisposable.swift */, - OBJ_199 /* SingleAsync.swift */, - OBJ_200 /* Sink.swift */, - OBJ_201 /* Skip.swift */, - OBJ_202 /* SkipUntil.swift */, - OBJ_203 /* SkipWhile.swift */, - OBJ_204 /* StartWith.swift */, - OBJ_205 /* String+Rx.swift */, - OBJ_206 /* SubjectType.swift */, - OBJ_207 /* SubscribeOn.swift */, - OBJ_208 /* SubscriptionDisposable.swift */, - OBJ_209 /* SwiftSupport.swift */, - OBJ_210 /* Switch.swift */, - OBJ_211 /* SwitchIfEmpty.swift */, - OBJ_212 /* SynchronizedDisposeType.swift */, - OBJ_213 /* SynchronizedOnType.swift */, - OBJ_214 /* SynchronizedUnsubscribeType.swift */, - OBJ_215 /* TailRecursiveSink.swift */, - OBJ_216 /* Take.swift */, - OBJ_217 /* TakeLast.swift */, - OBJ_218 /* TakeUntil.swift */, - OBJ_219 /* TakeWhile.swift */, - OBJ_220 /* Throttle.swift */, - OBJ_221 /* Timeout.swift */, - OBJ_222 /* Timer.swift */, - OBJ_223 /* ToArray.swift */, - OBJ_224 /* Using.swift */, - OBJ_225 /* VirtualTimeConverterType.swift */, - OBJ_226 /* VirtualTimeScheduler.swift */, - OBJ_227 /* Window.swift */, - OBJ_228 /* WithLatestFrom.swift */, - OBJ_229 /* Zip+Collection.swift */, - OBJ_230 /* Zip+arity.swift */, - OBJ_231 /* Zip.swift */, - ); - name = RxSwift; - path = .build/checkouts/RxSwift/Sources/RxSwift; - sourceTree = SOURCE_ROOT; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - OBJ_263 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 0; - files = ( - OBJ_264 /* CNIOAtomics.h in Headers */, - OBJ_265 /* cpp_magic.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - "Mini::MiniSwift" /* MiniSwift */ = { - isa = PBXNativeTarget; - buildConfigurationList = OBJ_285 /* Build configuration list for PBXNativeTarget "MiniSwift" */; - buildPhases = ( - F2F0AA1C232185C800BC7CD9 /* Swiftlint */, - OBJ_288 /* Sources */, - OBJ_307 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - OBJ_311 /* PBXTargetDependency */, - OBJ_313 /* PBXTargetDependency */, - OBJ_314 /* PBXTargetDependency */, - ); - name = MiniSwift; - productName = MiniSwift; - productReference = "Mini::MiniSwift::Product" /* MiniSwift.framework */; - productType = "com.apple.product-type.framework"; - }; - "Mini::MiniSwiftTests" /* MiniSwiftTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = OBJ_316 /* Build configuration list for PBXNativeTarget "MiniSwiftTests" */; - buildPhases = ( - OBJ_319 /* Sources */, - OBJ_326 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - OBJ_331 /* PBXTargetDependency */, - OBJ_332 /* PBXTargetDependency */, - OBJ_333 /* PBXTargetDependency */, - OBJ_334 /* PBXTargetDependency */, - ); - name = MiniSwiftTests; - productName = MiniSwiftTests; - productReference = "Mini::MiniSwiftTests::Product" /* MiniSwiftTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - "Mini::SwiftPMPackageDescription" /* MiniPackageDescription */ = { - isa = PBXNativeTarget; - buildConfigurationList = OBJ_274 /* Build configuration list for PBXNativeTarget "MiniPackageDescription" */; - buildPhases = ( - OBJ_277 /* Sources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = MiniPackageDescription; - productName = MiniPackageDescription; - productType = "com.apple.product-type.framework"; - }; - "RxSwift::RxSwift" /* RxSwift */ = { - isa = PBXNativeTarget; - buildConfigurationList = OBJ_344 /* Build configuration list for PBXNativeTarget "RxSwift" */; - buildPhases = ( - OBJ_347 /* Sources */, - OBJ_499 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = RxSwift; - productName = RxSwift; - productReference = "RxSwift::RxSwift::Product" /* RxSwift.framework */; - productType = "com.apple.product-type.framework"; - }; - "RxSwift::SwiftPMPackageDescription" /* RxSwiftPackageDescription */ = { - isa = PBXNativeTarget; - buildConfigurationList = OBJ_501 /* Build configuration list for PBXNativeTarget "RxSwiftPackageDescription" */; - buildPhases = ( - OBJ_504 /* Sources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = RxSwiftPackageDescription; - productName = RxSwiftPackageDescription; - productType = "com.apple.product-type.framework"; - }; - "swift-nio::CNIOAtomics" /* CNIOAtomics */ = { - isa = PBXNativeTarget; - buildConfigurationList = OBJ_258 /* Build configuration list for PBXNativeTarget "CNIOAtomics" */; - buildPhases = ( - OBJ_261 /* Sources */, - OBJ_263 /* Headers */, - OBJ_266 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = CNIOAtomics; - productName = CNIOAtomics; - productReference = "swift-nio::CNIOAtomics::Product" /* CNIOAtomics.framework */; - productType = "com.apple.product-type.framework"; - }; - "swift-nio::NIOConcurrencyHelpers" /* NIOConcurrencyHelpers */ = { - isa = PBXNativeTarget; - buildConfigurationList = OBJ_335 /* Build configuration list for PBXNativeTarget "NIOConcurrencyHelpers" */; - buildPhases = ( - OBJ_338 /* Sources */, - OBJ_341 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - OBJ_343 /* PBXTargetDependency */, - ); - name = NIOConcurrencyHelpers; - productName = NIOConcurrencyHelpers; - productReference = "swift-nio::NIOConcurrencyHelpers::Product" /* NIOConcurrencyHelpers.framework */; - productType = "com.apple.product-type.framework"; - }; - "swift-nio::SwiftPMPackageDescription" /* swift-nioPackageDescription */ = { - isa = PBXNativeTarget; - buildConfigurationList = OBJ_507 /* Build configuration list for PBXNativeTarget "swift-nioPackageDescription" */; - buildPhases = ( - OBJ_510 /* Sources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "swift-nioPackageDescription"; - productName = "swift-nioPackageDescription"; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - OBJ_1 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftMigration = 9999; - LastUpgradeCheck = 9999; - TargetAttributes = { - "swift-nio::CNIOAtomics" = { - LastSwiftMigration = 1030; - }; - }; - }; - buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "Mini" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - English, - en, - ); - mainGroup = OBJ_5; - productRefGroup = OBJ_234 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - "swift-nio::CNIOAtomics" /* CNIOAtomics */, - "Mini::Mini::ProductTarget" /* Mini */, - "Mini::SwiftPMPackageDescription" /* MiniPackageDescription */, - "Mini::MiniPackageTests::ProductTarget" /* MiniPackageTests */, - "Mini::MiniSwift" /* MiniSwift */, - "Mini::MiniSwiftTests" /* MiniSwiftTests */, - "swift-nio::NIOConcurrencyHelpers" /* NIOConcurrencyHelpers */, - "RxSwift::RxSwift" /* RxSwift */, - "RxSwift::SwiftPMPackageDescription" /* RxSwiftPackageDescription */, - "swift-nio::SwiftPMPackageDescription" /* swift-nioPackageDescription */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXShellScriptBuildPhase section */ - F2F0AA1C232185C800BC7CD9 /* Swiftlint */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = Swiftlint; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if which swiftlint >/dev/null; then\nswiftlint autocorrect\nswiftlint\nelse\necho \"warning: SwiftLint not installed, run: brew install swiftlint\"\nfi\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - OBJ_261 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 0; - files = ( - OBJ_262 /* c-atomics.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_277 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 0; - files = ( - OBJ_278 /* Package.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_288 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 0; - files = ( - OBJ_289 /* Action.swift in Sources */, - OBJ_290 /* ActionReducer.swift in Sources */, - OBJ_291 /* Dispatcher.swift in Sources */, - OBJ_292 /* Middleware.swift in Sources */, - OBJ_293 /* ReducerGroup.swift in Sources */, - OBJ_294 /* Service.swift in Sources */, - OBJ_295 /* StateType.swift in Sources */, - OBJ_296 /* Store.swift in Sources */, - OBJ_297 /* Task.swift in Sources */, - OBJ_298 /* Dictionary+Extensions.swift in Sources */, - OBJ_299 /* DispatchQueue+Extensions.swift in Sources */, - 12A3ADEF2328D3D100019F49 /* PrimitiveSequenceType+Extensions.swift in Sources */, - OBJ_300 /* String+Extensions.swift in Sources */, - OBJ_301 /* TestMiddleware.swift in Sources */, - OBJ_302 /* OrderedSet.swift in Sources */, - OBJ_303 /* PayloadAction.swift in Sources */, - OBJ_304 /* ObservableType+Extensions.swift in Sources */, - OBJ_305 /* LoggingService.swift in Sources */, - OBJ_306 /* SharedDictionary.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_319 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 0; - files = ( - OBJ_320 /* ChainTests.swift in Sources */, - OBJ_321 /* Dictionary+ExtensionsTests.swift in Sources */, - OBJ_322 /* DispatcherTests.swift in Sources */, - OBJ_323 /* ReducerTests.swift in Sources */, - OBJ_324 /* TaskTests.swift in Sources */, - OBJ_325 /* XCTestManifests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_338 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 0; - files = ( - OBJ_339 /* atomics.swift in Sources */, - OBJ_340 /* lock.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_347 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 0; - files = ( - OBJ_348 /* AddRef.swift in Sources */, - OBJ_349 /* Amb.swift in Sources */, - OBJ_350 /* AnonymousDisposable.swift in Sources */, - OBJ_351 /* AnonymousObserver.swift in Sources */, - OBJ_352 /* AnyObserver.swift in Sources */, - OBJ_353 /* AsMaybe.swift in Sources */, - OBJ_354 /* AsSingle.swift in Sources */, - OBJ_355 /* AsyncLock.swift in Sources */, - OBJ_356 /* AsyncSubject.swift in Sources */, - OBJ_357 /* AtomicInt.swift in Sources */, - OBJ_358 /* Bag+Rx.swift in Sources */, - OBJ_359 /* Bag.swift in Sources */, - OBJ_360 /* BehaviorSubject.swift in Sources */, - OBJ_361 /* BinaryDisposable.swift in Sources */, - OBJ_362 /* BooleanDisposable.swift in Sources */, - OBJ_363 /* Buffer.swift in Sources */, - OBJ_364 /* Cancelable.swift in Sources */, - OBJ_365 /* Catch.swift in Sources */, - OBJ_366 /* CombineLatest+Collection.swift in Sources */, - OBJ_367 /* CombineLatest+arity.swift in Sources */, - OBJ_368 /* CombineLatest.swift in Sources */, - OBJ_369 /* CompactMap.swift in Sources */, - OBJ_370 /* Completable+AndThen.swift in Sources */, - OBJ_371 /* Completable.swift in Sources */, - OBJ_372 /* CompositeDisposable.swift in Sources */, - OBJ_373 /* Concat.swift in Sources */, - OBJ_374 /* ConcurrentDispatchQueueScheduler.swift in Sources */, - OBJ_375 /* ConcurrentMainScheduler.swift in Sources */, - OBJ_376 /* ConnectableObservableType.swift in Sources */, - OBJ_377 /* Create.swift in Sources */, - OBJ_378 /* CurrentThreadScheduler.swift in Sources */, - OBJ_379 /* Date+Dispatch.swift in Sources */, - OBJ_380 /* Debounce.swift in Sources */, - OBJ_381 /* Debug.swift in Sources */, - OBJ_382 /* DefaultIfEmpty.swift in Sources */, - OBJ_383 /* Deferred.swift in Sources */, - OBJ_384 /* Delay.swift in Sources */, - OBJ_385 /* DelaySubscription.swift in Sources */, - OBJ_386 /* Dematerialize.swift in Sources */, - OBJ_387 /* Deprecated.swift in Sources */, - OBJ_388 /* DispatchQueue+Extensions.swift in Sources */, - OBJ_389 /* DispatchQueueConfiguration.swift in Sources */, - OBJ_390 /* Disposable.swift in Sources */, - OBJ_391 /* Disposables.swift in Sources */, - OBJ_392 /* DisposeBag.swift in Sources */, - OBJ_393 /* DisposeBase.swift in Sources */, - OBJ_394 /* DistinctUntilChanged.swift in Sources */, - OBJ_395 /* Do.swift in Sources */, - OBJ_396 /* ElementAt.swift in Sources */, - OBJ_397 /* Empty.swift in Sources */, - OBJ_398 /* Enumerated.swift in Sources */, - OBJ_399 /* Error.swift in Sources */, - OBJ_400 /* Errors.swift in Sources */, - OBJ_401 /* Event.swift in Sources */, - OBJ_402 /* Filter.swift in Sources */, - OBJ_403 /* First.swift in Sources */, - OBJ_404 /* Generate.swift in Sources */, - OBJ_405 /* GroupBy.swift in Sources */, - OBJ_406 /* GroupedObservable.swift in Sources */, - OBJ_407 /* HistoricalScheduler.swift in Sources */, - OBJ_408 /* HistoricalSchedulerTimeConverter.swift in Sources */, - OBJ_409 /* ImmediateSchedulerType.swift in Sources */, - OBJ_410 /* InfiniteSequence.swift in Sources */, - OBJ_411 /* InvocableScheduledItem.swift in Sources */, - OBJ_412 /* InvocableType.swift in Sources */, - OBJ_413 /* Just.swift in Sources */, - OBJ_414 /* Lock.swift in Sources */, - OBJ_415 /* LockOwnerType.swift in Sources */, - OBJ_416 /* MainScheduler.swift in Sources */, - OBJ_417 /* Map.swift in Sources */, - OBJ_418 /* Materialize.swift in Sources */, - OBJ_419 /* Maybe.swift in Sources */, - OBJ_420 /* Merge.swift in Sources */, - OBJ_421 /* Multicast.swift in Sources */, - OBJ_422 /* Never.swift in Sources */, - OBJ_423 /* NopDisposable.swift in Sources */, - OBJ_424 /* Observable.swift in Sources */, - OBJ_425 /* ObservableConvertibleType.swift in Sources */, - OBJ_426 /* ObservableType+Extensions.swift in Sources */, - OBJ_427 /* ObservableType+PrimitiveSequence.swift in Sources */, - OBJ_428 /* ObservableType.swift in Sources */, - OBJ_429 /* ObserveOn.swift in Sources */, - OBJ_430 /* ObserverBase.swift in Sources */, - OBJ_431 /* ObserverType.swift in Sources */, - OBJ_432 /* OperationQueueScheduler.swift in Sources */, - OBJ_433 /* Optional.swift in Sources */, - OBJ_434 /* Platform.Darwin.swift in Sources */, - OBJ_435 /* Platform.Linux.swift in Sources */, - OBJ_436 /* PrimitiveSequence+Zip+arity.swift in Sources */, - OBJ_437 /* PrimitiveSequence.swift in Sources */, - OBJ_438 /* PriorityQueue.swift in Sources */, - OBJ_439 /* Producer.swift in Sources */, - OBJ_440 /* PublishSubject.swift in Sources */, - OBJ_441 /* Queue.swift in Sources */, - OBJ_442 /* Range.swift in Sources */, - OBJ_443 /* Reactive.swift in Sources */, - OBJ_444 /* RecursiveLock.swift in Sources */, - OBJ_445 /* RecursiveScheduler.swift in Sources */, - OBJ_446 /* Reduce.swift in Sources */, - OBJ_447 /* RefCountDisposable.swift in Sources */, - OBJ_448 /* Repeat.swift in Sources */, - OBJ_449 /* ReplaySubject.swift in Sources */, - OBJ_450 /* RetryWhen.swift in Sources */, - OBJ_451 /* Rx.swift in Sources */, - OBJ_452 /* RxMutableBox.swift in Sources */, - OBJ_453 /* Sample.swift in Sources */, - OBJ_454 /* Scan.swift in Sources */, - OBJ_455 /* ScheduledDisposable.swift in Sources */, - OBJ_456 /* ScheduledItem.swift in Sources */, - OBJ_457 /* ScheduledItemType.swift in Sources */, - OBJ_458 /* SchedulerServices+Emulation.swift in Sources */, - OBJ_459 /* SchedulerType.swift in Sources */, - OBJ_460 /* Sequence.swift in Sources */, - OBJ_461 /* SerialDispatchQueueScheduler.swift in Sources */, - OBJ_462 /* SerialDisposable.swift in Sources */, - OBJ_463 /* ShareReplayScope.swift in Sources */, - OBJ_464 /* Single.swift in Sources */, - OBJ_465 /* SingleAssignmentDisposable.swift in Sources */, - OBJ_466 /* SingleAsync.swift in Sources */, - OBJ_467 /* Sink.swift in Sources */, - OBJ_468 /* Skip.swift in Sources */, - OBJ_469 /* SkipUntil.swift in Sources */, - OBJ_470 /* SkipWhile.swift in Sources */, - OBJ_471 /* StartWith.swift in Sources */, - OBJ_472 /* String+Rx.swift in Sources */, - OBJ_473 /* SubjectType.swift in Sources */, - OBJ_474 /* SubscribeOn.swift in Sources */, - OBJ_475 /* SubscriptionDisposable.swift in Sources */, - OBJ_476 /* SwiftSupport.swift in Sources */, - OBJ_477 /* Switch.swift in Sources */, - OBJ_478 /* SwitchIfEmpty.swift in Sources */, - OBJ_479 /* SynchronizedDisposeType.swift in Sources */, - OBJ_480 /* SynchronizedOnType.swift in Sources */, - OBJ_481 /* SynchronizedUnsubscribeType.swift in Sources */, - OBJ_482 /* TailRecursiveSink.swift in Sources */, - OBJ_483 /* Take.swift in Sources */, - OBJ_484 /* TakeLast.swift in Sources */, - OBJ_485 /* TakeUntil.swift in Sources */, - OBJ_486 /* TakeWhile.swift in Sources */, - OBJ_487 /* Throttle.swift in Sources */, - OBJ_488 /* Timeout.swift in Sources */, - OBJ_489 /* Timer.swift in Sources */, - OBJ_490 /* ToArray.swift in Sources */, - OBJ_491 /* Using.swift in Sources */, - OBJ_492 /* VirtualTimeConverterType.swift in Sources */, - OBJ_493 /* VirtualTimeScheduler.swift in Sources */, - OBJ_494 /* Window.swift in Sources */, - OBJ_495 /* WithLatestFrom.swift in Sources */, - OBJ_496 /* Zip+Collection.swift in Sources */, - OBJ_497 /* Zip+arity.swift in Sources */, - OBJ_498 /* Zip.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_504 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 0; - files = ( - OBJ_505 /* Package.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - OBJ_510 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 0; - files = ( - OBJ_511 /* Package.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - OBJ_271 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "Mini::MiniSwift" /* MiniSwift */; - targetProxy = F2F0AA1A2321256E00BC7CD9 /* PBXContainerItemProxy */; - }; - OBJ_283 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "Mini::MiniSwiftTests" /* MiniSwiftTests */; - targetProxy = F2F0AA1B2321256E00BC7CD9 /* PBXContainerItemProxy */; - }; - OBJ_311 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "swift-nio::NIOConcurrencyHelpers" /* NIOConcurrencyHelpers */; - targetProxy = F2F0AA122321256C00BC7CD9 /* PBXContainerItemProxy */; - }; - OBJ_313 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "swift-nio::CNIOAtomics" /* CNIOAtomics */; - targetProxy = F2F0AA142321256C00BC7CD9 /* PBXContainerItemProxy */; - }; - OBJ_314 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "RxSwift::RxSwift" /* RxSwift */; - targetProxy = F2F0AA152321256C00BC7CD9 /* PBXContainerItemProxy */; - }; - OBJ_331 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "Mini::MiniSwift" /* MiniSwift */; - targetProxy = F2F0AA162321256C00BC7CD9 /* PBXContainerItemProxy */; - }; - OBJ_332 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "swift-nio::NIOConcurrencyHelpers" /* NIOConcurrencyHelpers */; - targetProxy = F2F0AA172321256C00BC7CD9 /* PBXContainerItemProxy */; - }; - OBJ_333 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "swift-nio::CNIOAtomics" /* CNIOAtomics */; - targetProxy = F2F0AA182321256C00BC7CD9 /* PBXContainerItemProxy */; - }; - OBJ_334 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "RxSwift::RxSwift" /* RxSwift */; - targetProxy = F2F0AA192321256C00BC7CD9 /* PBXContainerItemProxy */; - }; - OBJ_343 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = "swift-nio::CNIOAtomics" /* CNIOAtomics */; - targetProxy = F2F0AA132321256C00BC7CD9 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - OBJ_259 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; - ENABLE_TESTABILITY = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/.build/checkouts/swift-nio/Sources/CNIOAtomics/include", - ); - INFOPLIST_FILE = Mini.xcodeproj/CNIOAtomics_Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; - MACOSX_DEPLOYMENT_TARGET = 10.10; - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = CNIOAtomics; - PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGET_NAME = CNIOAtomics; - TVOS_DEPLOYMENT_TARGET = 9.0; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Debug; - }; - OBJ_260 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - DEFINES_MODULE = YES; - ENABLE_TESTABILITY = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/.build/checkouts/swift-nio/Sources/CNIOAtomics/include", - ); - INFOPLIST_FILE = Mini.xcodeproj/CNIOAtomics_Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; - MACOSX_DEPLOYMENT_TARGET = 10.10; - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = CNIOAtomics; - PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_VERSION = 5.0; - TARGET_NAME = CNIOAtomics; - TVOS_DEPLOYMENT_TARGET = 9.0; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Release; - }; - OBJ_269 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - }; - name = Debug; - }; - OBJ_270 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - }; - name = Release; - }; - OBJ_275 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - LD = /usr/bin/true; - OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-10.3.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - OBJ_276 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - LD = /usr/bin/true; - OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-10.3.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk"; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - OBJ_281 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - }; - name = Debug; - }; - OBJ_282 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - }; - name = Release; - }; - OBJ_286 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ENABLE_TESTABILITY = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/.build/checkouts/swift-nio/Sources/CNIOAtomics/include", - ); - INFOPLIST_FILE = Mini.xcodeproj/MiniSwift_Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; - MACOSX_DEPLOYMENT_TARGET = 10.13; - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = MiniSwift; - PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_VERSION = 5.0; - TARGET_NAME = MiniSwift; - TVOS_DEPLOYMENT_TARGET = 11.0; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Debug; - }; - OBJ_287 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ENABLE_TESTABILITY = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/.build/checkouts/swift-nio/Sources/CNIOAtomics/include", - ); - INFOPLIST_FILE = Mini.xcodeproj/MiniSwift_Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; - MACOSX_DEPLOYMENT_TARGET = 10.13; - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = MiniSwift; - PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_VERSION = 5.0; - TARGET_NAME = MiniSwift; - TVOS_DEPLOYMENT_TARGET = 11.0; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Release; - }; - OBJ_3 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_OBJC_ARC = YES; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_NS_ASSERTIONS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "SWIFT_PACKAGE=1", - "DEBUG=1", - ); - MACOSX_DEPLOYMENT_TARGET = 10.10; - ONLY_ACTIVE_ARCH = YES; - OTHER_SWIFT_FLAGS = "-DXcode"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE DEBUG"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - USE_HEADERMAP = NO; - }; - name = Debug; - }; - OBJ_317 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/.build/checkouts/swift-nio/Sources/CNIOAtomics/include", - ); - INFOPLIST_FILE = Mini.xcodeproj/MiniSwiftTests_Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.13; - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_VERSION = 5.0; - TARGET_NAME = MiniSwiftTests; - TVOS_DEPLOYMENT_TARGET = 11.0; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Debug; - }; - OBJ_318 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/.build/checkouts/swift-nio/Sources/CNIOAtomics/include", - ); - INFOPLIST_FILE = Mini.xcodeproj/MiniSwiftTests_Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.13; - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_VERSION = 5.0; - TARGET_NAME = MiniSwiftTests; - TVOS_DEPLOYMENT_TARGET = 11.0; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Release; - }; - OBJ_336 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ENABLE_TESTABILITY = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/.build/checkouts/swift-nio/Sources/CNIOAtomics/include", - ); - INFOPLIST_FILE = Mini.xcodeproj/NIOConcurrencyHelpers_Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; - MACOSX_DEPLOYMENT_TARGET = 10.10; - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = NIOConcurrencyHelpers; - PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_VERSION = 5.0; - TARGET_NAME = NIOConcurrencyHelpers; - TVOS_DEPLOYMENT_TARGET = 9.0; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Debug; - }; - OBJ_337 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ENABLE_TESTABILITY = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/.build/checkouts/swift-nio/Sources/CNIOAtomics/include", - ); - INFOPLIST_FILE = Mini.xcodeproj/NIOConcurrencyHelpers_Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; - MACOSX_DEPLOYMENT_TARGET = 10.10; - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = NIOConcurrencyHelpers; - PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_VERSION = 5.0; - TARGET_NAME = NIOConcurrencyHelpers; - TVOS_DEPLOYMENT_TARGET = 9.0; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Release; - }; - OBJ_345 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ENABLE_TESTABILITY = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = Mini.xcodeproj/RxSwift_Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; - MACOSX_DEPLOYMENT_TARGET = 10.10; - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = RxSwift; - PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_VERSION = 5.0; - TARGET_NAME = RxSwift; - TVOS_DEPLOYMENT_TARGET = 9.0; - WATCHOS_DEPLOYMENT_TARGET = 3.0; - }; - name = Debug; - }; - OBJ_346 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ENABLE_TESTABILITY = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - HEADER_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = Mini.xcodeproj/RxSwift_Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; - MACOSX_DEPLOYMENT_TARGET = 10.10; - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = RxSwift; - PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_VERSION = 5.0; - TARGET_NAME = RxSwift; - TVOS_DEPLOYMENT_TARGET = 9.0; - WATCHOS_DEPLOYMENT_TARGET = 3.0; - }; - name = Release; - }; - OBJ_4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_OBJC_ARC = YES; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_OPTIMIZATION_LEVEL = s; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - "SWIFT_PACKAGE=1", - ); - MACOSX_DEPLOYMENT_TARGET = 10.10; - OTHER_SWIFT_FLAGS = "-DXcode"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - USE_HEADERMAP = NO; - }; - name = Release; - }; - OBJ_502 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - LD = /usr/bin/true; - OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-10.3.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - OBJ_503 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - LD = /usr/bin/true; - OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-10.3.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk"; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - OBJ_508 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - LD = /usr/bin/true; - OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-10.3.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - OBJ_509 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - LD = /usr/bin/true; - OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode-10.3.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk"; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - OBJ_2 /* Build configuration list for PBXProject "Mini" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_3 /* Debug */, - OBJ_4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - OBJ_258 /* Build configuration list for PBXNativeTarget "CNIOAtomics" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_259 /* Debug */, - OBJ_260 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - OBJ_268 /* Build configuration list for PBXAggregateTarget "Mini" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_269 /* Debug */, - OBJ_270 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - OBJ_274 /* Build configuration list for PBXNativeTarget "MiniPackageDescription" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_275 /* Debug */, - OBJ_276 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - OBJ_280 /* Build configuration list for PBXAggregateTarget "MiniPackageTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_281 /* Debug */, - OBJ_282 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - OBJ_285 /* Build configuration list for PBXNativeTarget "MiniSwift" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_286 /* Debug */, - OBJ_287 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - OBJ_316 /* Build configuration list for PBXNativeTarget "MiniSwiftTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_317 /* Debug */, - OBJ_318 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - OBJ_335 /* Build configuration list for PBXNativeTarget "NIOConcurrencyHelpers" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_336 /* Debug */, - OBJ_337 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - OBJ_344 /* Build configuration list for PBXNativeTarget "RxSwift" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_345 /* Debug */, - OBJ_346 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - OBJ_501 /* Build configuration list for PBXNativeTarget "RxSwiftPackageDescription" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_502 /* Debug */, - OBJ_503 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - OBJ_507 /* Build configuration list for PBXNativeTarget "swift-nioPackageDescription" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_508 /* Debug */, - OBJ_509 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = OBJ_1 /* Project object */; -} diff --git a/Mini.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Mini.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index fe1aa713..00000000 --- a/Mini.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/Mini.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Mini.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/Mini.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Mini.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Mini.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index a72dc2b4..00000000 --- a/Mini.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded - - - \ No newline at end of file diff --git a/Mini.xcodeproj/xcshareddata/xcschemes/Mini-Package.xcscheme b/Mini.xcodeproj/xcshareddata/xcschemes/Mini-Package.xcscheme deleted file mode 100644 index 346db3e4..00000000 --- a/Mini.xcodeproj/xcshareddata/xcschemes/Mini-Package.xcscheme +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Mintfile b/Mintfile new file mode 100644 index 00000000..a0fbb958 --- /dev/null +++ b/Mintfile @@ -0,0 +1 @@ +realm/SwiftLint@0.58.2 \ No newline at end of file diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index 02be3c68..00000000 --- a/Package.resolved +++ /dev/null @@ -1,25 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "RxSwift", - "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", - "state": { - "branch": null, - "revision": "b3e888b4972d9bc76495dd74d30a8c7fad4b9395", - "version": "5.0.1" - } - }, - { - "package": "swift-nio", - "repositoryURL": "https://github.com/apple/swift-nio.git", - "state": { - "branch": null, - "revision": "b07efecaea7359054b8a85d357f05c8152b7716c", - "version": "2.6.0" - } - } - ] - }, - "version": 1 -} diff --git a/Package.swift b/Package.swift index 2ca6178c..df0f448f 100644 --- a/Package.swift +++ b/Package.swift @@ -1,35 +1,35 @@ -// swift-tools-version:5.0 -// The swift-tools-version declares the minimum version of Swift required to build this package. - +// swift-tools-version:5.5 import PackageDescription let package = Package( name: "Mini", platforms: [ - .iOS(.v11), - .macOS(.v10_13), - .tvOS(.v11) + .iOS("14.1"), + .macOS(.v11), + .tvOS(.v13), ], products: [ - // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( name: "Mini", - targets: ["MiniSwift"]), - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "5.0.0"), - .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0") + targets: ["Mini"] + ) ], + dependencies: [], targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( - name: "MiniSwift", - dependencies: ["RxSwift", "NIOConcurrencyHelpers"]), + name: "Mini", + dependencies: [], + path: "Sources", + exclude: [ + "../_config.yml", + "../Mintfile", + "../Rakefile", + ] + ), .testTarget( name: "MiniSwiftTests", - dependencies: ["MiniSwift", "RxSwift"]), - ], - swiftLanguageVersions: [.v4, .v4_2, .v5] + dependencies: ["Mini"], + path: "Tests" + ), + ] ) diff --git a/README.md b/README.md index 0fdc35ba..01c2fac4 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -# Mini-Swift +# MasMini-Swift The minimal expression of a Flux architecture in Swift. Mini is built with be a first class citizen in Swift applications: **macOS, iOS and tvOS** applications. With Mini, you can create a thread-safe application with a predictable unidirectional data flow, focusing on what really matters: build awesome applications. -[![Release Version](https://img.shields.io/github/release/bq/mini-swift.svg)](https://github.com/bq/mini-swift/releases) -[![Release Date](https://img.shields.io/github/release-date/bq/mini-swift.svg)](https://github.com/bq/mini-swift/releases) -[![Pod](https://img.shields.io/cocoapods/v/Mini-Swift.svg?style=flat)](https://cocoapods.org/pods/Mini-Swift) -[![Platform](https://img.shields.io/cocoapods/p/Mini-Swift.svg?style=flat)](https://cocoapods.org/pods/Mini-Swift) -[![GitHub](https://img.shields.io/github/license/bq/mini-swift.svg)](https://github.com/bq/mini-swift/blob/master/LICENSE) +[![Release Version](https://img.shields.io/github/release/masmovil/masmini-swift.svg)](https://github.com/masmovil/masmini-swift/releases) +[![Release Date](https://img.shields.io/github/release-date/masmovil/masmini-swift.svg)](https://github.com/masmovil/masmini-swift/releases) +[![Pod](https://img.shields.io/cocoapods/v/MasMini-Swift.svg?style=flat)](https://cocoapods.org/pods/MasMini-Swift) +[![Platform](https://img.shields.io/cocoapods/p/MasMini-Swift.svg?style=flat)](https://cocoapods.org/pods/MasMini-Swift) +[![GitHub](https://img.shields.io/github/license/masmovil/masmini-swift.svg)](https://github.com/masmovil/masmini-swift/blob/master/LICENSE) -[![Build Status](https://travis-ci.org/bq/mini-swift.svg?branch=5.0)](https://travis-ci.org/bq/mini-swift) -[![codecov](https://codecov.io/gh/bq/mini-swift/branch/master/graph/badge.svg)](https://codecov.io/gh/bq/mini-swift) +[![Build Status](https://github.com/masmovil/masmini-swift/actions/workflows/build-and-tests.yml/badge.svg)](https://github.com/masmovil/masmini-swift/actions/workflows/build-and-tests.yml) +[![codecov](https://codecov.io/gh/masmovil/masmini-swift/branch/master/graph/badge.svg)](https://codecov.io/gh/masmovil/masmini-swift) ## Requirements @@ -23,47 +23,30 @@ With Mini, you can create a thread-safe application with a predictable unidirect ## Installation -### [Swift Package Manager](https://github.com/apple/swift-package-manager) +### [Carthage](https://github.com/Carthage/Carthage) -- Create a Package.swift file. - -```swift -// swift-tools-version:5.0 - -import PackageDescription - -let package = Package( - name: "MiniSwiftProject", - dependencies: [ - .package(url: "https://github.com/bq/mini-swift.git"), - ], - targets: [ - .target(name: "MiniSwiftProject", dependencies: ["Mini"]) - ] -) +- Add this to you `Cartfile`: ``` -``` -$ swift build +github "masmovil/masmini-swift" ``` ### [Cocoapods](https://cocoapods.org/) - Add this to you `Podfile`: - ``` -pod "Mini-Swift" +pod "MasMini-Swift" ``` - We also offer two subpecs for logging and testing: ``` -pod "Mini-Swift/Log" -pod "Mini-Swift/Test" +pod "MasMini-Swift/Log" +pod "MasMini-Swift/Test" ``` ## Usage -- **MiniSwift** is a library which aims the ease of the usage of a Flux oriented architecture for Swift applications. Due its Flux-based nature, it heavily relies on some of its concepts like **Store**, **State**, **Dispatcher**, **Action**, **Task** and **Reducer**. +- **MasMiniSwift** is a library which aims the ease of the usage of a Flux oriented architecture for Swift applications. Due its Flux-based nature, it heavily relies on some of its concepts like **Store**, **State**, **Dispatcher**, **Action**, **Task** and **Reducer**. ![Architecture](https://i.imgur.com/DioR3i0.png) diff --git a/Rakefile b/Rakefile index 668546fa..0728a5a7 100644 --- a/Rakefile +++ b/Rakefile @@ -3,27 +3,14 @@ task default: %w[setup] task(:setup) do - - raise '`brew` is required. Please install brew. https://brew.sh/' unless system('which brew') - - puts('➡️ Bundle') - sh('brew bundle') - sh('bundle install') - - puts('➡️ Overcommit') - sh('bundle exec overcommit --install') - sh('bundle exec overcommit --sign') - sh('bundle exec overcommit --sign pre-commit') - sh('bundle exec overcommit --sign post-checkout') - - puts('➡️ SPM Resolve Dependencies') - sh('swift package resolve') + puts('➡️ Mint 🍃') + sh('mint bootstrap') end -task(:build) do - sh('swift build') +task(:lint) do + sh('mint run swiftlint --fix --format') end task(:test) do - sh('swift test') + sh('swift test --enable-code-coverage --disable-swift-testing') end diff --git a/Sources/ActionReducer.swift b/Sources/ActionReducer.swift new file mode 100644 index 00000000..f63263d1 --- /dev/null +++ b/Sources/ActionReducer.swift @@ -0,0 +1,28 @@ +import Combine +import Foundation + +public class Reducer: Cancellable { + public let action: A.Type + public let dispatcher: Dispatcher + public let reducer: (A) -> Void + + 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.cancellable = build() + } + + private func build() -> Cancellable { + let cancelable = dispatcher.subscribe(tag: action.tag) { + self.reducer($0) + } + return cancelable + } + + public func cancel() { + cancellable.cancel() + } +} diff --git a/Sources/MiniSwift/Action.swift b/Sources/Actions/Action.swift similarity index 50% rename from Sources/MiniSwift/Action.swift rename to Sources/Actions/Action.swift index 685483ac..b42b5928 100644 --- a/Sources/MiniSwift/Action.swift +++ b/Sources/Actions/Action.swift @@ -1,35 +1,18 @@ -/* -Copyright [2019] [BQ] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - import Foundation public protocol Action { - func isEqual(to other: Action) -> Bool } extension Action { /// String used as tag of the given Action based on his name. /// - Returns: The name of the action as a String. public var innerTag: String { - return String(describing: type(of: self)) + String(describing: type(of: self)) } /** Static method to retrieve the name of the action as a tag.action. - + Calling this method in a static way return the Action name .Type cause it's not an instance.Action For this reason the String is split in two separated by a dot and returning the first part. */ diff --git a/Sources/Actions/AttributedAction.swift b/Sources/Actions/AttributedAction.swift new file mode 100644 index 00000000..5917417b --- /dev/null +++ b/Sources/Actions/AttributedAction.swift @@ -0,0 +1,38 @@ +import Foundation + +public protocol AttributedAction: Action { + associatedtype Attribute: Equatable + var attribute: Attribute { get } + + init(attribute: Attribute) +} + +public protocol AttributedCompletableAction: Action & PayloadAction & AttributedAction { + init(task: Task, attribute: Attribute) +} + +extension AttributedCompletableAction { + public init(task: Task) { + fatalError("You must use init(task:attribute:)") + } + + public init(attribute: Attribute) { + fatalError("You must use init(task:attribute:)") + } +} + +public protocol AttributedEmptyAction: Action & PayloadAction & AttributedAction { + associatedtype TaskPayload = None + + init(task: EmptyTask, attribute: Attribute) +} + +extension AttributedEmptyAction { + public init(task: EmptyTask) { + fatalError("You must use init(task:attribute:)") + } + + public init(attribute: Attribute) { + fatalError("You must use init(task:attribute:)") + } +} diff --git a/Sources/Actions/KeyedAction.swift b/Sources/Actions/KeyedAction.swift new file mode 100644 index 00000000..159dab75 --- /dev/null +++ b/Sources/Actions/KeyedAction.swift @@ -0,0 +1,20 @@ +import Foundation + +public protocol KeyedPayloadAction { + associatedtype TaskPayload: Equatable + associatedtype TaskError: Error & Equatable + 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/Actions/PayloadAction.swift b/Sources/Actions/PayloadAction.swift new file mode 100644 index 00000000..061956b0 --- /dev/null +++ b/Sources/Actions/PayloadAction.swift @@ -0,0 +1,18 @@ +import Foundation + +public protocol PayloadAction { + associatedtype TaskPayload: Equatable + associatedtype TaskError: Error & Equatable + + var task: Task { get } + + init(task: Task) +} + +public protocol CompletableAction: Action & PayloadAction { } + +public protocol EmptyAction: Action & PayloadAction { + associatedtype TaskPayload = None + + init(task: EmptyTask) +} diff --git a/Sources/Chain.swift b/Sources/Chain.swift new file mode 100644 index 00000000..9db1073c --- /dev/null +++ b/Sources/Chain.swift @@ -0,0 +1,38 @@ +import Foundation + +public typealias Next = (Action) -> Action + +public protocol Chain { + var proceed: Next { get } +} + +public final class ForwardingChain: Chain { + private let next: Next + + public var proceed: Next { + { self.next($0) } + } + + public init(next: @escaping Next) { + self.next = next + } +} + +public final class RootChain: Chain { + private let map: SubscriptionMap + + public var proceed: Next { + { action in + if let set = self.map[action.innerTag] { + set?.forEach { sub in + sub.on(action) + } + } + return action + } + } + + public init(map: SubscriptionMap) { + self.map = map + } +} diff --git a/Sources/Dispatcher.swift b/Sources/Dispatcher.swift new file mode 100644 index 00000000..108f7ea5 --- /dev/null +++ b/Sources/Dispatcher.swift @@ -0,0 +1,131 @@ +import Foundation + +public typealias SubscriptionMap = SharedDictionary?> + +public final class Dispatcher { + public var subscriptionCount: Int { + subscriptionMap.innerDictionary.mapValues { set -> Int in + guard let setValue = set else { return 0 } + return setValue.count + } + .reduce(0) { $0 + $1.value } + } + + public static let defaultPriority = 100 + + private let internalQueue = DispatchQueue(label: "MiniSwift", qos: .userInitiated) + private var subscriptionMap = SubscriptionMap() + private var interceptors = [Interceptor]() + private let root: RootChain + private var chain: Chain + private var dispatching = false + private var subscriptionCounter = 0 + + public init() { + root = RootChain(map: subscriptionMap) + chain = root + } + + public func register(interceptor: Interceptor) { + internalQueue.sync { + self.interceptors.append(interceptor) + } + } + + public func unregister(interceptor: Interceptor) { + internalQueue.sync { + if let index = self.interceptors.firstIndex(where: { interceptor.id == $0.id }) { + self.interceptors.remove(at: index) + } + } + } + + public func subscribe(priority: Int, tag: String, completion: @escaping (Action) -> Void) -> DispatcherSubscription { + let subscription = DispatcherSubscription( + dispatcher: self, + id: getNewSubscriptionId(), + priority: priority, + tag: tag, + completion: completion) + return registerInternal(subscription: subscription) + } + + public func registerInternal(subscription: DispatcherSubscription) -> DispatcherSubscription { + internalQueue.sync { + if let map = subscriptionMap[subscription.tag, ifNotExistsSave: OrderedSet()] { + map.insert(subscription) + } + } + return subscription + } + + public func unregisterInternal(subscription: DispatcherSubscription) { + internalQueue.sync { + var removed = false + if let set = subscriptionMap[subscription.tag] as? OrderedSet { + removed = set.remove(subscription) + } else { + removed = true + } + assert(removed, "Failed to remove DispatcherSubscription, multiple dispose calls?") + } + } + + public func subscribe(completion: @escaping (T) -> Void) -> DispatcherSubscription { + subscribe(tag: T.tag) { (action: T) in + completion(action) + } + } + + public func subscribe(tag: String, completion: @escaping (T) -> Void) -> DispatcherSubscription { + subscribe(tag: tag) { object in + if let action = object as? T { + completion(action) + } else { + fatalError("Casting to \(tag) failed") + } + } + } + + public func subscribe(tag: String, completion: @escaping (Action) -> Void) -> DispatcherSubscription { + subscribe(priority: Dispatcher.defaultPriority, tag: tag, completion: completion) + } + + public func dispatch(_ action: Action) { + DispatchQueue.main.async { + self.dispatchOnQueue(action) + } + } + + internal func stateWasReplayed(state: any State) { + internalQueue.async { [weak self] in + guard let self = self else { return } + self.interceptors.forEach { + $0.stateWasReplayed(state: state) + } + } + } + + private func dispatchOnQueue(_ action: Action) { + internalQueue.sync { + defer { dispatching = false } + if dispatching { + preconditionFailure("Already dispatching") + } + dispatching = true + _ = chain.proceed(action) + internalQueue.async { [weak self] in + guard let self = self else { return } + self.interceptors.forEach { + $0.perform(action, self.chain) + } + } + } + } + + private func getNewSubscriptionId() -> Int { + let previous = subscriptionCounter + subscriptionCounter += 1 + return previous + } +} diff --git a/Sources/DispatcherSubscription.swift b/Sources/DispatcherSubscription.swift new file mode 100644 index 00000000..39f114a2 --- /dev/null +++ b/Sources/DispatcherSubscription.swift @@ -0,0 +1,49 @@ +public final class DispatcherSubscription: Comparable, Equatable, Hashable { + internal let dispatcher: Dispatcher + + public let id: Int + private let priority: Int + private let completion: (Action) -> Void + + public let tag: String + + public init (dispatcher: Dispatcher, + id: Int, + priority: Int, + tag: String, + completion: @escaping (Action) -> Void) { + self.dispatcher = dispatcher + self.id = id + self.priority = priority + self.tag = tag + self.completion = completion + } + + public func on(_ action: Action) { + completion(action) + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + public static func == (lhs: DispatcherSubscription, rhs: DispatcherSubscription) -> Bool { + lhs.id == rhs.id + } + + public static func > (lhs: DispatcherSubscription, rhs: DispatcherSubscription) -> Bool { + lhs.priority > rhs.priority + } + + public static func < (lhs: DispatcherSubscription, rhs: DispatcherSubscription) -> Bool { + lhs.priority < rhs.priority + } + + public static func >= (lhs: DispatcherSubscription, rhs: DispatcherSubscription) -> Bool { + lhs.priority >= rhs.priority + } + + public static func <= (lhs: DispatcherSubscription, rhs: DispatcherSubscription) -> Bool { + lhs.priority <= rhs.priority + } +} diff --git a/Sources/Extensions/DictionaryExtensions.swift b/Sources/Extensions/DictionaryExtensions.swift new file mode 100644 index 00000000..0efd6fcd --- /dev/null +++ b/Sources/Extensions/DictionaryExtensions.swift @@ -0,0 +1,20 @@ +import Foundation + +public extension Dictionary { + /// Returns the value for the given key. If the key is not found in the map, calls the [defaultValue] function, + /// puts its result into the map under the given key and returns it. + mutating func safeValue(_ key: Key, defaultValue: () -> Value) -> Value { + self[key, ifNotExistsSave: defaultValue()] + } + + subscript (_ key: Key, ifNotExistsSave value: @autoclosure () -> Value) -> Value { + mutating get { + if let value = self[key] { + return value + } + let value = value() + self[key] = value + return value + } + } +} diff --git a/Sources/Interceptor.swift b/Sources/Interceptor.swift new file mode 100644 index 00000000..ed0d08f7 --- /dev/null +++ b/Sources/Interceptor.swift @@ -0,0 +1,9 @@ +import Foundation + +public typealias InterceptorChain = (Action, Chain) -> Void + +public protocol Interceptor { + var id: UUID { get } + var perform: InterceptorChain { get } + func stateWasReplayed(state: any State) +} diff --git a/Sources/Mini.h b/Sources/Mini.h new file mode 100644 index 00000000..d3b4824b --- /dev/null +++ b/Sources/Mini.h @@ -0,0 +1,19 @@ +// +// Mini.h +// Mini +// +// Created by Sebastián Varela Basconi on 27/03/2019. +// Copyright © 2019 BQ. All rights reserved. +// + +#import + +//! Project version number for Mini. +FOUNDATION_EXPORT double MiniVersionNumber; + +//! Project version string for Mini. +FOUNDATION_EXPORT const unsigned char MiniVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Sources/Mini.swift b/Sources/Mini.swift new file mode 100644 index 00000000..1c5d431e --- /dev/null +++ b/Sources/Mini.swift @@ -0,0 +1,9 @@ +import Foundation + +#if canImport(AppKit) +@_exported import AppKit +#endif + +#if canImport(UIKit) +@_exported import UIKit +#endif diff --git a/Sources/MiniSwift/ActionReducer.swift b/Sources/MiniSwift/ActionReducer.swift deleted file mode 100644 index c8250fc6..00000000 --- a/Sources/MiniSwift/ActionReducer.swift +++ /dev/null @@ -1,44 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation -import RxSwift - -public class Reducer: Disposable { - public let action: A.Type - public let dispatcher: Dispatcher - public let reducer: (A) -> Void - - private var disposable: Disposable! - - 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() - } - - private func build() -> Disposable { - let disposable = dispatcher.subscribe(tag: action.tag) { - self.reducer($0) - } - return disposable - } - - public func dispose() { - disposable.dispose() - } -} diff --git a/Sources/MiniSwift/Dispatcher.swift b/Sources/MiniSwift/Dispatcher.swift deleted file mode 100644 index 56fa1118..00000000 --- a/Sources/MiniSwift/Dispatcher.swift +++ /dev/null @@ -1,233 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation -import RxSwift -import NIOConcurrencyHelpers - -public typealias SubscriptionMap = SharedDictionary?> - -final public class Dispatcher { - - public struct DispatchMode { - // swiftlint:disable:next type_name nesting - public enum UI { - case sync, async - } - } - - public var subscriptionCount: Int { - return subscriptionMap.innerDictionary.mapValues { set -> Int in - guard let setValue = set else { return 0 } - return setValue.count - } - .reduce(0, { $0 + $1.value }) - } - - public static let defaultPriority = 100 - - private let internalQueue = DispatchQueue(label: "MiniSwift", qos: .userInitiated) - private var subscriptionMap = SubscriptionMap() - private var middleware = [Middleware]() - private var service = [Service]() - private let root: RootChain - private var chain: Chain - private var dispatching: Bool = false - private var subscriptionCounter: Atomic = Atomic(value: 0) - - public init() { - root = RootChain(map: subscriptionMap) - chain = root - } - - private func build() -> Chain { - return middleware.reduce(root, { (chain: Chain, middleware: Middleware) -> Chain in - return ForwardingChain { action in - middleware.perform(action, chain) - } - }) - } - - public func add(middleware: Middleware) { - internalQueue.sync { - self.middleware.append(middleware) - self.chain = build() - } - } - - public func remove(middleware: Middleware) { - internalQueue.sync { - if let index = self.middleware.firstIndex(where: { middleware.id == $0.id }) { - self.middleware.remove(at: index) - } - chain = build() - } - } - - public func register(service: Service) { - internalQueue.sync { - self.service.append(service) - } - } - - public func unregister(service: Service) { - internalQueue.sync { - if let index = self.service.firstIndex(where: { service.id == $0.id }) { - self.service.remove(at: index) - } - } - } - - public func subscribe(priority: Int, tag: String, completion: @escaping (Action) -> Void) -> DispatcherSubscription { - let subscription = DispatcherSubscription( - dispatcher: self, - id: getNewSubscriptionId(), - priority: priority, - tag: tag, - completion: completion) - return registerInternal(subscription: subscription) - } - - public func registerInternal(subscription: DispatcherSubscription) -> DispatcherSubscription { - internalQueue.sync { - if let map = subscriptionMap[subscription.tag, orPut: OrderedSet()] { - map.insert(subscription) - } - } - return subscription - } - - public func unregisterInternal(subscription: DispatcherSubscription) { - internalQueue.sync { - var removed = false - if let set = subscriptionMap[subscription.tag] as? OrderedSet { - removed = set.remove(subscription) - } else { - removed = true - } - assert(removed, "Failed to remove DispatcherSubscription, multiple dispose calls?") - } - } - - public func subscribe(completion: @escaping (T) -> Void) -> DispatcherSubscription { - return subscribe(tag: T.tag, completion: { (action: T) -> Void in - completion(action) - }) - } - - public func subscribe(tag: String, completion: @escaping (T) -> Void) -> DispatcherSubscription { - return subscribe(tag: tag, completion: { object in - if let action = object as? T { - completion(action) - } else { - fatalError("Casting to \(tag) failed") - } - }) - } - - public func subscribe(tag: String, completion: @escaping (Action) -> Void) -> DispatcherSubscription { - return subscribe(priority: Dispatcher.defaultPriority, tag: tag, completion: completion) - } - - public func dispatch(_ action: Action, mode: Dispatcher.DispatchMode.UI) { - switch mode { - case .sync: - if DispatchQueue.isMain { - self.dispatch(action) - } else { - DispatchQueue.main.sync { - self.dispatch(action) - } - } - case .async: - DispatchQueue.main.async { - self.dispatch(action) - } - } - } - - private func dispatch(_ action: Action) { - assert(DispatchQueue.isMain) - internalQueue.sync { - defer { dispatching = false } - if dispatching { - preconditionFailure("Already dispatching") - } - dispatching = true - _ = chain.proceed(action) - internalQueue.async { [weak self] in - guard let self = self else { return } - self.service.forEach { - $0.perform(action, self.chain) - } - } - } - } - - private func getNewSubscriptionId() -> Int { - return subscriptionCounter.add(1) - } -} - -public final class DispatcherSubscription: Comparable, Disposable { - - private let dispatcher: Dispatcher - public let id: Int - private let priority: Int - private let completion: (Action) -> Void - - public let tag: String - - public init (dispatcher: Dispatcher, - id: Int, - priority: Int, - tag: String, - completion: @escaping (Action) -> Void) { - self.dispatcher = dispatcher - self.id = id - self.priority = priority - self.tag = tag - self.completion = completion - } - - public func dispose() { - dispatcher.unregisterInternal(subscription: self) - } - - public func on(_ action: Action) { - completion(action) - } - - public static func == (lhs: DispatcherSubscription, rhs: DispatcherSubscription) -> Bool { - return lhs.id == rhs.id - } - - public static func > (lhs: DispatcherSubscription, rhs: DispatcherSubscription) -> Bool { - return lhs.priority > rhs.priority - } - - public static func < (lhs: DispatcherSubscription, rhs: DispatcherSubscription) -> Bool { - return lhs.priority < rhs.priority - } - - public static func >= (lhs: DispatcherSubscription, rhs: DispatcherSubscription) -> Bool { - return lhs.priority >= rhs.priority - } - - public static func <= (lhs: DispatcherSubscription, rhs: DispatcherSubscription) -> Bool { - return lhs.priority <= rhs.priority - } -} diff --git a/Sources/MiniSwift/LoggingService/LoggingService.swift b/Sources/MiniSwift/LoggingService/LoggingService.swift deleted file mode 100644 index 82ec84e1..00000000 --- a/Sources/MiniSwift/LoggingService/LoggingService.swift +++ /dev/null @@ -1,30 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -public class LoggingService: Service { - - public var id: UUID = UUID() - - public var perform: ServiceChain { - return { action, _ -> Void in - NSLog(String(dumping: action)) - } - } - - public init() { } -} diff --git a/Sources/MiniSwift/Middleware.swift b/Sources/MiniSwift/Middleware.swift deleted file mode 100644 index e862ca40..00000000 --- a/Sources/MiniSwift/Middleware.swift +++ /dev/null @@ -1,64 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -public typealias MiddlewareChain = (Action, Chain) -> Action -public typealias Next = (Action) -> Action - -public protocol Chain { - var proceed: Next { get } -} - -public protocol Middleware { - var id: UUID { get } - var perform: MiddlewareChain { get } -} - -public final class ForwardingChain: Chain { - - private let next: Next - - public var proceed: Next { - return { action in - return self.next(action) - } - } - - public init(next: @escaping Next) { - self.next = next - } -} - -public final class RootChain: Chain { - - private let map: SubscriptionMap - - public var proceed: Next { - return { action in - if let set = self.map[action.innerTag] { - set?.forEach { sub in - sub.on(action) - } - } - return action - } - } - - public init(map: SubscriptionMap) { - self.map = map - } -} diff --git a/Sources/MiniSwift/ReducerGroup.swift b/Sources/MiniSwift/ReducerGroup.swift deleted file mode 100644 index 53926243..00000000 --- a/Sources/MiniSwift/ReducerGroup.swift +++ /dev/null @@ -1,36 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation -import RxSwift - -public protocol Group: Disposable { - var disposeBag: CompositeDisposable { get } -} - -public class ReducerGroup: Group { - - public let disposeBag = CompositeDisposable() - - public init(_ builder: () -> [Disposable]) { - let disposable = builder() - disposable.forEach { _ = disposeBag.insert($0) } - } - - public func dispose() { - disposeBag.dispose() - } -} diff --git a/Sources/MiniSwift/Service.swift b/Sources/MiniSwift/Service.swift deleted file mode 100644 index eaaf0144..00000000 --- a/Sources/MiniSwift/Service.swift +++ /dev/null @@ -1,24 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -public typealias ServiceChain = (Action, Chain) -> Void - -public protocol Service { - var id: UUID { get } - var perform: ServiceChain { get } -} diff --git a/Sources/MiniSwift/StateType.swift b/Sources/MiniSwift/StateType.swift deleted file mode 100644 index 8654155c..00000000 --- a/Sources/MiniSwift/StateType.swift +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -public protocol StateType { - func isEqual(to other: StateType) -> Bool -} - -public extension StateType where Self: Equatable { - func isEqual(to other: StateType) -> Bool { - guard let other = other as? Self else { return false } - return self == other - } -} diff --git a/Sources/MiniSwift/Store.swift b/Sources/MiniSwift/Store.swift deleted file mode 100644 index 2faac953..00000000 --- a/Sources/MiniSwift/Store.swift +++ /dev/null @@ -1,119 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation -import RxSwift - -public protocol StoreType { - associatedtype State: StateType - associatedtype StoreController: Disposable - - var state: State { get set } - var dispatcher: Dispatcher { get } - var reducerGroup: ReducerGroup { get } -} - -extension StoreType { - /** - Property responsible of reduce the `State` given a certain `Action` being triggered. - ``` - public var reducerGroup: ReducerGroup { - ReducerGroup {[ - Reducer(of: SomeAction.self, on: self.dispatcher) { (action: SomeAction) - self.state = myCoolNewState - }, - Reducer(of: OtherAction.self, on: self.dispatcher) { (action: OtherAction) - // Needed work - self.state = myAnotherState - } - } - ]} - ``` - - Note : The property has a default implementation which complies with the @_functionBuilder's current limitations, where no empty blocks can be produced in this iteration. - */ - public var reducerGroup: ReducerGroup { - return ReducerGroup { - [] - } - } -} - -public class Store: ObservableType, StoreType { - - public typealias Element = State - - public typealias State = State - public typealias StoreController = StoreController - - public typealias ObjectWillChangePublisher = BehaviorSubject - - public var objectWillChange: ObjectWillChangePublisher - - private var _initialState: State - public let dispatcher: Dispatcher - public var storeController: StoreController - - private let queue = DispatchQueue(label: "atomic state") - - private var _state: State - - public var state: State { - get { - return _state - } - set { - queue.sync { - if !newValue.isEqual(to: _state) { - _state = newValue - objectWillChange.onNext(state) - } - } - } - } - - public var initialState: State { - return _initialState - } - - public init(_ state: State, - dispatcher: Dispatcher, - storeController: StoreController) { - self._initialState = state - self._state = state - self.dispatcher = dispatcher - self.objectWillChange = ObjectWillChangePublisher(value: state) - self.storeController = storeController - self.state = _initialState - } - - public var reducerGroup: ReducerGroup { - return ReducerGroup { - [] - } - } - - public func replayOnce() { - objectWillChange.onNext(state) - } - - public func reset() { - state = initialState - } - - public func subscribe(_ observer: Observer) -> Disposable where Observer.Element == Store.Element { - return objectWillChange.subscribe(observer) - } -} diff --git a/Sources/MiniSwift/Task.swift b/Sources/MiniSwift/Task.swift deleted file mode 100644 index 1a9d8ba9..00000000 --- a/Sources/MiniSwift/Task.swift +++ /dev/null @@ -1,113 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -public typealias Task = TypedTask -public typealias KeyedTask = [K: Task] - -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 - case .long: return 180 - case .custom(let value): return value - } - } - } - - public let status: Status - public let started: Date - public let expiration: Expiration - public let data: T? - public let progress: Decimal? - public let error: Error? - - public required init(status: Status = .idle, - started: Date = Date(), - expiration: Expiration = .immediately, - data: T? = nil, - progress: Decimal? = nil, - error: Error? = nil) { - self.status = status - self.started = started - self.expiration = expiration - self.data = data - self.progress = progress - self.error = error - } - - public var isRunning: Bool { - return status == .running - } - - public var isRecentlySucceeded: Bool { - return status == .success && started.timeIntervalSinceNow + expiration.value >= 0 - } - - public var isTerminal: Bool { - return status == .success || status == .failure - } - - public var isSuccessful: Bool { - return status == .success - } - - public var isFailure: Bool { - return status == .failure - } - - public static func requestRunning() -> Task { - return Task(status: .running) - } - - public static func requestSuccess(_ expiration: Task.Expiration = .immediately) -> Task { - return Task(status: .success, expiration: expiration) - } - - public static func requestFailure(_ error: Error) -> Task { - return Task(status: .failure, error: error) - } - - // MARK: - CustomDebugStringConvertible - public var debugDescription: String { - return """ - 🚀 Task: status: \(status), started: \(started), - data: \(String(describing: data)), progress: \(String(describing: progress)) error: \(String(describing: error)) - """ - } -} - -public func == (lhs: TypedTask, rhs: TypedTask) -> Bool { - return lhs.status == rhs.status && - lhs.started == rhs.started && - lhs.progress == rhs.progress -} diff --git a/Sources/MiniSwift/TestMiddleware/TestMiddleware.swift b/Sources/MiniSwift/TestMiddleware/TestMiddleware.swift deleted file mode 100644 index fbe99fe9..00000000 --- a/Sources/MiniSwift/TestMiddleware/TestMiddleware.swift +++ /dev/null @@ -1,64 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -/// Action for testing purposes. -public class TestOnlyAction: Action { - public func isEqual(to other: Action) -> Bool { - return true - } -} - -/// Interceptor class for testing purposes which mute all the received actions. -public class TestMiddleware: Middleware { - - public var id: UUID = UUID() - - private var interceptedActions: [Action] = [] - - public var perform: MiddlewareChain { - return { action, _ -> Action in - self.interceptedActions.append(action) - return TestOnlyAction() - } - } - - public init() { } - - /// Check if a given action have been intercepted before by the Middleware. - /// - /// - Parameter action: action to be checked - /// - Returns: returns true if an action with the same params have been intercepted before. - public func contains(action: Action) -> Bool { - return interceptedActions.contains(where: { - action.isEqual(to: $0) - }) - } - - /// Check for actions of certain type being intercepted. - /// - /// - Parameter kind: Action type to be checked against the intercepted actions. - /// - Returns: Array of actions of `kind` being intercepted. - public func actions(of kind: T.Type) -> [T] { - return interceptedActions.compactMap { $0 as? T } - } - - /// Clear all the intercepted actions - public func clear() { - interceptedActions.removeAll() - } -} diff --git a/Sources/MiniSwift/Utils/Foundation/Dictionary+Extensions.swift b/Sources/MiniSwift/Utils/Foundation/Dictionary+Extensions.swift deleted file mode 100644 index e902564e..00000000 --- a/Sources/MiniSwift/Utils/Foundation/Dictionary+Extensions.swift +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -extension Dictionary { - - /// Returns the value for the given key. If the key is not found in the map, calls the `defaultValue` function, - /// puts its result into the map under the given key and returns it. - public mutating func getOrPut(_ key: Key, defaultValue: @autoclosure () -> Value) -> Value { - return self[key, orPut: defaultValue()] - } - - public subscript (_ key: Key, orPut value: @autoclosure () -> Value) -> Value { - mutating get { - if let value = self[key] { - return value - } - let value = value() - self[key] = value - return value - } - } - - public subscript(unwrapping key: Key) -> Value! { - return self[key] - } -} - -extension Dictionary where Value: Task { - - public subscript(task key: Key) -> Value { - return self[unwrapping: key] - } - - public func hasValue(for key: Dictionary.Key) -> Bool { - return self.keys.contains(key) - } -} diff --git a/Sources/MiniSwift/Utils/Foundation/DispatchQueue+Extensions.swift b/Sources/MiniSwift/Utils/Foundation/DispatchQueue+Extensions.swift deleted file mode 100644 index 5d92c270..00000000 --- a/Sources/MiniSwift/Utils/Foundation/DispatchQueue+Extensions.swift +++ /dev/null @@ -1,29 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -public extension DispatchQueue { - private static var token: DispatchSpecificKey<()> = { - let key = DispatchSpecificKey<()>() - DispatchQueue.main.setSpecific(key: key, value: ()) - return key - }() - - static var isMain: Bool { - return DispatchQueue.getSpecific(key: token) != nil - } -} diff --git a/Sources/MiniSwift/Utils/Foundation/String+Extensions.swift b/Sources/MiniSwift/Utils/Foundation/String+Extensions.swift deleted file mode 100644 index cdabd4a1..00000000 --- a/Sources/MiniSwift/Utils/Foundation/String+Extensions.swift +++ /dev/null @@ -1,25 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -extension String { - - init(dumping object: T) { - self.init() - dump(object, to: &self) - } -} diff --git a/Sources/MiniSwift/Utils/PayloadAction.swift b/Sources/MiniSwift/Utils/PayloadAction.swift deleted file mode 100644 index c063791a..00000000 --- a/Sources/MiniSwift/Utils/PayloadAction.swift +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -public protocol PayloadAction { - associatedtype Payload - - init(task: Task, payload: Payload?) -} - -public protocol CompletableAction: Action & PayloadAction { } - -public protocol EmptyAction: Action & PayloadAction where Payload == Swift.Never { - 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 { } diff --git a/Sources/MiniSwift/Utils/RxSwift/ObservableType+Extensions.swift b/Sources/MiniSwift/Utils/RxSwift/ObservableType+Extensions.swift deleted file mode 100644 index 0f3ad228..00000000 --- a/Sources/MiniSwift/Utils/RxSwift/ObservableType+Extensions.swift +++ /dev/null @@ -1,184 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -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 { - return 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 - .skipWhile { - 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)[task: key].isTerminal } - case .forever: - return self - .skipWhile { taskMap($0)[task: key].status == .idle || taskMap($0)[task: key].isTerminal } - .filter { taskMap($0).hasValue(for: key) } - .filter { taskMap($0)[task: key].isTerminal } - .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 { - return 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 { - return self - .filterForKeyedLifetime(key: key, taskMap: taskMap, lifetime: lifetime) - .subscribe(onNext: { state in - let task = taskMap(state)[task: key] - if task.isSuccessful { - success(state) - } else if task.isFailure { - error(state) - } else { - 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, mode: .sync) - 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, mode: .sync) - 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/MiniSwift/Utils/RxSwift/PrimitiveSequenceType+Extensions.swift b/Sources/MiniSwift/Utils/RxSwift/PrimitiveSequenceType+Extensions.swift deleted file mode 100644 index 4bfdce9e..00000000 --- a/Sources/MiniSwift/Utils/RxSwift/PrimitiveSequenceType+Extensions.swift +++ /dev/null @@ -1,126 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -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, - mode: Dispatcher.DispatchMode.UI = .async, - fillOnError errorPayload: A.Payload? = nil) - -> Disposable where A.Payload == Self.Element { - let subscription = self.subscribe( - onSuccess: { payload in - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestSuccess(expiration), payload: payload) - dispatcher.dispatch(action, mode: mode) - }, - onError: { error in - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestFailure(error), payload: errorPayload) - dispatcher.dispatch(action, mode: mode) - } - ) - return subscription - } - - func dispatch(action: A.Type, - expiration: Task.Expiration = .immediately, - key: A.Key, - on dispatcher: Dispatcher, - mode: Dispatcher.DispatchMode.UI = .async, - fillOnError errorPayload: A.Payload? = nil) - -> Disposable where A.Payload == Self.Element { - let subscription = self.subscribe( - onSuccess: { payload in - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestSuccess(expiration), payload: payload, key: key) - dispatcher.dispatch(action, mode: mode) - }, - onError: { error in - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestFailure(error), payload: errorPayload, key: key) - dispatcher.dispatch(action, mode: mode) - } - ) - return subscription - } - - func action(_ action: A.Type, - expiration: Task.Expiration = .immediately, - fillOnError errorPayload: A.Payload? = nil) - -> Single where A.Payload == Self.Element { - return Single.create { single in - let subscription = self.subscribe( - onSuccess: { payload in - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestSuccess(expiration), payload: payload) - single(.success(action)) - }, - onError: { 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, - mode: Dispatcher.DispatchMode.UI = .async) - -> 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, mode: mode) - case .error(let error): - // swiftlint:disable:next explicit_init - let action = A.init(task: .requestFailure(error)) - dispatcher.dispatch(action, mode: mode) - } - } - return subscription - } - - func action(_ action: A.Type, - expiration: Task.Expiration = .immediately) - -> Single { - return 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/Sources/MiniSwift/Utils/SharedDictionary.swift b/Sources/MiniSwift/Utils/SharedDictionary.swift deleted file mode 100644 index e8d6bf0d..00000000 --- a/Sources/MiniSwift/Utils/SharedDictionary.swift +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -/// Wrapper class to allow pass dictionaries with a memory reference -public class SharedDictionary { - public var innerDictionary: [Key: Value] - - public init() { - innerDictionary = [:] - } - - public func getOrPut(_ key: Key, defaultValue: @autoclosure () -> Value) -> Value { - return self[key, orPut: defaultValue()] - } - - public func get(withKey key: Key) -> Value? { - return self[key] - } - - public subscript (_ key: Key, orPut defaultValue: @autoclosure () -> Value) -> Value { - return innerDictionary[key, orPut: defaultValue()] - } - - public subscript (_ key: Key) -> Value? { - return innerDictionary[key] - } -} diff --git a/Sources/None.swift b/Sources/None.swift new file mode 100644 index 00000000..0d21e91e --- /dev/null +++ b/Sources/None.swift @@ -0,0 +1,7 @@ +import Foundation + +public struct None: Equatable { + public static var none: None { + None() + } +} diff --git a/Sources/MiniSwift/Utils/OrderedSet.swift b/Sources/OrderedSet.swift similarity index 81% rename from Sources/MiniSwift/Utils/OrderedSet.swift rename to Sources/OrderedSet.swift index 8ad9da2b..09afc722 100644 --- a/Sources/MiniSwift/Utils/OrderedSet.swift +++ b/Sources/OrderedSet.swift @@ -1,19 +1,3 @@ -/* - Copyright [2019] [BQ] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - import Foundation /** @@ -30,7 +14,7 @@ public class OrderedSet { /// Returns the number of elements in the OrderedSet. public var count: Int { - return internalSet.count + internalSet.count } /// Inserts an item. Performance: O(n) @@ -73,7 +57,7 @@ public class OrderedSet { /// Returns true if and only if the item exists somewhere in the set. public func exists(_ item: T) -> Bool { - return indexOf(item) != nil + indexOf(item) != nil } /// Returns the index of an item if it exists, or nil otherwise. @@ -137,33 +121,33 @@ public class OrderedSet { /// Returns the 'maximum' or 'largest' value in the set. public var max: T? { - return internalSet.isEmpty ? nil : internalSet[count - 1] + internalSet.isEmpty ? nil : internalSet[count - 1] } /// Returns the 'minimum' or 'smallest' value in the set. public var min: T? { - return internalSet.isEmpty ? nil : internalSet[0] + internalSet.isEmpty ? nil : internalSet[0] } /// Returns the k-th largest element in the set, if k is in the range /// [1, count]. Returns nil otherwise. public func kLargest(element: Int) -> T? { - return element > count || element <= 0 ? nil : internalSet[count - element] + element > count || element <= 0 ? nil : internalSet[count - element] } /// Returns the k-th smallest element in the set, if k is in the range /// [1, count]. Returns nil otherwise. public func kSmallest(element: Int) -> T? { - return element > count || element <= 0 ? nil : internalSet[element - 1] + element > count || element <= 0 ? nil : internalSet[element - 1] } /// For each function public func forEach(_ body: (T) -> Swift.Void) { - return internalSet.forEach(body) + internalSet.forEach(body) } /// Enumerated function public func enumerated() -> EnumeratedSequence<[T]> { - return internalSet.enumerated() + internalSet.enumerated() } } diff --git a/Sources/Publishers/Dispatcher+Combine.swift b/Sources/Publishers/Dispatcher+Combine.swift new file mode 100644 index 00000000..181bd52f --- /dev/null +++ b/Sources/Publishers/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/Publishers/PublisherExtensions+CompletableActions.swift b/Sources/Publishers/PublisherExtensions+CompletableActions.swift new file mode 100644 index 00000000..70c05f6c --- /dev/null +++ b/Sources/Publishers/PublisherExtensions+CompletableActions.swift @@ -0,0 +1,63 @@ +import Combine +import Foundation + +public extension Publisher { + 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: .failure(error)) + dispatcher.dispatch(action) + + case .finished: + break + } + } receiveValue: { payload in + let action = A(task: .success(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: .failure(error, tag: "\(key)"), key: key) + dispatcher.dispatch(action) + + case .finished: + break + } + } receiveValue: { payload in + let action = A(task: .success(payload, expiration: expiration, tag: "\(key)"), key: key) + dispatcher.dispatch(action) + } + } + + func dispatch(action: A.Type, + attribute: A.Attribute, + 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: .failure(error), attribute: attribute) + dispatcher.dispatch(action) + + case .finished: + break + } + } receiveValue: { payload in + let action = A(task: .success(payload, expiration: expiration), attribute: attribute) + dispatcher.dispatch(action) + } + } +} diff --git a/Sources/Publishers/PublisherExtensions+EmptyActions.swift b/Sources/Publishers/PublisherExtensions+EmptyActions.swift new file mode 100644 index 00000000..fb2feaa5 --- /dev/null +++ b/Sources/Publishers/PublisherExtensions+EmptyActions.swift @@ -0,0 +1,116 @@ +import Combine +import Foundation + +public extension Publisher { + 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: .failure(error), key: key) + dispatcher.dispatch(action) + + case .finished: + let action = A(task: .success(expiration: expiration, tag: "\(key)"), key: key) + dispatcher.dispatch(action) + } + } receiveValue: { _ in + } + } + + func dispatch(action: A.Type, + expiration: TaskExpiration = .immediately, + key: A.Key, + on dispatcher: Dispatcher) + -> Cancellable where A.TaskPayload == None, A.TaskError == Failure, Output == Void { + sink { completion in + switch completion { + case .failure(let error): + let action = A(task: .failure(error), key: key) + dispatcher.dispatch(action) + + case .finished: + let action = A(task: .success(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: .failure(error)) + dispatcher.dispatch(action) + + case .finished: + let action = A(task: .success(expiration: expiration)) + dispatcher.dispatch(action) + } + } receiveValue: { _ in + } + } + + func dispatch(action: A.Type, + expiration: TaskExpiration = .immediately, + on dispatcher: Dispatcher) + -> Cancellable where A.TaskPayload == None, A.TaskError == Failure, Output == Void { + sink { completion in + switch completion { + case .failure(let error): + let action = A(task: .failure(error)) + dispatcher.dispatch(action) + + case .finished: + let action = A(task: .success(expiration: expiration)) + dispatcher.dispatch(action) + } + } receiveValue: { _ in + } + } + + func dispatch(action: A.Type, + attribute: A.Attribute, + 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: .failure(error), attribute: attribute) + dispatcher.dispatch(action) + + case .finished: + let action = A(task: .success(expiration: expiration), attribute: attribute) + dispatcher.dispatch(action) + } + } receiveValue: { _ in + } + } + + func dispatch(action: A.Type, + attribute: A.Attribute, + expiration: TaskExpiration = .immediately, + on dispatcher: Dispatcher) + -> Cancellable where A.TaskPayload == None, A.TaskError == Failure, Output == Void { + sink { completion in + switch completion { + case .failure(let error): + let action = A(task: .failure(error), attribute: attribute) + dispatcher.dispatch(action) + + case .finished: + let action = A(task: .success(expiration: expiration), attribute: attribute) + dispatcher.dispatch(action) + } + } receiveValue: { _ in + } + } +} diff --git a/Sources/Publishers/Publishers.CombineMiniTasksArray.swift b/Sources/Publishers/Publishers.CombineMiniTasksArray.swift new file mode 100644 index 00000000..dbb9b3d4 --- /dev/null +++ b/Sources/Publishers/Publishers.CombineMiniTasksArray.swift @@ -0,0 +1,69 @@ +import Combine +import Foundation + +public extension Publisher { + 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 Taskable] else { + fatalError("Imposible!") + } + + if tasks.map({ $0.isRunning }).contains(true) { + return downstream.receive(.running()) + } + + if let failureTask = tasks.first(where: { $0.isFailure }), let failure = failureTask.error as? Output.Failure { + return downstream.receive(.failure(failure)) + } + + if + !tasks.map({ $0.isSuccessful }).contains(false), + let payload = tasks.compactMap({ $0.payload }) as? TaskPayload { + return downstream.receive(.success(payload)) + } + + return downstream.receive(.idle()) + } + + func receive(completion: Subscribers.Completion) { + downstream.receive(completion: completion) + } + } +} diff --git a/Sources/Publishers/Publishers.CombineMiniTasksTuple2.swift b/Sources/Publishers/Publishers.CombineMiniTasksTuple2.swift new file mode 100644 index 00000000..3049a5ec --- /dev/null +++ b/Sources/Publishers/Publishers.CombineMiniTasksTuple2.swift @@ -0,0 +1,94 @@ +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 { + 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 Taskable, any Taskable) else { + fatalError("Imposible!") + } + + if tuple.0.isRunning || tuple.1.isRunning { + return downstream.receive(.running()) + } + + if + tuple.0.isFailure || tuple.1.isFailure, + let failure = tuple.0.error as? Output.Failure ?? + tuple.1.error as? Output.Failure { + return downstream.receive(.failure(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(.success(payload)) + } + + return downstream.receive(.idle()) + } + + func receive(completion: Subscribers.Completion) { + downstream.receive(completion: completion) + } + } +} diff --git a/Sources/Publishers/Publishers.CombineMiniTasksTuple3.swift b/Sources/Publishers/Publishers.CombineMiniTasksTuple3.swift new file mode 100644 index 00000000..f1e88f97 --- /dev/null +++ b/Sources/Publishers/Publishers.CombineMiniTasksTuple3.swift @@ -0,0 +1,100 @@ +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 { + 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 Taskable, any Taskable, any Taskable) else { + fatalError("Imposible!") + } + + if tuple.0.isRunning || tuple.1.isRunning || tuple.2.isRunning { + return downstream.receive(.running()) + } + + 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(.failure(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(.success(payload)) + } + + return downstream.receive(.idle()) + } + + func receive(completion: Subscribers.Completion) { + downstream.receive(completion: completion) + } + } +} diff --git a/Sources/Publishers/Publishers.CombineMiniTasksTuple4.swift b/Sources/Publishers/Publishers.CombineMiniTasksTuple4.swift new file mode 100644 index 00000000..4269fcff --- /dev/null +++ b/Sources/Publishers/Publishers.CombineMiniTasksTuple4.swift @@ -0,0 +1,106 @@ +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 { + 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 Taskable, any Taskable, any Taskable, any Taskable) else { + fatalError("Imposible!") + } + + if tuple.0.isRunning || tuple.1.isRunning || tuple.2.isRunning || tuple.3.isRunning { + return downstream.receive(.running()) + } + + 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(.failure(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(.success(payload)) + } + + return downstream.receive(.idle()) + } + + func receive(completion: Subscribers.Completion) { + downstream.receive(completion: completion) + } + } +} diff --git a/Sources/Publishers/Publishers.EraseToEmptyTask.swift b/Sources/Publishers/Publishers.EraseToEmptyTask.swift new file mode 100644 index 00000000..5dfe3230 --- /dev/null +++ b/Sources/Publishers/Publishers.EraseToEmptyTask.swift @@ -0,0 +1,63 @@ +import Combine + +public extension Publisher { + func eraseToEmptyTask() -> Publishers.EraseToEmptyTask + where Output: Taskable { + Publishers.EraseToEmptyTask(upstream: self) + } +} + +public extension Publishers { + /// Create a `Publisher` that connect an Upstream (Another publisher) that type erases `Task`s to `EmptyTask` + /// The Output of this `Publisher` always is a combined `EmptyTask` + struct EraseToEmptyTask: Publisher where Upstream.Output: Taskable { + public typealias Output = EmptyTask + 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.EraseToEmptyTask { + 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 { + switch input.status { + case .success: + return downstream.receive(.success()) + + case .idle: + return downstream.receive(.idle()) + + case .running: + return downstream.receive(.running()) + + case .failure(let error): + return downstream.receive(.failure(error)) + } + } + + func receive(completion: Subscribers.Completion) { + downstream.receive(completion: completion) + } + } +} diff --git a/Sources/Publishers/Publishers.MapToLatestTask.swift b/Sources/Publishers/Publishers.MapToLatestTask.swift new file mode 100644 index 00000000..f5c788b5 --- /dev/null +++ b/Sources/Publishers/Publishers.MapToLatestTask.swift @@ -0,0 +1,36 @@ +import Combine + +public extension Publisher { + func mapToLatestTask( + transform: @escaping ((Output.Payload.ID) -> (AnyPublisher)) + ) + -> AnyPublisher + where Output: Taskable & Equatable, Output.Payload: Identifiable, + DownTask: Taskable & Equatable, DownTask.Failure == Output.Failure { + map { (task: Output) -> AnyPublisher in + switch task.status { + case .success(let payload): + return transform(payload.id) + .removeDuplicates() + .eraseToAnyPublisher() + + case .idle: + return Just(.idle()) + .setFailureType(to: Failure.self) + .eraseToAnyPublisher() + + case .running: + return Just(.running()) + .setFailureType(to: Failure.self) + .eraseToAnyPublisher() + + case .failure(error: let error): + return Just(.failure(error)) + .setFailureType(to: Failure.self) + .eraseToAnyPublisher() + } + } + .switchToLatest() + .eraseToAnyPublisher() + } +} diff --git a/Sources/Publishers/Publishers.RemoveExpired.swift b/Sources/Publishers/Publishers.RemoveExpired.swift new file mode 100644 index 00000000..1b6ab79f --- /dev/null +++ b/Sources/Publishers/Publishers.RemoveExpired.swift @@ -0,0 +1,58 @@ +import Combine + +public extension Publisher { + func removeExpired(margin: TimeInterval = taskDefaultMargin) -> Publishers.RemoveExpired + where Output: Taskable { + Publishers.RemoveExpired(upstream: self, margin: margin) + } +} + +public extension Publishers { + /// Create a `Publisher` that connect an Upstream (Another publisher) that filter any expired task received + /// The Output of this `Publisher` is the same of the Upstream. + struct RemoveExpired: Publisher where Upstream.Output: Taskable { + public typealias Output = Upstream.Output + public typealias Failure = Upstream.Failure + + public let upstream: Upstream + private let margin: TimeInterval + + public init(upstream: Upstream, margin: TimeInterval) { + self.upstream = upstream + self.margin = margin + } + + public func receive(subscriber: S) where Upstream.Failure == S.Failure, Output == S.Input { + upstream.subscribe(Inner(downstream: subscriber, margin: margin)) + } + } +} + +extension Publishers.RemoveExpired { + private struct Inner: Subscriber + where Downstream.Input == Output, Downstream.Failure == Upstream.Failure, Output: Taskable { + let combineIdentifier = CombineIdentifier() + private let downstream: Downstream + private let margin: TimeInterval + + fileprivate init(downstream: Downstream, margin: TimeInterval) { + self.downstream = downstream + self.margin = margin + } + + func receive(subscription: Subscription) { + downstream.receive(subscription: subscription) + } + + func receive(_ input: Upstream.Output) -> Subscribers.Demand { + if input.isExpired(margin: margin) { + return .none + } + return downstream.receive(input) + } + + func receive(completion: Subscribers.Completion) { + downstream.receive(completion: completion) + } + } +} diff --git a/Sources/Publishers/Publishers.Scope.swift b/Sources/Publishers/Publishers.Scope.swift new file mode 100644 index 00000000..c76d5e1f --- /dev/null +++ b/Sources/Publishers/Publishers.Scope.swift @@ -0,0 +1,12 @@ +import Combine +import Foundation + +public extension Publisher { + /// From a publisher, we can focus on a task and filter all expired and duplicated task. This publisher don't send value if at suscription moment there is a expired task. + func scope(_ transform: @escaping (Self.Output) -> T, margin: TimeInterval = taskDefaultMargin) -> AnyPublisher { + map(transform) + .removeExpired(margin: margin) + .removeDuplicates() + .eraseToAnyPublisher() + } +} diff --git a/Sources/ReducerGroup.swift b/Sources/ReducerGroup.swift new file mode 100644 index 00000000..3c17edd9 --- /dev/null +++ b/Sources/ReducerGroup.swift @@ -0,0 +1,20 @@ +import Combine +import Foundation + +public protocol Group: Cancellable { + var cancellables: Set { get } +} + +public class ReducerGroup: Group { + public var cancellables = Set() + + public init(_ builder: () -> [Cancellable]) { + let disposable = builder() + disposable.forEach { _ = cancellables.insert(AnyCancellable($0)) } + } + + public func cancel() { + cancellables.removeAll() + cancellables = Set() + } +} diff --git a/Sources/SharedDictionary.swift b/Sources/SharedDictionary.swift new file mode 100644 index 00000000..a0f41cdc --- /dev/null +++ b/Sources/SharedDictionary.swift @@ -0,0 +1,26 @@ +import Foundation + +/// Wrapper class to allow pass dictionaries with a memory reference +public class SharedDictionary { + public var innerDictionary: [Key: Value] + + public init() { + innerDictionary = [:] + } + + public func safeValue(_ key: Key, defaultValue: () -> Value) -> Value { + self[key, ifNotExistsSave: defaultValue()] + } + + public func value(withKey key: Key) -> Value? { + self[key] + } + + public subscript (_ key: Key, ifNotExistsSave defaultValue: @autoclosure () -> Value) -> Value { + innerDictionary.safeValue(key, defaultValue: defaultValue) + } + + public subscript (_ key: Key) -> Value? { + innerDictionary[key] + } +} diff --git a/Sources/State.swift b/Sources/State.swift new file mode 100644 index 00000000..9f931122 --- /dev/null +++ b/Sources/State.swift @@ -0,0 +1,4 @@ +import Foundation + +public protocol State: Equatable { +} diff --git a/Sources/Store.swift b/Sources/Store.swift new file mode 100644 index 00000000..bd90d387 --- /dev/null +++ b/Sources/Store.swift @@ -0,0 +1,88 @@ +import Combine +import Foundation + +public class Store: Publisher { + public typealias Output = StoreState + public typealias Failure = Never + public typealias StoreController = StoreController + + public let dispatcher: Dispatcher + public var storeController: StoreController + public var state: StoreState { + didSet { + queue.sync { + if state != oldValue { + if emitsInitialValue { + stateCurrentValueSubject.send(state) + } else { + statePassthroughSubject.send(state) + } + } + } + } + } + public var initialState: StoreState + + public init(_ state: StoreState, + dispatcher: Dispatcher, + storeController: StoreController, + emitsInitialValue: Bool = true) { + self.initialState = state + self.dispatcher = dispatcher + self.stateCurrentValueSubject = .init(state) + self.statePassthroughSubject = .init() + self.storeController = storeController + self.emitsInitialValue = emitsInitialValue + self.state = state + } + + /** + Property responsible of reduce the `State` given a certain `Action` being triggered. + ``` + public var reducerGroup: ReducerGroup { + ReducerGroup {[ + Reducer(of: SomeAction.self, on: self.dispatcher) { (action: SomeAction) + self.state = myCoolNewState + }, + Reducer(of: OtherAction.self, on: self.dispatcher) { (action: OtherAction) + // Needed work + self.state = myAnotherState + } + } + ]} + ``` + - Note : The property has a default implementation which complies with the @_functionBuilder's current limitations, where no empty blocks can be produced in this iteration. + */ + public var reducerGroup: ReducerGroup { + ReducerGroup { + [] + } + } + + public func replayOnce() { + if emitsInitialValue { + stateCurrentValueSubject.send(state) + } else { + statePassthroughSubject.send(state) + } + + dispatcher.stateWasReplayed(state: state) + } + + public func reset() { + state = initialState + } + + public func receive(subscriber: S) where Failure == S.Failure, Output == S.Input { + if emitsInitialValue { + stateCurrentValueSubject.subscribe(subscriber) + } else { + statePassthroughSubject.subscribe(subscriber) + } + } + + private var stateCurrentValueSubject: CurrentValueSubject + private var statePassthroughSubject: PassthroughSubject + private let queue = DispatchQueue(label: "atomic state") + private let emitsInitialValue: Bool +} diff --git a/Sources/Task/EmptyTask.swift b/Sources/Task/EmptyTask.swift new file mode 100644 index 00000000..ccbff7ea --- /dev/null +++ b/Sources/Task/EmptyTask.swift @@ -0,0 +1,3 @@ +import Foundation + +public typealias EmptyTask = Task diff --git a/Sources/Task/KeyedTask.swift b/Sources/Task/KeyedTask.swift new file mode 100644 index 00000000..c7956c7f --- /dev/null +++ b/Sources/Task/KeyedTask.swift @@ -0,0 +1,50 @@ +import Foundation + +public typealias KeyedTask = [Key: Task] +public typealias KeyedEmptyTask = KeyedTask + +extension KeyedTask where Key: Hashable, Value: Taskable { + /// If exists retrieve the task for the given key, if not receive an idle task + public subscript(task key: Key) -> Value { + self[key] ?? .idle() + } + + public func hasValue(for key: Dictionary.Key) -> Bool { + self.keys.contains(key) + } + + /// Returns true if the KeyedTask contains a task with given key and its running. If the key don't exists return false + public func isIdle(key: Key) -> Bool { + self[key]?.isIdle ?? false + } + + /// Returns true if the KeyedTask contains a task with given key and its running. If the key don't exists return false + public func isRunning(key: Key) -> Bool { + self[key]?.isRunning ?? false + } + + /// Returns true if the KeyedTask contains a task with given key and its expired. If the key don't exists return false + public func isExpired(key: Key) -> Bool { + self[key]?.isExpired ?? false + } + + /// Returns true if the KeyedTask contains a task with given key and its recently succeded. If the key don't exists return false + public func isRecentlySucceeded(key: Key) -> Bool { + self[key]?.isRecentlySucceeded ?? false + } + + /// Returns true if the KeyedTask contains a task with given key and its terminal. If the key don't exists return false + public func isTerminal(key: Key) -> Bool { + self[key]?.isTerminal ?? false + } + + /// Returns true if the KeyedTask contains a task with given key and its succesful. If the key don't exists return false + public func isSuccessful(key: Key) -> Bool { + self[key]?.isSuccessful ?? false + } + + /// Returns true if the KeyedTask contains a task with given key and its failure. If the key don't exists return false + public func isFailure(key: Key) -> Bool { + self[key]?.isFailure ?? false + } +} diff --git a/Sources/Task/Task.swift b/Sources/Task/Task.swift new file mode 100644 index 00000000..22b470a8 --- /dev/null +++ b/Sources/Task/Task.swift @@ -0,0 +1,160 @@ +import Foundation + +public let taskDefaultMargin: TimeInterval = 0.250 + +public class Task: Taskable, Equatable, CustomDebugStringConvertible { + public typealias Payload = T + public typealias Failure = E + + public let status: TaskStatus + public let started: Date + public let expiration: TaskExpiration + public let tag: String? + public let progress: Decimal? + + internal required init(status: TaskStatus = .idle, + started: Date = Date(), + expiration: TaskExpiration = .immediately, + tag: String? = nil, + progress: Decimal? = nil) { + self.status = status + self.started = started + self.expiration = expiration + self.tag = tag + self.progress = progress + } + + 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 { + status == .idle + } + + public var isRunning: Bool { + status == .running + } + + public func isExpired(margin: TimeInterval) -> Bool { + started.timeIntervalSinceNow + expiration.value + margin < 0 + } + + public var isRecentlySucceeded: Bool { + switch status { + case .success where started.timeIntervalSinceNow + expiration.value >= 0: + return true + + default: + return false + } + } + + public var isTerminal: Bool { + switch status { + case .success, .failure: + return true + + default: + return false + } + } + + public var isSuccessful: Bool { + switch status { + case .success: + return true + + default: + return false + } + } + + public var isFailure: Bool { + switch status { + case .failure: + return true + + default: + return false + } + } + + public static func idle(started: Date, tag: String?, progress: Decimal?) -> Self { + .init(status: .idle, + started: started, + tag: tag, + progress: progress) + } + + public static func running(started: Date, tag: String?, progress: Decimal?) -> Self { + .init(status: .running, + started: started, + tag: tag, + progress: progress) + } + + public static func failure(_ error: Failure, started: Date, tag: String?, progress: Decimal?) -> Self { + .init(status: .failure(error: error), + started: started, + tag: tag, + progress: progress) + } + + public static func success(_ payload: Payload, started: Date, expiration: TaskExpiration, tag: String?, progress: Decimal?) -> Self { + .init(status: .success(payload: payload), + started: started, + expiration: expiration, + tag: tag, + progress: progress) + } + + // MARK: - CustomDebugStringConvertible + public var debugDescription: String { + let tagPrint: String + if let tag = tag { + tagPrint = tag + } else { + tagPrint = "nil" + } + + return """ + 🚀 Task: status: \(status), started: \(started), tag: \(tagPrint) + 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 extension Task where T == None { + static func success(started: Date = Date(), + expiration: TaskExpiration = .immediately, + tag: String? = nil, + progress: Decimal? = nil) -> Self { + .init(status: .success(payload: .none), + started: started, + expiration: expiration, + tag: tag, + progress: progress) + } +} diff --git a/Sources/Task/TaskExpiration.swift b/Sources/Task/TaskExpiration.swift new file mode 100644 index 00000000..7ea20690 --- /dev/null +++ b/Sources/Task/TaskExpiration.swift @@ -0,0 +1,24 @@ +import Foundation + +public enum TaskExpiration: Equatable { + 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/Task/TaskStatus.swift b/Sources/Task/TaskStatus.swift new file mode 100644 index 00000000..87d0ce4e --- /dev/null +++ b/Sources/Task/TaskStatus.swift @@ -0,0 +1,22 @@ +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 + + case (.failure(let lhsFailure), .failure(let rhsFailure)): + return lhsFailure == rhsFailure + + default: + return false + } + } +} diff --git a/Sources/Task/Taskable.swift b/Sources/Task/Taskable.swift new file mode 100644 index 00000000..3d70d487 --- /dev/null +++ b/Sources/Task/Taskable.swift @@ -0,0 +1,59 @@ +import Foundation + +public protocol Taskable { + associatedtype Payload: Equatable + associatedtype Failure: Error & Equatable + + var isIdle: Bool { get } + var isRunning: Bool { get } + func isExpired(margin: TimeInterval) -> Bool + 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 } + var tag: String? { get } + + static func idle(started: Date, tag: String?, progress: Decimal?) -> Self + static func running(started: Date, tag: String?, progress: Decimal?) -> Self + static func failure(_ error: Failure, started: Date, tag: String?, progress: Decimal?) -> Self + static func success(_ payload: Payload, started: Date, expiration: TaskExpiration, tag: String?, progress: Decimal?) -> Self +} + +public extension Taskable { + var isExpired: Bool { + self.isExpired(margin: taskDefaultMargin) + } +} + +public extension Taskable { + static func idle(started: Date = Date(), + tag: String? = nil, + progress: Decimal? = nil) -> Self { + idle(started: started, tag: tag, progress: progress) + } + + static func running(started: Date = Date(), + tag: String? = nil, + progress: Decimal? = nil) -> Self { + running(started: started, tag: tag, progress: progress) + } + + static func failure(_ error: Failure, + started: Date = Date(), + tag: String? = nil, + progress: Decimal? = nil) -> Self { + failure(error, started: started, tag: tag, progress: progress) + } + + static func success(_ payload: Payload, + started: Date = Date(), + expiration: TaskExpiration = .immediately, + tag: String? = nil, + progress: Decimal? = nil) -> Self { + success(payload, started: started, expiration: expiration, tag: tag, progress: progress) + } +} diff --git a/Templates/AutoEquatableAction.stencil b/Templates/AutoEquatableAction.stencil deleted file mode 100644 index 26b06ad8..00000000 --- a/Templates/AutoEquatableAction.stencil +++ /dev/null @@ -1,29 +0,0 @@ -// MARK: - Empty template base file -{% macro compareVariables variables %} - {% for variable in variables where variable.name|!contains:"debug" and variable|!static %} - {% if not variable.annotations.skipEquality %} - guard {{ variable.name }} == action.{{ variable.name }} else { return false } - {% else %} - //TODO: Make {{ variable.typeName }} Equatable. - {% endif %} - {% endfor %} -{% endmacro %} - -{% for type in types.all where type.name|lowercase|!contains:"test" and type.implements.Action or type.implements.CompletableAction or type.implements.EmptyAction %} - - // sourcery:inline:auto:{{ type.name }}.AutoEquatableAction - func isEqualTo(_ other: Action) -> Bool { - {% for variable in type.attributes|!computed|instance %} - // {{variable.name }} - {% endfor %} - {% if type.allVariables|!computed|!static|count == 0 %} - guard let _ = other as? {{ type.name }} else { return false } - {% else %} - guard let action = other as? {{ type.name }} else { return false } - {% call compareVariables type.allVariables %} - {% endif %} - return true - } - // sourcery:end -{% endfor %} - diff --git a/Templates/Autocopy.stencil b/Templates/Autocopy.stencil deleted file mode 100644 index 8ff24084..00000000 --- a/Templates/Autocopy.stencil +++ /dev/null @@ -1,61 +0,0 @@ -{# Thanks to: https://arasthel.com/data-classes-on-swift/#selectwhichvalueswillbechangedandwhichwillbecopied #} -// swiftlint:disable all -public protocol AutoCopy { } - -public extension AutoCopy { - - func setValueOptional(_ value: OptionalCopyValue, _ defaultValue: T?) -> T? { - switch(value) { - case let .new(content): - return content - case .same: - return defaultValue - default: - return nil - } - } -} - -public enum OptionalCopyValue: ExpressibleByNilLiteral { - - case new(T) - case same - case `nil` - - public init(nilLiteral: ()) { - self = .nil - } -} - -prefix operator * - -prefix func * (lhs: T) -> OptionalCopyValue { - return .new(lhs) -} - -// swiftlint:enable all - -{# Thanks to: https://arasthel.com/data-classes-on-swift/#selectwhichvalueswillbechangedandwhichwillbecopied #} - -{% for type in types.all where type.implements.AutoCopy %} - {% if type|annotated:"import" %} - import {{type.annotations.import }} - {% endif %} - // swiftlint:disable all - extension {{ type.name }} { - - func copy( - {% for variable in type.storedVariables %} - {{ variable.name }} copied_{{ variable.name }}: OptionalCopyValue<{{ variable.unwrappedTypeName }}> = .same{% if not forloop.last %}, {% endif %} - {% endfor %} - ) -> {{ type.name }} { - return {{ type.name }}( - {% for variable in type.storedVariables %} - {% if variable.isOptional %} - {{ variable.name }}: setValueOptional(copied_{{ variable.name }}, self.{{ variable.name }}){% else %}{{ variable.name }}: setValueOptional(copied_{{ variable.name }}, self.{{ variable.name }}) ?? self.{{ variable.name }}{% endif %}{% if not forloop.last %}, {% endif %} - {% endfor %} - ) - } - } -{% endfor %} -// swiftlint:enable all diff --git a/Tests/ActionTests.swift b/Tests/ActionTests.swift new file mode 100644 index 00000000..348c1a9f --- /dev/null +++ b/Tests/ActionTests.swift @@ -0,0 +1,26 @@ +@testable import Mini +import XCTest + +final class ActionTests: XCTestCase { + func test_actions() { + let payloadTask: Task = .idle() + let emptyTask: EmptyTask = .running() + + 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") + + let attributedAction = TestAttributedAction(attribute: "asd") + XCTAssertEqual(attributedAction.attribute, "asd") + } +} diff --git a/Tests/ChainTests.swift b/Tests/ChainTests.swift new file mode 100644 index 00000000..9927c018 --- /dev/null +++ b/Tests/ChainTests.swift @@ -0,0 +1,19 @@ +@testable import Mini +import XCTest + +final class ChainTests: XCTestCase { + func test_forwarding_chain_forwards_action() { + let forwardingChain = ForwardingChain { action in + guard let action = action as? TestAction else { fatalError() } + return TestAction(counter: action.counter + 1) + } + + let testAction = TestAction(counter: 0) + + XCTAssert(testAction.counter == 0) + + let newAction = forwardingChain.proceed(testAction) as? TestAction + + XCTAssert(newAction?.counter == 1) + } +} diff --git a/Tests/DictionaryExtensionsTests.swift b/Tests/DictionaryExtensionsTests.swift new file mode 100644 index 00000000..6f14007d --- /dev/null +++ b/Tests/DictionaryExtensionsTests.swift @@ -0,0 +1,32 @@ +@testable import Mini +import XCTest + +final class DictionaryExtensionsTests: XCTestCase { + func test_safe_value() { + var dictionary = ["a": 1, "b": 2] + + dictionary.keys.forEach { key in + let value = dictionary.safeValue(key) { + XCTFail("This never been called because key must exists in the dictionary") + return 3 + } + XCTAssertEqual(value, dictionary[key]) + } + XCTAssertEqual(dictionary.keys.count, 2) + + XCTAssertEqual(dictionary.safeValue("c") { 3 }, 3) + XCTAssertEqual(dictionary.keys.count, 3) + } + + func test_get_or_put() { + var dic = [String: Int]() + + XCTAssertEqual(dic.safeValue("foo") { 1 }, 1) + XCTAssertEqual(["foo": 1], dic) + + dic["bar"] = 2 + + XCTAssertEqual(dic.safeValue("bar") { Int.max }, 2) + XCTAssertEqual(dic.safeValue("bar2") { 3 }, 3) + } +} diff --git a/Tests/DispatcherSubscriptionTests.swift b/Tests/DispatcherSubscriptionTests.swift new file mode 100644 index 00000000..78129874 --- /dev/null +++ b/Tests/DispatcherSubscriptionTests.swift @@ -0,0 +1,47 @@ +@testable import Mini +import XCTest + +final class DispatcherSubscriptionTests: XCTestCase { + func test_hashable() { + let dispatcher = Dispatcher() + + let one = DispatcherSubscription(dispatcher: dispatcher, + id: 1, + priority: 2, + tag: "one") { _ in } + let two = DispatcherSubscription(dispatcher: dispatcher, + id: 2, + priority: 2, + tag: "two") { _ in } + + let dict: [DispatcherSubscription: Int] = [one: 1, two: 2] + + XCTAssertEqual(dict[one], 1) + XCTAssertEqual(dict[two], 2) + } + + func test_comparable() { + let dispatcher = Dispatcher() + + let one = DispatcherSubscription(dispatcher: dispatcher, + id: 1, + priority: 2, + tag: "one") { _ in } + let two = DispatcherSubscription(dispatcher: dispatcher, + id: 2, + priority: 2, + tag: "two") { _ in } + let three = DispatcherSubscription(dispatcher: dispatcher, + id: 3, + priority: 1, + tag: "three") { _ in } + + XCTAssertEqual(one, one) + XCTAssertNotEqual(one, two) + XCTAssertNotEqual(one, three) + XCTAssertTrue(one >= two) + XCTAssertFalse(one <= three) + XCTAssertTrue(one > three) + XCTAssertTrue(three < two) + } +} diff --git a/Tests/DispatcherTests.swift b/Tests/DispatcherTests.swift new file mode 100644 index 00000000..31922ed5 --- /dev/null +++ b/Tests/DispatcherTests.swift @@ -0,0 +1,484 @@ +import Combine +@testable import Mini +import XCTest + +final class DispatcherTests: XCTestCase { + func test_subscription_count() { + let dispatcher = Dispatcher() + var cancellables = Set() + + XCTAssert(dispatcher.subscriptionCount == 0) + + dispatcher.subscribe { (_: TestAction) in }.store(in: &cancellables) + dispatcher.subscribe { (_: TestAction) in }.store(in: &cancellables) + + XCTAssert(dispatcher.subscriptionCount == 2) + + cancellables.removeAll() + + XCTAssert(dispatcher.subscriptionCount == 0) + } + + func test_add_remove_interceptor() { + let expectation = XCTestExpectation(description: "Perform Action check") + let dispatcher = Dispatcher() + let interceptor = TestInterceptor(onPerfomAction: { expectation.fulfill() }) + dispatcher.register(interceptor: interceptor) + + XCTAssert(interceptor.actions.isEmpty == true) + + dispatcher.dispatch(TestAction(counter: 1)) + + wait(for: [expectation], timeout: 5.0) + + XCTAssert(interceptor.actions.count == 1) + XCTAssert(interceptor.actions.contains { $0 is TestAction } == true) + + dispatcher.unregister(interceptor: interceptor) + interceptor.actions.removeAll() + + dispatcher.dispatch(TestAction(counter: 1)) + + XCTAssert(interceptor.actions.isEmpty == true) + } + + func test_replay_state_with_initial_value() { + let expectation = XCTestExpectation(description: "Replay state check") + let dispatcher = Dispatcher() + let initialState = TestState() + let store = Store(initialState, dispatcher: dispatcher, storeController: TestStoreController(), emitsInitialValue: true) + let interceptor = TestInterceptor(onStateReplayed: { expectation.fulfill() }) + dispatcher.register(interceptor: interceptor) + + store.replayOnce() + + wait(for: [expectation], timeout: 5.0) + } + + func test_replay_state_without_initial_value() { + let expectation = XCTestExpectation(description: "Replay state check") + let dispatcher = Dispatcher() + let initialState = TestState() + let store = Store(initialState, dispatcher: dispatcher, storeController: TestStoreController(), emitsInitialValue: false) + let interceptor = TestInterceptor(onStateReplayed: { expectation.fulfill() }) + dispatcher.register(interceptor: interceptor) + + store.replayOnce() + + wait(for: [expectation], timeout: 5.0) + } + + 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)) + } + + // 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 + .dispatch(action: TestCompletableAction.self, on: dispatcher) + .store(in: &cancellables) + futureFailure + .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)) + } + + // 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, action.task.expiration == .long { + 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 + .dispatch(action: TestKeyedCompletableAction.self, expiration: .long, key: expectedKey, on: dispatcher) + .store(in: &cancellables) + futureFailure + .dispatch(action: TestKeyedCompletableAction.self, expiration: .long, key: expectedKey, on: dispatcher) + .store(in: &cancellables) + + waitForExpectations(timeout: 10) + } + + func test_send_attributedcompletableaction_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)) + } + + // 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: TestAttributedCompletableAction) 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)") + } + + XCTAssertEqual(action.attribute, "hola") + } + .store(in: &cancellables) + + // SEND! + futureSuccess + .dispatch(action: TestAttributedCompletableAction.self, attribute: "hola", on: dispatcher) + .store(in: &cancellables) + futureFailure + .dispatch(action: TestAttributedCompletableAction.self, attribute: "hola", on: dispatcher) + .store(in: &cancellables) + + waitForExpectations(timeout: 10) + } + + func test_send_emptyaction_to_dispatcher_from_none_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() + + case .failure(let error): + if error == expectedError { + expectationFailure.fulfill() + } + + default: + XCTFail("bad action received: \(action)") + } + } + .store(in: &cancellables) + + // SEND! + futureSuccess + .dispatch(action: TestEmptyAction.self, on: dispatcher) + .store(in: &cancellables) + futureFailure + .dispatch(action: TestEmptyAction.self, on: dispatcher) + .store(in: &cancellables) + + waitForExpectations(timeout: 10) + } + + func test_send_emptyaction_to_dispatcher_from_void_future() { + let dispatcher = Dispatcher() + let expectedError = TestError.berenjenaError + var cancellables = Set() + + let futureSuccess = Future { promise in + promise(.success(())) + } + 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() + + case .failure(let error): + if error == expectedError { + expectationFailure.fulfill() + } + + default: + XCTFail("bad action received: \(action)") + } + } + .store(in: &cancellables) + + // SEND! + futureSuccess + .dispatch(action: TestEmptyAction.self, on: dispatcher) + .store(in: &cancellables) + futureFailure + .dispatch(action: TestEmptyAction.self, on: dispatcher) + .store(in: &cancellables) + + waitForExpectations(timeout: 10) + } + + func test_send_keyedemptyaction_to_dispatcher_from_none_future() { + let dispatcher = Dispatcher() + 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)) + } + + // 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: TestKeyedEmptyAction) in + switch action.task.status { + case .success where action.key == expectedKey: + 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 + .dispatch(action: TestKeyedEmptyAction.self, key: expectedKey, on: dispatcher) + .store(in: &cancellables) + futureFailure + .dispatch(action: TestKeyedEmptyAction.self, key: expectedKey, on: dispatcher) + .store(in: &cancellables) + + waitForExpectations(timeout: 10) + } + + func test_send_keyedemptyaction_to_dispatcher_from_void_future() { + let dispatcher = Dispatcher() + let expectedError = TestError.berenjenaError + let expectedKey = "wawa" + var cancellables = Set() + + let futureSuccess = Future { promise in + promise(.success(())) + } + 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: TestKeyedEmptyAction) in + switch action.task.status { + case .success where action.key == expectedKey: + 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 + .dispatch(action: TestKeyedEmptyAction.self, key: expectedKey, on: dispatcher) + .store(in: &cancellables) + futureFailure + .dispatch(action: TestKeyedEmptyAction.self, key: expectedKey, on: dispatcher) + .store(in: &cancellables) + + waitForExpectations(timeout: 10) + } + + func test_send_attributedemptyaction_to_dispatcher_from_none_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: TestAttributedEmptyAction) in + switch action.task.status { + case .success: + expectationSuccess.fulfill() + + case .failure(let error): + if error == expectedError { + expectationFailure.fulfill() + } + + default: + XCTFail("bad action received: \(action)") + } + + XCTAssertEqual(action.attribute, "hola") + } + .store(in: &cancellables) + + // SEND! + futureSuccess + .dispatch(action: TestAttributedEmptyAction.self, attribute: "hola", on: dispatcher) + .store(in: &cancellables) + futureFailure + .dispatch(action: TestAttributedEmptyAction.self, attribute: "hola", on: dispatcher) + .store(in: &cancellables) + + waitForExpectations(timeout: 10) + } + + func test_send_attributedemptyaction_to_dispatcher_from_void_future() { + let dispatcher = Dispatcher() + let expectedError = TestError.berenjenaError + var cancellables = Set() + + let futureSuccess = Future { promise in + promise(.success(())) + } + 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: TestAttributedEmptyAction) in + switch action.task.status { + case .success: + expectationSuccess.fulfill() + + case .failure(let error): + if error == expectedError { + expectationFailure.fulfill() + } + + default: + XCTFail("bad action received: \(action)") + } + + XCTAssertEqual(action.attribute, "hola") + } + .store(in: &cancellables) + + // SEND! + futureSuccess + .dispatch(action: TestAttributedEmptyAction.self, attribute: "hola", on: dispatcher) + .store(in: &cancellables) + futureFailure + .dispatch(action: TestAttributedEmptyAction.self, attribute: "hola", 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 00000000..ba6eada8 --- /dev/null +++ b/Tests/Helpers/TestActions.swift @@ -0,0 +1,95 @@ +import Foundation +import Mini + +class TestAction: Action { + let counter: Int + + init(counter: Int) { + self.counter = counter + } +} + +class TestCompletableAction: CompletableAction { + typealias TaskPayload = String + typealias TaskError = TestError + + let task: Task + + required init(task: Task) { + self.task = task + } +} + +class TestEmptyAction: EmptyAction { + typealias TaskError = TestError + + let task: EmptyTask + + required init(task: EmptyTask) { + self.task = task + } +} + +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 + } +} + +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 + } +} + +class TestAttributedAction: AttributedAction { + typealias Attribute = String + + let attribute: Attribute + + required init(attribute: Attribute) { + self.attribute = attribute + } +} + +class TestAttributedEmptyAction: AttributedEmptyAction { + typealias TaskError = TestError + typealias Attribute = String + + let attribute: Attribute + let task: EmptyTask + + required init(task: EmptyTask, attribute: Attribute) { + self.attribute = attribute + self.task = task + } +} + +class TestAttributedCompletableAction: AttributedCompletableAction { + typealias TaskPayload = String + typealias TaskError = TestError + typealias Attribute = String + + let attribute: Attribute + let task: Task + + required init(task: Task, attribute: Attribute) { + self.attribute = attribute + self.task = task + } +} diff --git a/Tests/Helpers/TestError.swift b/Tests/Helpers/TestError.swift new file mode 100644 index 00000000..62bf00ac --- /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/TestInterceptor.swift b/Tests/Helpers/TestInterceptor.swift new file mode 100644 index 00000000..53de5c2a --- /dev/null +++ b/Tests/Helpers/TestInterceptor.swift @@ -0,0 +1,31 @@ +import Foundation +import Mini +import XCTest + +class TestInterceptor: Interceptor { + typealias TestInterceptorCallBack = () -> Void + + func stateWasReplayed(state: any State) { + onStateReplayed?() + } + + var id = UUID() + + var actions = [Action]() + + private let onStateReplayed: TestInterceptorCallBack? + private let onPerfomAction: TestInterceptorCallBack? + + init(onStateReplayed: TestInterceptorCallBack? = nil, + onPerfomAction: TestInterceptorCallBack? = nil) { + self.onStateReplayed = onStateReplayed + self.onPerfomAction = onPerfomAction + } + + var perform: InterceptorChain { + { action, _ in + self.actions.append(action) + self.onPerfomAction?() + } + } +} diff --git a/Tests/Helpers/TestPayload.swift b/Tests/Helpers/TestPayload.swift new file mode 100644 index 00000000..a454801f --- /dev/null +++ b/Tests/Helpers/TestPayload.swift @@ -0,0 +1,4 @@ +struct TestPayload: Identifiable, Equatable { + let id: String + let value: Int +} diff --git a/Tests/Helpers/TestState.swift b/Tests/Helpers/TestState.swift new file mode 100644 index 00000000..a1ccfbe8 --- /dev/null +++ b/Tests/Helpers/TestState.swift @@ -0,0 +1,13 @@ +import Foundation +import Mini + +struct TestState: State { + public let testTask: Task + public let counter: Int + + public init(testTask: Task = .idle(), + counter: Int = 0) { + self.testTask = testTask + self.counter = counter + } +} diff --git a/Tests/Helpers/TestStoreController.swift b/Tests/Helpers/TestStoreController.swift new file mode 100644 index 00000000..6772d1af --- /dev/null +++ b/Tests/Helpers/TestStoreController.swift @@ -0,0 +1,24 @@ +import Combine +import Foundation +import Mini +import XCTest + +class TestStoreController: Cancellable { + public func cancel() { + // NO-OP + } +} + +typealias TestStore = Store + +extension TestStore { + func reducerGroup(expectation: XCTestExpectation? = nil) -> ReducerGroup { + ReducerGroup { [ + Reducer(of: TestAction.self, on: self.dispatcher) { action in + self.state = TestState(testTask: .success(action.counter), counter: action.counter) + expectation?.fulfill() + } + ] + } + } +} diff --git a/Tests/KeyedTaskTests.swift b/Tests/KeyedTaskTests.swift new file mode 100644 index 00000000..c6fbea97 --- /dev/null +++ b/Tests/KeyedTaskTests.swift @@ -0,0 +1,94 @@ +@testable import Mini +import XCTest + +class KeyedTaskTests: XCTestCase { + var tasks: KeyedTask { + [0: .running(), + 1: .success("hi"), + 2: .failure(NSError(domain: "domain", code: 44, userInfo: nil)), + 3: .idle()] + } + + var emptyTasks: KeyedEmptyTask { + [0: .running(), + 1: .success(), + 2: .failure(NSError(domain: "domain", code: 44, userInfo: nil)), + 3: .idle()] + } + + func test_subscript() { + XCTAssertTrue(tasks[task: 1].payload == "hi") + XCTAssertEqual(emptyTasks[task: 1].isSuccessful, true) + XCTAssertEqual(tasks[task: 4].isIdle, true) + } + + 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)) + + XCTAssertTrue(emptyTasks.hasValue(for: 0)) + XCTAssertTrue(emptyTasks.hasValue(for: 1)) + XCTAssertTrue(emptyTasks.hasValue(for: 2)) + XCTAssertTrue(emptyTasks.hasValue(for: 3)) + XCTAssertFalse(emptyTasks.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() { + XCTAssertTrue(tasks.isRunning(key: 0)) + 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() { + XCTAssertFalse(tasks.isRecentlySucceeded(key: 0)) + 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() { + XCTAssertFalse(tasks.isTerminal(key: 0)) + 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() { + XCTAssertFalse(tasks.isSuccessful(key: 0)) + 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() { + XCTAssertFalse(tasks.isFailure(key: 0)) + XCTAssertFalse(tasks.isFailure(key: 1)) + XCTAssertTrue(tasks.isFailure(key: 2)) + XCTAssertFalse(tasks.isFailure(key: 3)) + XCTAssertFalse(tasks.isFailure(key: 4)) + } + + func test_isExpired_inside_a_keyedtask() { + XCTAssertFalse(tasks.isExpired(key: 0)) + XCTAssertFalse(tasks.isExpired(key: 1)) + XCTAssertFalse(tasks.isExpired(key: 2)) + XCTAssertFalse(tasks.isExpired(key: 3)) + XCTAssertFalse(tasks.isExpired(key: 4)) + } +} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index f13474b9..00000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,8 +0,0 @@ -import XCTest - -import MiniSwiftTests - -var tests = [XCTestCaseEntry]() -tests += MiniSwiftTests.__allTests() - -XCTMain(tests) diff --git a/Tests/MiniSwiftTests/ChainTests.swift b/Tests/MiniSwiftTests/ChainTests.swift deleted file mode 100644 index f6c5831f..00000000 --- a/Tests/MiniSwiftTests/ChainTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -import XCTest -@testable import MiniSwift - -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 - } - - let testAction = TestAction(property: 0) - - XCTAssert(testAction.mutableProperty == 0) - - let newAction = forwardingChain.proceed(testAction) as! TestAction - - XCTAssert(newAction.mutableProperty == 1) - - } -} diff --git a/Tests/MiniSwiftTests/Dictionary+ExtensionsTests.swift b/Tests/MiniSwiftTests/Dictionary+ExtensionsTests.swift deleted file mode 100644 index a71ef908..00000000 --- a/Tests/MiniSwiftTests/Dictionary+ExtensionsTests.swift +++ /dev/null @@ -1,32 +0,0 @@ -import XCTest -@testable import MiniSwift - -final class DictionaryExtensionsTests: XCTestCase { - - func test_get_or_put() { - - var dic = [String: Int]() - - XCTAssertEqual(dic.getOrPut("foo", defaultValue: 1), 1) - XCTAssertEqual(["foo": 1], dic) - - dic["bar"] = 2 - - XCTAssertEqual(dic.getOrPut("bar", defaultValue: Int.max), 2) - } - - func test_unrapping_subscript() { - - var dic = [String: Int]() - - let test: Int? = dic[unwrapping: "foo"] - - XCTAssertEqual(test, nil) - - dic["foo"] = 1 - - let test2: Int? = dic[unwrapping: "foo"] - - XCTAssertEqual(test2, 1) - } -} diff --git a/Tests/MiniSwiftTests/DispatcherTests.swift b/Tests/MiniSwiftTests/DispatcherTests.swift deleted file mode 100644 index 68449a16..00000000 --- a/Tests/MiniSwiftTests/DispatcherTests.swift +++ /dev/null @@ -1,98 +0,0 @@ -import XCTest -import RxSwift -@testable import MiniSwift - -final class DispatcherTests: XCTestCase { - - func test_subscription_count() { - - let dispatcher = Dispatcher() - let disposable = CompositeDisposable() - - XCTAssert(dispatcher.subscriptionCount == 0) - - _ = disposable.insert(dispatcher.subscribe { (_: OneTestAction) -> Void in }) - _ = disposable.insert(dispatcher.subscribe { (_: OneTestAction) -> Void in }) - - print(dispatcher.subscriptionCount) - - XCTAssert(dispatcher.subscriptionCount == 2) - - disposable.dispose() - - XCTAssert(dispatcher.subscriptionCount == 0) - } - - func test_add_remove_middleware() { - - let dispatcher = Dispatcher() - - let middleware = TestMiddleware() - - dispatcher.add(middleware: middleware) - - dispatcher.dispatch(OneTestAction(counter: 0), mode: .sync) - - XCTAssert(middleware.actions(of: OneTestAction.self).isEmpty == false) - - middleware.clear() - - XCTAssert(middleware.actions(of: OneTestAction.self).isEmpty == true) - - dispatcher.remove(middleware: middleware) - - dispatcher.dispatch(OneTestAction(counter: 0), mode: .sync) - - XCTAssert(middleware.actions(of: OneTestAction.self).isEmpty == true) - } - - func test_add_remove_service() { - - class TestService: Service { - - var id: UUID = UUID() - - var actions = [Action]() - - private let expectation: XCTestExpectation - - init(_ expectation: XCTestExpectation) { - self.expectation = expectation - } - - var perform: ServiceChain { - return { action, _ -> Void in - self.actions.append(action) - self.expectation.fulfill() - } - } - } - - let expectation = XCTestExpectation(description: "Service") - - let dispatcher = Dispatcher() - - let service = TestService(expectation) - - dispatcher.register(service: service) - - XCTAssert(service.actions.isEmpty == true) - - dispatcher.dispatch(OneTestAction(counter: 1), mode: .sync) - - wait(for: [expectation], timeout: 5.0) - - XCTAssert(service.actions.count == 1) - - XCTAssert(service.actions.contains(where: { $0 is OneTestAction }) == true) - - dispatcher.unregister(service: service) - - service.actions.removeAll() - - dispatcher.dispatch(OneTestAction(counter: 1), mode: .sync) - - XCTAssert(service.actions.isEmpty == true) - - } -} diff --git a/Tests/MiniSwiftTests/ReducerTests.swift b/Tests/MiniSwiftTests/ReducerTests.swift deleted file mode 100644 index 1ed82e0a..00000000 --- a/Tests/MiniSwiftTests/ReducerTests.swift +++ /dev/null @@ -1,115 +0,0 @@ -import XCTest -import RxSwift -@testable import MiniSwift - -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 { - - var reducerGroup: ReducerGroup { - return ReducerGroup { [ - Reducer(of: OneTestAction.self, on: self.dispatcher) { action in - self.state = TestState(testTask: .requestSuccess(), counter: action.counter) - } - ] - } - } -} - -final class ReducerTests: XCTestCase { - - func test_dispatcher_triggers_action_in_reducer_group_reducer() { - let dBag = DisposeBag() - let dispatcher = Dispatcher() - let store = Store(TestState(), dispatcher: dispatcher, storeController: TestStoreController()) - store - .reducerGroup - .disposed(by: dBag) - XCTAssertTrue(store.state.counter == 0) - dispatcher.dispatch( - OneTestAction(counter: 1), - mode: .sync - ) - XCTAssertTrue(store.state.counter == 1) - } - - func test_no_subscribe_to_store_produces_no_changes() { - let dispatcher = Dispatcher() - let store = Store(TestState(), dispatcher: dispatcher, storeController: TestStoreController()) - XCTAssertTrue(store.state.counter == 0) - dispatcher.dispatch( - OneTestAction(counter: 2), - mode: .sync - ) - XCTAssertTrue(store.state.counter == 0) - } - - func test_subscribe_to_store_receive_actions() { - let dBag = DisposeBag() - let dispatcher = Dispatcher() - let store = Store(TestState(), dispatcher: dispatcher, storeController: TestStoreController()) - XCTAssertTrue(store.state.counter == 0) - store - .reducerGroup - .disposed(by: dBag) - dispatcher.dispatch( - OneTestAction(counter: 2), - mode: .sync - ) - XCTAssertTrue(store.state.counter == 2) - } - - func test_reset_state() { - let dBag = DisposeBag() - let dispatcher = Dispatcher() - let initialState = TestState() - let store = Store(initialState, dispatcher: dispatcher, storeController: TestStoreController()) - XCTAssertTrue(store.state.counter == 0) - store - .reducerGroup - .disposed(by: dBag) - dispatcher.dispatch( - OneTestAction(counter: 3), - mode: .sync - ) - XCTAssertTrue(store.state.counter == 3) - store.reset() - XCTAssert(store.state.isEqual(to: initialState)) - } -} diff --git a/Tests/MiniSwiftTests/TaskTests.swift b/Tests/MiniSwiftTests/TaskTests.swift deleted file mode 100644 index 68578f20..00000000 --- a/Tests/MiniSwiftTests/TaskTests.swift +++ /dev/null @@ -1,71 +0,0 @@ -import XCTest -@testable import MiniSwift - -class TaskTests: XCTestCase { - let error = NSError(domain: "wawa", code: 69, userInfo: nil) - - func test_check_states_for_running_task() { - let task = Task.requestRunning() - - XCTAssertEqual(task.status, .running) - XCTAssertNil(task.error) - - XCTAssertTrue(task.isRunning) - XCTAssertFalse(task.isFailure) - XCTAssertFalse(task.isTerminal) - XCTAssertFalse(task.isSuccessful) - XCTAssertFalse(task.isRecentlySucceeded) - } - - func test_check_states_for_success_task() { - let task = Task.requestSuccess() - - XCTAssertEqual(task.status, .success) - XCTAssertNil(task.error) - - XCTAssertFalse(task.isRunning) - XCTAssertFalse(task.isFailure) - XCTAssertTrue(task.isTerminal) - XCTAssertTrue(task.isSuccessful) - } - - func test_check_states_for_failure_task() { - let task = Task.requestFailure(error) - - XCTAssertEqual(task.status, .failure) - XCTAssertEqual(task.error as NSError?, error) - - XCTAssertFalse(task.isRunning) - XCTAssertTrue(task.isFailure) - XCTAssertTrue(task.isTerminal) - XCTAssertFalse(task.isSuccessful) - XCTAssertFalse(task.isRecentlySucceeded) - } - - func test_data_and_progress() { - let data = "comiendo perritos calientes" - let progress: Decimal = 0.5 - let task = TypedTask(data: data, progress: progress) - - XCTAssertEqual(task.data, data) - XCTAssertEqual(task.progress, progress) - } - - func test_expiration_of_task_created_with_past_date() { - let task = Task(status: .success, started: Date.distantPast) - XCTAssertFalse(task.isRecentlySucceeded) - } - - func test_success_task_with_expiration_setted_to_immediately() { - let task = Task.requestSuccess(.immediately) - XCTAssertFalse(task.isRecentlySucceeded) - } - - func test_success_task_with_expiration_setted() { - let task = Task.requestSuccess(.custom(2)) - XCTAssertTrue(task.isRecentlySucceeded) - - Thread.sleep(forTimeInterval: 3) - XCTAssertFalse(task.isRecentlySucceeded) - } -} diff --git a/Tests/MiniSwiftTests/XCTestManifests.swift b/Tests/MiniSwiftTests/XCTestManifests.swift deleted file mode 100644 index 3954b49b..00000000 --- a/Tests/MiniSwiftTests/XCTestManifests.swift +++ /dev/null @@ -1,70 +0,0 @@ -#if !canImport(ObjectiveC) -import XCTest - -extension ChainTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__ChainTests = [ - ("test_forwarding_chain_forwards_action", test_forwarding_chain_forwards_action) - ] -} - -extension DictionaryExtensionsTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__DictionaryExtensionsTests = [ - ("test_get_or_put", test_get_or_put), - ("test_unrapping_subscript", test_unrapping_subscript) - ] -} - -extension DispatcherTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__DispatcherTests = [ - ("test_add_remove_middleware", test_add_remove_middleware), - ("test_add_remove_service", test_add_remove_service), - ("test_subscription_count", test_subscription_count) - ] -} - -extension ReducerTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__ReducerTests = [ - ("test_dispatcher_triggers_action_in_reducer_group_reducer", test_dispatcher_triggers_action_in_reducer_group_reducer), - ("test_no_subscribe_to_store_produces_no_changes", test_no_subscribe_to_store_produces_no_changes), - ("test_reset_state", test_reset_state), - ("test_subscribe_to_store_receive_actions", test_subscribe_to_store_receive_actions) - ] -} - -extension TaskTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__TaskTests = [ - ("test_check_states_for_failure_task", test_check_states_for_failure_task), - ("test_check_states_for_running_task", test_check_states_for_running_task), - ("test_check_states_for_success_task", test_check_states_for_success_task), - ("test_data_and_progress", test_data_and_progress), - ("test_expiration_of_task_created_with_past_date", test_expiration_of_task_created_with_past_date), - ("test_success_task_with_expiration_setted", test_success_task_with_expiration_setted), - ("test_success_task_with_expiration_setted_to_immediately", test_success_task_with_expiration_setted_to_immediately) - ] -} - -public func __allTests() -> [XCTestCaseEntry] { - return [ - testCase(ChainTests.__allTests__ChainTests), - testCase(DictionaryExtensionsTests.__allTests__DictionaryExtensionsTests), - testCase(DispatcherTests.__allTests__DispatcherTests), - testCase(ReducerTests.__allTests__ReducerTests), - testCase(TaskTests.__allTests__TaskTests) - ] -} -#endif diff --git a/Tests/OrderedSetTests.swift b/Tests/OrderedSetTests.swift new file mode 100644 index 00000000..20f93d0e --- /dev/null +++ b/Tests/OrderedSetTests.swift @@ -0,0 +1,72 @@ +@testable import Mini +import XCTest + +final class OrderedSetTests: XCTestCase { + func test_remove() { + let orderedSet = OrderedSet(initial: [1, 2, 3]) + + XCTAssertTrue(orderedSet.remove(2)) + + XCTAssertFalse(orderedSet.exists(2)) + + XCTAssertFalse(orderedSet.remove(2)) + } + + func test_insert() { + let orderedSet = OrderedSet(initial: [1, 2, 3]) + + orderedSet.insert([2, 2]) + + XCTAssertEqual(orderedSet.count, 3) + + orderedSet.insert(4) + + XCTAssertEqual(orderedSet.count, 4) + } + + func test_min_max() { + let orderedSet = OrderedSet(initial: []) + + XCTAssertNil(orderedSet.min) + XCTAssertNil(orderedSet.max) + + orderedSet.insert([1, 2]) + + XCTAssertEqual(orderedSet.min, 1) + XCTAssertEqual(orderedSet.max, 2) + } + + func test_enumerated() { + let orderedSet = OrderedSet(initial: [1, 2]) + + var result = [Int]() + orderedSet.enumerated().forEach { element in + result.append(element.element) + } + + XCTAssertEqual(result, [1, 2]) + } + + func test_subscript() { + let orderedSet = OrderedSet(initial: [1, 2]) + XCTAssertEqual(orderedSet[0], 1) + } + + func test_klargest() { + let orderedSet = OrderedSet(initial: [1, 2, 3, 4, 5, 6, 7]) + + XCTAssertEqual(orderedSet.kLargest(element: 0), nil) + XCTAssertEqual(orderedSet.kLargest(element: 4), 4) + XCTAssertEqual(orderedSet.kLargest(element: 3), 5) + XCTAssertEqual(orderedSet.kLargest(element: 2), 6) + } + + func test_ksmallest() { + let orderedSet = OrderedSet(initial: [1, 2, 3, 4, 5, 6, 7]) + + XCTAssertEqual(orderedSet.kSmallest(element: 0), nil) + XCTAssertEqual(orderedSet.kSmallest(element: 4), 4) + XCTAssertEqual(orderedSet.kSmallest(element: 3), 3) + XCTAssertEqual(orderedSet.kSmallest(element: 2), 2) + } +} diff --git a/Tests/PublishersTests+CombineMiniTasks.swift b/Tests/PublishersTests+CombineMiniTasks.swift new file mode 100644 index 00000000..c45f9fbd --- /dev/null +++ b/Tests/PublishersTests+CombineMiniTasks.swift @@ -0,0 +1,255 @@ +import Combine +@testable import Mini +import XCTest + +extension PublishersTests { + // Tuple2 + + func test_combining_tuple_of_2_with_2_idle() { + var cancellables = Set() + let expectation = expectation(description: "test_combining_tuple_of_2_with_2_idle") + expectation.expectedFulfillmentCount = 1 + + 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: "test_combining_tuple_of_2_with_2_success") + expectation.expectedFulfillmentCount = 1 + + 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: "test_combining_tuple_of_2_with_1_success_1_running") + expectation.expectedFulfillmentCount = 1 + + 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: "test_combining_tuple_of_2_with_1_success_1_failure") + expectation.expectedFulfillmentCount = 1 + + 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_3_success() { + var cancellables = Set() + let expectation = expectation(description: "test_combining_tuple_of_3_with_3_success") + expectation.expectedFulfillmentCount = 1 + + Publishers + .CombineLatest3(Just(taskSuccess1), Just(taskSuccess2), Just(taskSuccess3)) + .combineMiniTasks() + .sink { combinedTask in + XCTAssertTrue(combinedTask.isSuccessful) + XCTAssertEqual(combinedTask.payload, .init("hola", "chau", "adios")) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_combining_tuple_of_3_with_2_success_1_failure() { + var cancellables = Set() + let expectation = expectation(description: "test_combining_tuple_of_3_with_2_success_1_failure") + expectation.expectedFulfillmentCount = 1 + + 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: "test_combining_tuple_of_3_with_2_success_1_running") + expectation.expectedFulfillmentCount = 1 + + 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: "test_combining_tuple_of_4_with_2_success_1_failure_1_running") + expectation.expectedFulfillmentCount = 1 + + 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: "test_combining_tuple_of_4_with_2_success_2_failure") + expectation.expectedFulfillmentCount = 1 + + 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: "test_combining_tuple_of_4_with_4_success") + expectation.expectedFulfillmentCount = 1 + + 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: "test_combining_two_success_in_array") + expectation.expectedFulfillmentCount = 1 + + 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: "test_combining_one_success_one_failure_in_array") + expectation.expectedFulfillmentCount = 1 + + 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: "test_combining_one_success_one_running_in_array") + expectation.expectedFulfillmentCount = 1 + + 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: "test_combining_idle_tasks_in_array") + expectation.expectedFulfillmentCount = 1 + + 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/PublishersTests+EraseToEmptyTask.swift b/Tests/PublishersTests+EraseToEmptyTask.swift new file mode 100644 index 00000000..d2971ce2 --- /dev/null +++ b/Tests/PublishersTests+EraseToEmptyTask.swift @@ -0,0 +1,72 @@ +import Combine +@testable import Mini +import XCTest + +extension PublishersTests { + // EraseToEmptyTask + + func test_erase_to_empty_task_when_task_is_idle() { + var cancellables = Set() + let expectation = expectation(description: "test_erase_to_empty_task_when_task_is_idle") + expectation.expectedFulfillmentCount = 1 + + Just(taskIdle1) + .eraseToEmptyTask() + .sink { task in + XCTAssertTrue(task.isIdle) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_erase_to_empty_task_when_task_is_running() { + var cancellables = Set() + let expectation = expectation(description: "test_erase_to_empty_task_when_task_is_running") + expectation.expectedFulfillmentCount = 1 + + Just(taskRunning1) + .eraseToEmptyTask() + .sink { task in + XCTAssertTrue(task.isRunning) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_erase_to_empty_task_when_task_is_success() { + var cancellables = Set() + let expectation = expectation(description: "test_erase_to_empty_task_when_task_is_success") + expectation.expectedFulfillmentCount = 1 + + Just(taskSuccess1) + .eraseToEmptyTask() + .sink { task in + XCTAssertTrue(task.isSuccessful) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_erase_to_empty_task_when_task_is_failure() { + var cancellables = Set() + let expectation = expectation(description: "test_erase_to_empty_task_when_task_is_failure") + expectation.expectedFulfillmentCount = 1 + + Just(taskFailure1) + .eraseToEmptyTask() + .sink { task in + XCTAssertTrue(task.isFailure) + XCTAssertEqual(task.error?.localizedDescription, self.taskFailure1.error?.localizedDescription) + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } +} diff --git a/Tests/PublishersTests+MapToLatestTask.swift b/Tests/PublishersTests+MapToLatestTask.swift new file mode 100644 index 00000000..cd88aa08 --- /dev/null +++ b/Tests/PublishersTests+MapToLatestTask.swift @@ -0,0 +1,176 @@ +import Combine +@testable import Mini +import XCTest + +extension PublishersTests { + func test_flatmap_identifiable_task_to_severals_successes_when_original_task_emits_severals_tasks() { + var cancellables = Set() + let expectationSuccess = expectation(description: "wait for async process - Success") + expectationSuccess.expectedFulfillmentCount = 2 + let expectationFailure = expectation(description: "wait for async process - Failure") + expectationFailure.expectedFulfillmentCount = 3 + let expectationRunning = expectation(description: "wait for async process - Running") + expectationRunning.expectedFulfillmentCount = 1 + let expectationIdle = expectation(description: "wait for async process - Idle") + expectationIdle.expectedFulfillmentCount = 1 + + let triggerSubject = PassthroughSubject, Never>() + let internalSubject = PassthroughSubject, Never>() + + triggerSubject + .mapToLatestTask { _ in + internalSubject + .eraseToAnyPublisher() + } + .sink { task in + switch task.status { + case .success: + expectationSuccess.fulfill() + + case .idle: + expectationIdle.fulfill() + + case .failure: + expectationFailure.fulfill() + + case .running: + expectationRunning.fulfill() + } + } + .store(in: &cancellables) + + triggerSubject.send(taskIdentifiableIdle1) // Pass- Hit IDLE! + triggerSubject.send(taskIdentifiableRunning1) // Pass- Hit RUNNING! + triggerSubject.send(taskIdentifiableSuccess1) // Connect subject + internalSubject.send(taskSuccess2) // Hit SUCCESS! + internalSubject.send(taskFailure1) // Hit FAILURE! + triggerSubject.send(taskIdentifiableFailure1) // Pass- Hit FAILURE! + triggerSubject.send(taskIdentifiableFailure2) // Pass- Hit FAILURE! + internalSubject.send(taskFailure1) // Ignored + internalSubject.send(taskFailure1) // Ignored + internalSubject.send(taskFailure1) // Ignored + triggerSubject.send(taskIdentifiableSuccess2) // Connect subject + internalSubject.send(taskSuccess1) // Hit SUCCESS! + + waitForExpectations(timeout: 2) + } + + func test_flatmap_identifiable_task_to_success_when_original_task_is_success() { + var cancellables = Set() + let expectation = expectation(description: "test_flatmap_identifiable_task_to_success_when_original_task_is_success") + expectation.expectedFulfillmentCount = 1 + + Just(taskIdentifiableSuccess1) // Emits a task with an Id="uno" + .mapToLatestTask { id in + Just(self.taskSuccess(value: id)) // This task concats success with received value (id) + .eraseToAnyPublisher() + } + .sink { task in + XCTAssertTrue(task.isSuccessful) + XCTAssertEqual(task.payload, "success:uno") + expectation.fulfill() + } + .store(in: &cancellables) + + waitForExpectations(timeout: 2) + } + + func test_flatmap_identifiable_task_to_severals_successes_when_original_task_is_success() { + var cancellables = Set() + let expectation = expectation(description: "test_flatmap_identifiable_task_to_severals_successes_when_original_task_is_success") + expectation.expectedFulfillmentCount = 2 + + let subject = PassthroughSubject, Never>() + + Just(taskIdentifiableSuccess1) // Emits a task with an Id="uno" + .mapToLatestTask { _ in + subject + .eraseToAnyPublisher() + } + .sink { task in + XCTAssertTrue(task.isSuccessful) + expectation.fulfill() + } + .store(in: &cancellables) + + subject.send(taskSuccess1) + subject.send(taskSuccess2) + + waitForExpectations(timeout: 2) + } + + func test_flatmap_identifiable_task_to_severals_successes_when_original_task_is_failure() { + var cancellables = Set() + let expectation = expectation(description: "test_flatmap_identifiable_task_to_severals_successes_when_original_task_is_failure") + expectation.expectedFulfillmentCount = 1 + + let subject = PassthroughSubject, Never>() + + Just(taskIdentifiableFailure1) + .mapToLatestTask { _ in + subject + .eraseToAnyPublisher() + } + .sink { task in + XCTAssertTrue(task.isFailure) + expectation.fulfill() + } + .store(in: &cancellables) + + // These changes are omited because the trigger task is on a failure state + subject.send(taskSuccess1) + subject.send(taskSuccess2) + + waitForExpectations(timeout: 2) + } + + func test_flatmap_identifiable_task_to_severals_successes_when_original_task_is_running() { + var cancellables = Set() + let expectation = expectation(description: "test_flatmap_identifiable_task_to_severals_successes_when_original_task_is_running") + expectation.expectedFulfillmentCount = 1 + + let subject = PassthroughSubject, Never>() + + Just(taskIdentifiableRunning1) + .mapToLatestTask { _ in + subject + .eraseToAnyPublisher() + } + .sink { task in + XCTAssertTrue(task.isRunning) + expectation.fulfill() + } + .store(in: &cancellables) + + // These changes are omited because the trigger task is on a failure state + subject.send(taskSuccess1) + subject.send(taskSuccess2) + + waitForExpectations(timeout: 2) + } + + func test_flatmap_identifiable_task_to_severals_successes_when_original_task_is_idle() { + var cancellables = Set() + let expectation = expectation(description: "test_flatmap_identifiable_task_to_severals_successes_when_original_task_is_idle") + expectation.expectedFulfillmentCount = 1 + + let subject = PassthroughSubject, Never>() + + Just(taskIdentifiableIdle1) + .mapToLatestTask { _ in + subject + .eraseToAnyPublisher() + } + .sink { task in + XCTAssertTrue(task.isIdle) + expectation.fulfill() + } + .store(in: &cancellables) + + // These changes are omited because the trigger task is on a failure state + subject.send(taskSuccess1) + subject.send(taskSuccess2) + + waitForExpectations(timeout: 2) + } +} diff --git a/Tests/PublishersTests+RemoveExpired.swift b/Tests/PublishersTests+RemoveExpired.swift new file mode 100644 index 00000000..4c435789 --- /dev/null +++ b/Tests/PublishersTests+RemoveExpired.swift @@ -0,0 +1,32 @@ +import Combine +@testable import Mini +import XCTest + +extension PublishersTests { + func test_remove_expired() { + var cancellables = Set() + let expectation = expectation(description: "test_remove_expired") + expectation.expectedFulfillmentCount = 1 + + let subject = PassthroughSubject, Never>() + + let margin: TimeInterval = 0.500 + + subject + .removeExpired(margin: margin) // Filter the 2 expired task + .removeDuplicates() // Pass only the first success task because the expired they never get here! + .sink { task in + XCTAssertFalse(task.isExpired(margin: margin)) + expectation.fulfill() + } + .store(in: &cancellables) + + // Send 2 unexpired and 2 expired: + subject.send(taskSuccess1) + subject.send(taskSuccessExpired) + subject.send(taskFailureExpired) + subject.send(taskSuccess1) + + waitForExpectations(timeout: 2) + } +} diff --git a/Tests/PublishersTests.swift b/Tests/PublishersTests.swift new file mode 100644 index 00000000..89b1a5bf --- /dev/null +++ b/Tests/PublishersTests.swift @@ -0,0 +1,24 @@ +import Combine +@testable import Mini +import XCTest + +class PublishersTests: XCTestCase { + static var payload1 = TestPayload(id: "uno", value: 4) + static var payload2 = TestPayload(id: "dos", value: 6) + var taskIdentifiableSuccess1: Task = .success(payload1) + var taskIdentifiableSuccess2: Task = .success(payload2) + var taskIdentifiableFailure1: Task = .failure(.berenjenaError) + var taskIdentifiableFailure2: Task = .failure(.bigBerenjenaError) + var taskIdentifiableRunning1: Task = .running() + var taskIdentifiableIdle1: Task = .idle() + func taskSuccess(value: String) -> Task { .success("success:\(value)") } + var taskSuccessExpired: Task = .success("hola viejo", started: Date() - 1_000, expiration: .immediately) + var taskSuccess1: Task = .success("hola") + var taskSuccess2: Task = .success("chau") + var taskSuccess3: Task = .success("adios") + var taskFailure1: Task = .failure(.berenjenaError) + var taskFailureExpired: Task = .failure(.berenjenaError, started: Date() - 1_000) + var taskFailure2: Task = .failure(.bigBerenjenaError) + var taskRunning1: Task = .running() + var taskIdle1: Task = .idle() +} diff --git a/Tests/ReducerTests.swift b/Tests/ReducerTests.swift new file mode 100644 index 00000000..a2701a1f --- /dev/null +++ b/Tests/ReducerTests.swift @@ -0,0 +1,163 @@ +import Combine +@testable import Mini +import XCTest + +final class ReducerTests: XCTestCase { + func test_dispatcher_triggers_action_in_reducer_group_reducer() { + var cancellables = Set() + let dispatcher = Dispatcher() + let store = Store(TestState(), dispatcher: dispatcher, storeController: TestStoreController()) + let expectation = XCTestExpectation(description: "Reducer") + store + .reducerGroup(expectation: expectation) + .store(in: &cancellables) + + XCTAssertTrue(store.state.counter == 0) + + dispatcher.dispatch(TestAction(counter: 1)) + wait(for: [expectation], timeout: 5.0) + + XCTAssertTrue(store.state.counter == 1) + } + + func test_no_subscribe_to_store_produces_no_changes() { + let dispatcher = Dispatcher() + let store = Store(TestState(), dispatcher: dispatcher, storeController: TestStoreController()) + + XCTAssertTrue(store.state.counter == 0) + + dispatcher.dispatch(TestAction(counter: 2)) + + XCTAssertTrue(store.state.counter == 0) + } + + func test_subscribe_to_store_receive_actions() { + var cancellables = Set() + let dispatcher = Dispatcher() + let store = Store(TestState(), dispatcher: dispatcher, storeController: TestStoreController()) + let expectation = XCTestExpectation(description: "Reducer") + store + .reducerGroup(expectation: expectation) + .store(in: &cancellables) + + XCTAssertTrue(store.state.counter == 0) + + dispatcher.dispatch(TestAction(counter: 2)) + wait(for: [expectation], timeout: 5.0) + + XCTAssertTrue(store.state.counter == 2) + } + + func test_reset_state() { + 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) + .store(in: &cancellables) + + XCTAssertTrue(store.state.counter == 0) + + dispatcher.dispatch(TestAction(counter: 3)) + wait(for: [expectation], timeout: 5.0) + + XCTAssertTrue(store.state.counter == 3) + + store.reset() + XCTAssertEqual(store.state, initialState) + } + + func test_subscribe_state_changes() { + var cancellables = Set() + let dispatcher = Dispatcher() + let initialState = TestState() + let store = Store(initialState, dispatcher: dispatcher, storeController: TestStoreController()) + let expectation1 = XCTestExpectation(description: "Subscription Emits 1") + let expectation2 = XCTestExpectation(description: "Subscription Emits 2") + + store + .reducerGroup() + .store(in: &cancellables) + + store + .map(\.counter) + .sink { counter in + if counter == 1 { + expectation1.fulfill() + } + if counter == 2 { + expectation2.fulfill() + } + } + .store(in: &cancellables) + + dispatcher.dispatch(TestAction(counter: 1)) + dispatcher.dispatch(TestAction(counter: 2)) + wait(for: [expectation1, expectation2], timeout: 5.0) + } + + func test_subscribe_state_changes_without_initial_value() { + var cancellables = Set() + let dispatcher = Dispatcher() + let initialState = TestState() + let store = Store(initialState, dispatcher: dispatcher, storeController: TestStoreController(), emitsInitialValue: false) + let expectation = XCTestExpectation(description: "Subscription Emits") + + store + .reducerGroup() + .store(in: &cancellables) + + dispatcher.dispatch(TestAction(counter: 1)) + + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + // Only gets the action with counter == 2. + store + .map(\.counter) + .sink { counter in + if counter == 1 { + XCTFail("counter == 1 should not be emmited because this is a stateless subscription") + } + if counter == 2 { + expectation.fulfill() + } + } + .store(in: &cancellables) + + // Send action with counter == 2, this action should be caught by the two subscriptions + dispatcher.dispatch(TestAction(counter: 2)) + } + + wait(for: [expectation], timeout: 5.0) + } + + func test_scope() { + var cancellables = Set() + let dispatcher = Dispatcher() + let initialState = TestState() + let store = Store(initialState, dispatcher: dispatcher, storeController: TestStoreController()) + let expectation1 = XCTestExpectation(description: "Subscription Emits 1") + + store + .reducerGroup() + .store(in: &cancellables) + + dispatcher.dispatch(TestAction(counter: 1)) + + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + store + .scope { $0.testTask } + .sink { task in + XCTAssertEqual(task.payload, 2) // Only get 2 because we scope the suscription to task + // on the state and receive non expired and unique values. + expectation1.fulfill() + } + .store(in: &cancellables) + + dispatcher.dispatch(TestAction(counter: 2)) + } + + wait(for: [expectation1], timeout: 5.0) + } +} diff --git a/Tests/SharedDictionaryTests.swift b/Tests/SharedDictionaryTests.swift new file mode 100644 index 00000000..7ce8d099 --- /dev/null +++ b/Tests/SharedDictionaryTests.swift @@ -0,0 +1,19 @@ +@testable import Mini +import XCTest + +class SharedDictionaryTests: XCTestCase { + func test_init() { + let sharedDictionary = SharedDictionary() + + XCTAssertEqual(sharedDictionary.innerDictionary, [:]) + } + + func test_getorput() { + let sharedDictionary = SharedDictionary() + + let value = sharedDictionary.safeValue("a") { 4 } + + XCTAssertEqual(value, sharedDictionary.value(withKey: "a")) + XCTAssertEqual(sharedDictionary.innerDictionary, ["a": 4]) + } +} diff --git a/Tests/TaskTests.swift b/Tests/TaskTests.swift new file mode 100644 index 00000000..1b91b478 --- /dev/null +++ b/Tests/TaskTests.swift @@ -0,0 +1,130 @@ +@testable import Mini +import XCTest + +class TaskTests: XCTestCase { + let error = NSError(domain: "wawa", code: 69, userInfo: nil) + + func test_description() { + let task1: Task = .running() + + XCTAssertEqual(task1.debugDescription, "\(task1)") + + let task2: Task = .running(tag: "a") + + XCTAssertEqual(task2.debugDescription, "\(task2)") + } + + func test_check_states_for_running_task() { + let task: Task = .running() + + XCTAssertEqual(task.status, .running) + XCTAssertNil(task.error) + + XCTAssertTrue(task.isRunning) + XCTAssertFalse(task.isFailure) + XCTAssertFalse(task.isTerminal) + XCTAssertFalse(task.isSuccessful) + XCTAssertFalse(task.isRecentlySucceeded) + XCTAssertFalse(task.isExpired) + } + + func test_check_states_for_success_task() { + let task: Task = .success(5) + + XCTAssertEqual(task.status, .success(payload: 5)) + XCTAssertEqual(task.payload, 5) + XCTAssertNil(task.error) + + XCTAssertFalse(task.isRunning) + XCTAssertFalse(task.isFailure) + XCTAssertTrue(task.isTerminal) + XCTAssertTrue(task.isSuccessful) + XCTAssertFalse(task.isExpired) + } + + func test_check_states_for_failure_task() { + let task: Task = .failure(error) + + XCTAssertEqual(task.status, .failure(error: error)) + XCTAssertNil(task.payload) + XCTAssertEqual(task.error, error) + + XCTAssertFalse(task.isRunning) + XCTAssertTrue(task.isFailure) + XCTAssertTrue(task.isTerminal) + XCTAssertFalse(task.isSuccessful) + XCTAssertFalse(task.isRecentlySucceeded) + XCTAssertFalse(task.isExpired) + } + + func test_check_expiration_for_custom() { + let expectation = expectation(description: "wait for async process") + expectation.expectedFulfillmentCount = 2 + + let task: Task = .success("hola", expiration: .custom(3)) + + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + XCTAssertFalse(task.isExpired) + expectation.fulfill() + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 4) { + XCTAssertTrue(task.isExpired) + expectation.fulfill() + } + + waitForExpectations(timeout: 5) + } + + func test_check_expiration_for_immediately() { + let expectation = expectation(description: "wait for async process") + expectation.expectedFulfillmentCount = 2 + + let task: Task = .success("hola", expiration: .immediately) + + DispatchQueue.main.asyncAfter(deadline: .now()) { + XCTAssertFalse(task.isExpired) + expectation.fulfill() + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + XCTAssertTrue(task.isExpired) + expectation.fulfill() + } + + waitForExpectations(timeout: 3) + } + + func test_data_and_progress() { + let payload = "comiendo perritos calientes" + let progress: Decimal = 0.5 + let task: Task = .success(payload, progress: progress) + + XCTAssertEqual(task.payload, payload) + XCTAssertEqual(task.progress, progress) + } + + func test_success_task_with_payload() { + let task: Task = .success("hola") + + XCTAssertEqual(task.payload, "hola") + } + + func test_expiration_of_task_created_with_past_date() { + let task: Task = .success("55", started: Date.distantPast) + XCTAssertFalse(task.isRecentlySucceeded) + } + + func test_success_task_with_expiration_setted_to_immediately() { + let task: Task = .success(6, expiration: .immediately) + XCTAssertFalse(task.isRecentlySucceeded) + } + + func test_success_task_with_expiration_setted() { + let task: Task = .success(66, expiration: .custom(2)) + XCTAssertTrue(task.isRecentlySucceeded) + + Thread.sleep(forTimeInterval: 3) + XCTAssertFalse(task.isRecentlySucceeded) + } +} diff --git a/bin/pre-commit.sh b/bin/pre-commit.sh deleted file mode 100755 index 96b4229d..00000000 --- a/bin/pre-commit.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# - -LINT=$(which swiftlint) - -EXITSTATUS=0 - -if [[ -e "${LINT}" ]]; then - echo "SwiftLint Start..." -else - echo "🚨 SwiftLint does not exist, download from https://github.com/realm/SwiftLint" - exit 1 -fi - -RESULT=$($LINT lint --quiet) - -if [ "$RESULT" == '' ]; then - printf "✅ SwiftLint Finished.\n" -else - echo "" - printf "❌❌❌ SwiftLint Failed. Please check below:\n" - - while read -r line; do - - FILEPATH=$(echo $line | cut -d : -f 1) - L=$(echo $line | cut -d : -f 2) - C=$(echo $line | cut -d : -f 3) - TYPE=$(echo $line | cut -d : -f 4 | cut -c 2-) - MESSAGE=$(echo $line | cut -d : -f 5 | cut -c 2-) - DESCRIPTION=$(echo $line | cut -d : -f 6 | cut -c 2-) - if [ "$TYPE" == 'error' ]; then - printf "\n☠️ $TYPE \n" - EXITSTATUS=1 - elif [ "$TYPE" == 'warning' ]; then - printf "\n⚠️ $TYPE \n" - EXITSTATUS=1 - else - printf "\nℹ️ $TYPE \n" - fi - printf "➡️ $FILEPATH:$L:$C: \n" - printf "🗒 $MESSAGE - $DESCRIPTION\n" - done <<< "$RESULT" - - printf "\nCOMMIT ABORTED. Please fix them before commiting.\n" - - exit $EXITSTATUS -fi \ No newline at end of file diff --git a/fastlane/Fastfile b/fastlane/Fastfile deleted file mode 100644 index e13ef136..00000000 --- a/fastlane/Fastfile +++ /dev/null @@ -1,20 +0,0 @@ -default_platform(:ios) - -platform :ios do - desc "Pass all test for main target" - lane :pass_tests do - Dir.chdir('..') do - clear_derived_data - sh "xcodebuild -scheme Mini-Package -enableCodeCoverage YES clean test -destination 'platform=iOS Simulator,name=iPhone 8,OS=12.1' | xcpretty -f `xcpretty-travis-formatter`" - sh "curl -s https://codecov.io/bash | bash -s -- -F ios -J 'Mini'" - - clear_derived_data - sh "xcodebuild -scheme Mini-Package -enableCodeCoverage YES clean test -destination 'platform=OS X,arch=x86_64' | xcpretty -f `xcpretty-travis-formatter`" - sh "curl -s https://codecov.io/bash | bash -s -- -F mac -J 'Mini'" - - clear_derived_data - sh "xcodebuild -scheme Mini-Package -enableCodeCoverage YES clean test -destination 'platform=tvOS Simulator,name=Apple TV,OS=12.1' | xcpretty -f `xcpretty-travis-formatter`" - sh "curl -s https://codecov.io/bash | bash -s -- -F tv -J 'Mini'" - end - end -end diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile deleted file mode 100644 index 138e9e12..00000000 --- a/fastlane/Pluginfile +++ /dev/null @@ -1,6 +0,0 @@ -# Autogenerated by fastlane -# -# Ensure this file is checked in to source control! - -gem 'fastlane-plugin-trainer' -gem "fastlane-plugin-test_scheme", git: "https://github.com/bq/fastlane-plugin-test_scheme" \ No newline at end of file diff --git a/fastlane/README.md b/fastlane/README.md deleted file mode 100644 index d080618c..00000000 --- a/fastlane/README.md +++ /dev/null @@ -1,29 +0,0 @@ -fastlane documentation -================ -# Installation - -Make sure you have the latest version of the Xcode command line tools installed: - -``` -xcode-select --install -``` - -Install _fastlane_ using -``` -[sudo] gem install fastlane -NV -``` -or alternatively using `brew cask install fastlane` - -# Available Actions -## iOS -### ios pass_tests -``` -fastlane ios pass_tests -``` -Pass all test for main target - ----- - -This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. -More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). -The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).