├── .circleci
└── config.yml
├── .gitignore
├── .gitmodules
├── .swiftlint.yml
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── CHANGELOG.md
├── CONDUCT.md
├── CONTRIBUTING.md
├── Cartfile
├── Cartfile.resolved
├── ISSUE_TEMPLATE.md
├── LICENSE
├── Package.swift
├── Playground
└── RxSwiftExtPlayground.playground
│ ├── Contents.o
│ ├── Pages
│ ├── Index.xcplaygroundpage
│ │ └── Contents.swift
│ ├── UIScrollView.reachedBottom.xcplaygroundpage
│ │ └── Contents.swift
│ ├── UIViewPropertyAnimator.animate.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── UIViewPropertyAnimator.fractionComplete.xcplaygroundpage
│ │ └── Contents.swift
│ ├── and.xcplaygroundpage
│ │ └── Contents.swift
│ ├── apply.xcplaygroundpage
│ │ └── Contents.swift
│ ├── bufferWithTrigger.xcplaygroundpage
│ │ └── Contents.swift
│ ├── cascade.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── catchErrorJustComplete.xcplaygroundpage
│ │ └── Contents.swift
│ ├── count.xcplaygroundpage
│ │ └── Contents.swift
│ ├── distinct.xcplaygroundpage
│ │ └── Contents.swift
│ ├── filterMap.xcplaygroundpage
│ │ └── Contents.swift
│ ├── fromAsync.xcplaygroundpage
│ │ └── Contents.swift
│ ├── ignore.xcplaygroundpage
│ │ └── Contents.swift
│ ├── mapAt.xcplaygroundpage
│ │ └── Contents.swift
│ ├── mapMany.xcplaygroundpage
│ │ └── Contents.swift
│ ├── mapTo.xcplaygroundpage
│ │ └── Contents.swift
│ ├── mergeWith.xcplaygroundpage
│ │ └── Contents.swift
│ ├── not.xcplaygroundpage
│ │ └── Contents.swift
│ ├── nwise.xcplaygroundpage
│ │ └── Contents.swift
│ ├── ofType.xcplaygroundpage
│ │ └── Contents.swift
│ ├── once.xcplaygroundpage
│ │ └── Contents.swift
│ ├── partition.xcplaygroundpage
│ │ └── Contents.swift
│ ├── pausable.xcplaygroundpage
│ │ └── Contents.swift
│ ├── pausableBuffered.xcplaygroundpage
│ │ └── Contents.swift
│ ├── repeatWithBehavior.xcplaygroundpage
│ │ └── Contents.swift
│ ├── retryWithBehavior.xcplaygroundpage
│ │ └── Contents.swift
│ ├── toSortedArray.xcplaygroundpage
│ │ └── Contents.swift
│ ├── unwrap.xcplaygroundpage
│ │ └── Contents.swift
│ ├── withUnretained.xcplaygroundpage
│ │ └── Contents.swift
│ └── zipWith.xcplaygroundpage
│ │ └── Contents.swift
│ ├── Sources
│ └── SupportCode.swift
│ ├── SupportCode.o
│ ├── SupportCode.remap
│ ├── contents.xcplayground
│ └── playground.xcworkspace
│ └── contents.xcworkspacedata
├── Readme.md
├── RxSwiftExt.podspec
├── RxSwiftExt.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── xcshareddata
│ └── xcschemes
│ ├── RxSwiftExt-iOS.xcscheme
│ ├── RxSwiftExt-macOS.xcscheme
│ ├── RxSwiftExt-tvOS.xcscheme
│ ├── RxSwiftExtPlayground.xcscheme
│ ├── RxSwiftExtTests-iOS.xcscheme
│ ├── RxSwiftExtTests-macOS.xcscheme
│ └── RxSwiftExtTests-tvOS.xcscheme
├── RxSwiftExt.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── Source
├── Info.plist
├── RxCocoa
│ ├── UIScrollView+reachedBottom.swift
│ ├── UIViewPropertyAnimator+Rx.swift
│ ├── distinct+RxCocoa.swift
│ ├── mapTo+RxCocoa.swift
│ ├── not+RxCocoa.swift
│ ├── partition+RxCocoa.swift
│ └── unwrap+SharedSequence.swift
├── RxSwift
│ ├── ObservableType+Weak.swift
│ ├── and.swift
│ ├── apply.swift
│ ├── bufferWithTrigger.swift
│ ├── cascade.swift
│ ├── catchErrorJustComplete.swift
│ ├── count.swift
│ ├── distinct.swift
│ ├── filterMap.swift
│ ├── fromAsync.swift
│ ├── ignore.swift
│ ├── ignoreErrors.swift
│ ├── ignoreWhen.swift
│ ├── mapAt.swift
│ ├── mapMany.swift
│ ├── mapTo.swift
│ ├── materialized+elements.swift
│ ├── mergeWith.swift
│ ├── not.swift
│ ├── nwise.swift
│ ├── ofType.swift
│ ├── once.swift
│ ├── partition.swift
│ ├── pausable.swift
│ ├── pausableBuffered.swift
│ ├── repeatWithBehavior.swift
│ ├── retryWithBehavior.swift
│ ├── toSortedArray.swift
│ ├── unwrap.swift
│ └── zipWith.swift
└── Tools
│ ├── Observable+Alias.swift
│ └── curry.swift
├── Tests
├── Info.plist
├── RxCocoa
│ ├── DistinctTests+RxCocoa.swift
│ ├── MapToTests+RxCocoa.swift
│ ├── NotTests+RxCocoa.swift
│ ├── PartitionTests+RxCocoa.swift
│ ├── UIScrollView+reachedBottomTests.swift
│ ├── UIViewPropertyAnimatorTests+Rx.swift
│ └── unrwapTests+SharedSequence.swift
├── RxSwift
│ ├── BufferWithTriggerTests.swift
│ ├── CountTests.swift
│ ├── DistinctTests.swift
│ ├── IgnoreTests.swift
│ ├── MapAtTests.swift
│ ├── MapManyTests.swift
│ ├── MergeWithTests.swift
│ ├── Observable+OfTypeTests.swift
│ ├── OnceTests.swift
│ ├── PartitionTests.swift
│ ├── ToSortedArrayTests.swift
│ ├── UnwrapTests.swift
│ ├── WeakTarget.swift
│ ├── WeakTests.swift
│ ├── ZipWithTest.swift
│ ├── andTests.swift
│ ├── applyTests.swift
│ ├── cascadeTests.swift
│ ├── catchErrorJustCompleteTests.swift
│ ├── filterMapTests.swift
│ ├── fromAsyncTests.swift
│ ├── ignoreErrorsTests.swift
│ ├── ignoreWhenTests.swift
│ ├── mapToTests.swift
│ ├── materialized+elementsTests.swift
│ ├── notTests.swift
│ ├── nwiseTests.swift
│ ├── pausableBufferedTests.swift
│ ├── pausableTests.swift
│ ├── repeatWithBehaviorTests.swift
│ └── retryWithBehaviorTests.swift
└── TestErrors.swift
└── scripts
├── bootstrap-if-needed.sh
└── bootstrap.sh
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | "RxSwiftExt Tests":
4 | working_directory: ~/RxSwiftCommunity/RxSwiftExt
5 | parallelism: 1
6 | shell: /bin/bash --login
7 | environment:
8 | XCODE_TEST_REPORTS: /tmp/xcode-test-results
9 | LANG: en_US.UTF-8
10 | macos:
11 | xcode: '12.3.0'
12 | steps:
13 | - checkout
14 | - run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS $XCODE_TEST_REPORTS
15 | - restore_cache:
16 | keys:
17 | - v1-dep-{{ .Branch }}-
18 | - v1-dep-master-
19 | - v1-dep-
20 | - run:
21 | name: Bootstrap Carthage
22 | command: scripts/bootstrap-if-needed.sh
23 | - save_cache:
24 | key: v1-dep-{{ .Branch }}-{{ epoch }}
25 | paths:
26 | - Carthage
27 | - run:
28 | name: Test macOS
29 | command: set -o pipefail && xcodebuild test -scheme RxSwiftExt-macOS -workspace RxSwiftExt.xcworkspace -sdk macosx -destination "arch=x86_64" | tee "${XCODE_TEST_REPORTS}/xcode_macOS.log" | xcpretty -c -r html --output $XCODE_TEST_REPORTS/macOS.html
30 | - run:
31 | name: Test iOS
32 | command: set -o pipefail && xcodebuild test -scheme RxSwiftExt-iOS -workspace RxSwiftExt.xcworkspace -sdk iphonesimulator -destination "name=iPhone 12" | tee "${XCODE_TEST_REPORTS}/xcode_iOS.log" | xcpretty -c -r html --output $XCODE_TEST_REPORTS/iOS.html
33 | - run:
34 | name: Test tvOS
35 | command: set -o pipefail && xcodebuild test -scheme RxSwiftExt-tvOS -workspace RxSwiftExt.xcworkspace -sdk appletvsimulator -destination "name=Apple TV 4K (at 1080p)" | tee "${XCODE_TEST_REPORTS}/xcode_tvOS.log" | xcpretty -c -r html --output $XCODE_TEST_REPORTS/tvOS.html
36 | - run:
37 | name: Test SPM
38 | command: swift test
39 | - store_artifacts:
40 | path: /tmp/xcode-test-results
41 | "RxSwiftExt Release":
42 | working_directory: ~/RxSwiftCommunity/RxSwiftExt
43 | parallelism: 1
44 | shell: /bin/bash --login
45 | environment:
46 | LANG: en_US.UTF-8
47 | macos:
48 | xcode: '12.3.0'
49 | steps:
50 | - checkout
51 | - run:
52 | name: Setup CocoaPods
53 | command: pod setup
54 | - run:
55 | name: Override Circle CI Config
56 | command: rm ~/.cocoapods/config.yaml # This hack is needed since CircleCI forces --verbose
57 | - run:
58 | name: Push Podspec to Trunk
59 | command: pod trunk push --skip-tests --allow-warnings
60 | workflows:
61 | version: 2
62 | build:
63 | jobs:
64 | - "RxSwiftExt Tests":
65 | filters:
66 | tags:
67 | ignore: /[0-9]+(\.[0-9]+)*/
68 | release:
69 | jobs:
70 | - "RxSwiftExt Release":
71 | filters:
72 | branches:
73 | ignore: /.*/
74 | tags:
75 | only: /[0-9]+(\.[0-9]+)*/
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata
15 | *.xccheckout
16 | profile
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 | *.o
22 |
23 | # Bundler
24 | .bundle
25 |
26 | # Carthage
27 | Carthage
28 | !Carthage/**
29 |
30 | # AppCode
31 | .idea
32 |
33 | # SPM
34 | .build
35 |
36 | # Cocoapods
37 | # We recommend against adding the Pods directory to your .gitignore. However
38 | # you should judge for yourself, the pros and cons are mentioned at:
39 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
40 | #
41 | # Note: if you ignore the Pods directory, make sure to uncomment
42 | # `pod install` in .travis.yml
43 | #
44 | Pods/
45 | Podfile.lock
46 | Demo/RxSwiftExtDemo/RxSwiftExtPlayground.playground/Pages/not.xcplaygroundpage/timeline.xctimeline
47 | Package.resolved
48 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Carthage/Checkouts/RxSwift"]
2 | path = Carthage/Checkouts/RxSwift
3 | url = https://github.com/ReactiveX/RxSwift.git
4 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | included:
2 | - Source
3 | - Tests
4 | disabled_rules:
5 | - line_length
6 | - trailing_comma
7 | identifier_name:
8 | min_length: 1
9 | opt_in_rules:
10 | - file_header
11 | type_name:
12 | excluded: T
13 | function_body_length: 100
14 | type_body_length: 500
15 | cyclomatic_complexity:
16 | warning: 15
17 | error: 25
18 | file_header:
19 | required_pattern: |
20 | \/\/
21 | \/\/ .*?\.swift
22 | \/\/ RxSwiftExt
23 | \/\/
24 | \/\/ Created by .*? on \d{1,2}\/\d{1,2}\/(\d{2}|\d{4})\.
25 | \/\/ Copyright © \d{4} RxSwift Community\. All rights reserved\.
26 | \/\/
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Changelog
2 | =========
3 |
4 | - added `reachedBottom(offset:)` for `UIScrollView`
5 | - `once` now uses a `NSRecursiveLock` instead of the deprecated `OSAtomicOr32OrigBarrier`
6 | - Simplify `filterMap(_:)` implementation and make callback throwing
7 | - `once` now uses a `NSRecursiveLock` instead of the deprecated `OSAtomicOr32OrigBarrier`
8 | - added `merge(with:)` for `Observable`
9 | - removed `flatMapSync` operator
10 | - added `apply` for `Completable` and `Maybe`
11 | - added `mapTo` for `Single` and `Maybe`
12 | - added SPM support
13 | - adjusted repeat with behaviour doc
14 |
15 | 5.0.0
16 | -----
17 | - Update to RxSwift 5.0.
18 | - Requires the Swift 5 compiler (Xcode 10.2 and up).i
19 | - added `partition(_:)` operator
20 | - added `bufferWithTrigger` operator
21 | - added `fromAsync` operator for `Single`
22 |
23 | 4.0.0
24 | ------
25 | Version 4.x has been skipped to align with RxSwift versioning.
26 |
27 | RxSwiftExt 5.x supports Swift 5.x
28 | RxSwiftExt 3.x supports Swift 4.x
29 |
30 | 3.4.0
31 | -----
32 | - Fix `withUnretained` so it allows proper destructuring
33 | - added `mapMany` operator
34 | - added `toSortedArray` operator
35 | - rolled `map(to:)` back to `mapTo(_:)` for `SharedSequenceConvertibleType`
36 | - added `unwrap()` operator for SharedSequence
37 | - added `apply(_:)` for `Single`
38 | - added `count` operator
39 |
40 | 3.3.0
41 | -----
42 | - added UIViewPropertyAnimator `fractionComplete` reactive binder
43 | - added `withUnretained(_:)` operator
44 | - added UIViewPropertyAnimator Reactive Extensions (`animate()` operator)
45 |
46 | 3.2.0
47 | -----
48 | - added `mapAt(keyPath:)` operator
49 | - added `zip(with:)` operator
50 | - added `ofType(_:)` operator
51 |
52 | 3.1.0
53 | -----
54 | - added `pairwise()` and `nwise(_:)` operators
55 | - added `and()` operators
56 | - added support for compiling in an iOS App Extension
57 |
58 | 3.0.0
59 | -----
60 | - added support for Swift 4, RxSwift 4.0
61 |
62 | 2.5.1
63 | -----
64 | - added support for macOS
65 |
66 | 2.5.0
67 | -----
68 | - new operator: `filterMap`
69 | - new operator: `flatMapSync`
70 | - new operator: `pausableBuffered`
71 | - fixed issues with the demo Playground
72 |
73 | 2.4.0
74 | -----
75 | - re-added `errors()` and `elements()` operators for materialized sequences
76 | - fixed Carthage and CI issues
77 |
78 | 2.3.0
79 | -----
80 | - removed `materialize` and `dematerialize` operators as they now are part of RxSwift 3.4.0 and later
81 |
82 | 2.2.1
83 | -----
84 | - fixed compilation warning with Swift 3.1
85 |
86 | 2.2.0
87 | -----
88 | - new operator: `apply`
89 | - added `not`, `mapTo` and `distinct` support for RxCocoa units (`Driver` et al.)
90 |
91 | 2.1.0
92 | -----
93 | - new operators: `materialize` / `dematerialize`
94 | - extract Playground to use Carthage instead of CocoaPods
95 |
96 | 2.0.0
97 | -----
98 | - Support Swift 3.0 / RxSwift 3.0
99 |
100 | 1.2
101 | -----
102 | - new operator: `pausable`
103 | - Tweaked `Podfile` to fix an issue with running the demo playground
104 |
105 | 1.1
106 | -----
107 | - new operator: `retry` with four different behaviors
108 | - new operator: `catchErrorJustComplete`
109 | - new operator: `ignoreErrors`
110 |
111 | 1.0.1
112 | -----
113 | - new operator: `distinct` with predicate
114 | - updated to CocoaPods 1.0
115 |
116 | 1.0
117 | -----
118 | - Initial release.
119 |
--------------------------------------------------------------------------------
/CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at fpillet@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Preparing for development
2 |
3 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed. We use it to pull the RxSwiftExt dependency for development.
4 | 2. Check out this repository.
5 | 3. Do a `carthage update --platform ios --no-use-binaries`
6 | 4. Open `RxSwiftExt.xcworkspace` and start hacking!
7 |
8 | ## About contributions
9 |
10 | There are multiple ways you can contribute to this project.
11 |
12 | The easiest way to contribute is to report possible bugs, request features, [discuss ideas](https://github.com/RxSwiftCommunity/RxSwiftExt/issues) and share excitement about this project. We use [issues](https://github.com/RxSwiftCommunity/RxSwiftExt/issues) to discuss new operators for inclusion in the project.
13 |
14 | You can also make pull requests. Other than bug fixes, please make sure you discuss your contribution in Issues first.
15 |
16 | ## Submitting a Pull Request
17 |
18 | When submitting new code, please make sure that it is backed by tests. When submitting code for a new operator, please make sure that:
19 |
20 | - Your code documents what the operator does (see other operators for examples).
21 | - You provide tests for your new operator.
22 | - You provide a demo playground page, and add a reference to this page in the playground index.
23 | - You add documentation of the operator in this repo's `README.md` file.
24 |
25 | **Note**: Base your custom operators only on RxSwift Core operators. Do not use existing RxSwiftExt operators to implement your own operator, unless it's an absolute necessity.
26 |
27 | ## Developer's Certificate of Origin 1.1
28 |
29 | By making a contribution to this project, I certify that:
30 |
31 | - (a) The contribution was created in whole or in part by me and I
32 | have the right to submit it under the open source license
33 | indicated in the file; or
34 |
35 | - (b) The contribution is based upon previous work that, to the best
36 | of my knowledge, is covered under an appropriate open source
37 | license and I have the right under that license to submit that
38 | work with modifications, whether created in whole or in part
39 | by me, under the same open source license (unless I am
40 | permitted to submit under a different license), as indicated
41 | in the file; or
42 |
43 | - (c) The contribution was provided directly to me by some other
44 | person who certified (a), (b) or (c) and I have not modified
45 | it.
46 |
47 | - (d) I understand and agree that this project and the contribution
48 | are public and that a record of the contribution (including all
49 | personal information I submit with it, including my sign-off) is
50 | maintained indefinitely and may be redistributed consistent with
51 | this project or the open source license(s) involved.
52 |
53 | *Wording of statement copied from [elinux.org](http://elinux.org/Developer_Certificate_Of_Origin)*
54 |
--------------------------------------------------------------------------------
/Cartfile:
--------------------------------------------------------------------------------
1 | github "ReactiveX/RxSwift" ~> 6.0
2 |
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "ReactiveX/RxSwift" "6.0.0"
2 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Name and description
2 |
3 | ### Motivation for inclusion
4 |
5 | ### Example of use
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-latest RxSwiftCommunity https://github.com/RxSwiftCommunity
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "RxSwiftExt",
7 | platforms: [
8 | .iOS(.v9), .tvOS(.v9), .macOS(.v10_11), .watchOS(.v3)
9 | ],
10 | products: [
11 | .library(name: "RxSwiftExt", targets: ["RxSwiftExt"]),
12 | ],
13 | dependencies: [
14 | .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.0.0")),
15 | ],
16 | targets: [
17 | .target(name: "RxSwiftExt", dependencies: ["RxSwift", "RxCocoa"], path: "Source"),
18 | .testTarget(name: "RxSwiftExtTests", dependencies: ["RxSwiftExt", "RxTest"], path: "Tests"),
19 | ],
20 | swiftLanguageVersions: [.v5]
21 | )
22 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Contents.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RxSwiftCommunity/RxSwiftExt/eb4adf9f00a21b3efc3869a5218a6d7517e95222/Playground/RxSwiftExtPlayground.playground/Contents.o
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/UIScrollView.reachedBottom.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxCocoa
14 | import RxSwiftExt
15 | import PlaygroundSupport
16 | import UIKit
17 |
18 | /*:
19 | ## reachedBottom
20 |
21 | `reachedBottom` provides a sequence that emits every time the `UIScrollView` is scrolled to the bottom, with an optional offset.
22 |
23 | Please open the Assistant Editor (⌘⌥⏎) to see the Interactive Live View example.
24 | */
25 |
26 | final class ReachedBottomViewController: UITableViewController {
27 | private let dataSource = Array(stride(from: 0, to: 28, by: 1))
28 | private let identifier = "identifier"
29 | private let disposeBag = DisposeBag()
30 |
31 | override func viewDidLoad() {
32 | super.viewDidLoad()
33 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: identifier)
34 | tableView.rx.reachedBottom(offset: 40)
35 | .subscribe { print("Reached bottom") }
36 | .disposed(by: disposeBag)
37 | }
38 |
39 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
40 | return dataSource.count
41 | }
42 |
43 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
44 | let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
45 | cell.textLabel?.text = "\(dataSource[indexPath.row])"
46 | return cell
47 | }
48 | }
49 |
50 | // Present the view controller in the Live View window
51 | PlaygroundPage.current.liveView = ReachedBottomViewController()
52 | //: [Next](@next)
53 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/UIViewPropertyAnimator.animate.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxCocoa
14 | import RxSwiftExt
15 | import PlaygroundSupport
16 | import UIKit
17 |
18 | /*:
19 | ## animate
20 |
21 | The `animate` operator provides a Completable that triggers the animation upon subscription and completes when the animation ends.
22 |
23 | Please open the Assistant Editor (⌘⌥⏎) to see the Interactive Live View example.
24 | */
25 |
26 | class AnimateViewController: UIViewController {
27 |
28 | let disposeBag = DisposeBag()
29 |
30 | lazy var box1: UIView = {
31 | let view = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
32 | view.backgroundColor = .red
33 | return view
34 | }()
35 |
36 | lazy var box2: UIView = {
37 | let view = UIView(frame: CGRect(x: 100, y: 220, width: 100, height: 100))
38 | view.backgroundColor = .green
39 | return view
40 | }()
41 |
42 | lazy var box3: UIView = {
43 | let view = UIView(frame: CGRect(x: 100, y: 340, width: 100, height: 100))
44 | view.backgroundColor = .blue
45 | return view
46 | }()
47 |
48 | lazy var button: UIButton = {
49 | let button = UIButton(frame: CGRect(x: 100, y: 500, width: 200, height: 50))
50 | button.setTitle("Play animation", for: .normal)
51 | button.setTitleColor(.blue, for: .normal)
52 | button.backgroundColor = .white
53 | button.layer.borderColor = UIColor.black.cgColor
54 | button.layer.borderWidth = 2
55 |
56 | return button
57 | }()
58 |
59 | var animator1: UIViewPropertyAnimator!
60 | var animator2: UIViewPropertyAnimator!
61 | var animator3: UIViewPropertyAnimator!
62 |
63 | private func makeAnimators() {
64 | animator1 = UIViewPropertyAnimator(duration: 0.3, curve: .easeInOut) { [unowned self] in
65 | self.box1.transform = self.box1.transform != .identity ? .identity
66 | : self.box1.transform.translatedBy(x: 0, y: -100)
67 | }
68 |
69 | animator2 = UIViewPropertyAnimator(duration: 0.25, curve: .easeInOut) { [unowned self] in
70 | self.box2.transform = self.box2.transform != .identity ? .identity
71 | : self.box2.transform
72 | .translatedBy(x: 0, y: -100)
73 | .scaledBy(x: 1.2, y: 1.2)
74 | }
75 |
76 | animator3 = UIViewPropertyAnimator(duration: 0.15, curve: .easeInOut) { [unowned self] in
77 | self.box3.transform = self.box3.transform != .identity ? .identity
78 | : self.box3.transform
79 | .translatedBy(x: 0, y: -100)
80 | .rotated(by: .pi)
81 | }
82 | }
83 |
84 | override func viewDidLoad() {
85 | super.viewDidLoad()
86 |
87 | // construct the main view
88 | let views = [box1, box2, box3, button]
89 | view.backgroundColor = .white
90 |
91 | views.forEach {
92 | view.addSubview($0)
93 | }
94 |
95 | makeAnimators()
96 |
97 | // Trigger chained animations after a button tap
98 | button.rx.tap
99 | .flatMap { [unowned self] in
100 | self.animator1.rx.animate()
101 | .andThen(self.animator2.rx.animate(afterDelay: 0.15))
102 | .andThen(self.animator3.rx.animate(afterDelay: 0.1))
103 | .do(onCompleted: {
104 | self.makeAnimators()
105 | })
106 | .debug("animation sequence")
107 | }
108 | .subscribe()
109 | .disposed(by: disposeBag)
110 | }
111 | }
112 |
113 | // Present the view controller in the Live View window
114 | PlaygroundPage.current.liveView = AnimateViewController()
115 |
116 | //: [Next](@next)
117 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/UIViewPropertyAnimator.animate.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/UIViewPropertyAnimator.fractionComplete.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxCocoa
14 | import RxSwiftExt
15 | import PlaygroundSupport
16 | import UIKit
17 |
18 | /*:
19 | ## fractionComplete
20 |
21 | The `fractionComplete` binder provides a reactive way to bind to `UIViewPropertyAnimator.fractionComplete`.
22 |
23 | Please open the Assistant Editor (⌘⌥⏎) to see the Interactive Live View example.
24 | */
25 |
26 | class FractionCompleteViewController: UIViewController {
27 |
28 | let disposeBag = DisposeBag()
29 |
30 | lazy var box: UIView = {
31 | let view = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
32 | view.backgroundColor = .purple
33 | return view
34 | }()
35 |
36 | lazy var slider: UISlider = {
37 | let slider = UISlider(frame: .zero)
38 | slider.translatesAutoresizingMaskIntoConstraints = false
39 | return slider
40 | }()
41 |
42 | lazy var animator: UIViewPropertyAnimator = {
43 | UIViewPropertyAnimator(duration: 1, curve: .linear, animations: {
44 | let transform = CGAffineTransform(translationX: 100, y: 0)
45 | .concatenating(CGAffineTransform(rotationAngle: 360))
46 | self.box.transform = transform
47 | })
48 | }()
49 |
50 | lazy var fractionCompleteLabel: UILabel = {
51 | let label = UILabel()
52 | label.translatesAutoresizingMaskIntoConstraints = false
53 | return label
54 | }()
55 |
56 | override func viewDidLoad() {
57 | super.viewDidLoad()
58 |
59 | // construct the main view
60 | view.backgroundColor = .white
61 | setupViewHierarchy()
62 | setupConstraints()
63 |
64 | slider.rx.value.map(CGFloat.init)
65 | .bind(to: animator.rx.fractionComplete)
66 | .disposed(by: disposeBag)
67 |
68 | slider.rx.value
69 | .map { value in
70 | String(format: "fractionComplete: %.2lf", value)
71 | }
72 | .bind(to: fractionCompleteLabel.rx.text)
73 | .disposed(by: disposeBag)
74 | }
75 |
76 | private func setupViewHierarchy() {
77 | [box, slider, fractionCompleteLabel]
78 | .forEach(view.addSubview)
79 | }
80 |
81 | private func setupConstraints() {
82 |
83 | slider.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
84 | slider.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
85 | slider.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8).isActive = true
86 |
87 | fractionCompleteLabel.topAnchor.constraint(equalTo: slider.bottomAnchor).isActive = true
88 | fractionCompleteLabel.centerXAnchor.constraint(equalTo: slider.centerXAnchor).isActive = true
89 | }
90 | }
91 |
92 | // Present the view controller in the Live View window
93 | PlaygroundPage.current.liveView = FractionCompleteViewController()
94 |
95 | //: [Next](@next)
96 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/and.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 | import RxSwift
12 | import RxSwiftExt
13 |
14 | example("Ensure that only `true` values are emitted") {
15 | let allTrue = Observable.of(true, true ,true)
16 | let allTrue2 = Observable.just(true)
17 | let someFalse = Observable.of(true, false, true)
18 | let empty = Observable.empty()
19 |
20 | allTrue.and().subscribe { result in
21 | print("- when all values are true, we get a `true` Maybe: \(result)")
22 | }
23 |
24 | someFalse.and().subscribe { result in
25 | print("- when some values are false, we get a `false` Maybe: \(result)")
26 | }
27 |
28 | empty.and().subscribe { result in
29 | print("- when no value is emitted, we get a Maybe with no result: \(result)")
30 | }
31 |
32 | Observable.and(allTrue, empty).subscribe { result in
33 | print("- mixing an empty sequence and a sequence of true values, we get a `true` Maybe: \(result)")
34 | }
35 |
36 | Observable.and(allTrue, someFalse, empty).subscribe { result in
37 | print("- mixing an empty sequence and sequences of true and false values, we get a `false` Maybe: \(result)")
38 | }
39 |
40 | Observable.and(allTrue, allTrue2).subscribe { result in
41 | print("- mixing sequences of true values, we get a `true` Maybe: \(result)")
42 | }
43 | }
44 |
45 | //: [Next](@next)
46 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/apply.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import Foundation
13 | import RxSwift
14 | import RxSwiftExt
15 |
16 | /*:
17 | ## apply
18 |
19 | The `apply` operator takes a transformation function `(Observable) -> Observable` and applies it to the stream. The purpose of this operator is to provide syntactic sugar for applying multiple operators to the stream, while preserving the chaining operator structure of Rx.
20 |
21 | */
22 |
23 | func addOne(input: Observable) -> Observable {
24 | return input
25 | .map { $0 + 1 }
26 | .map { "The next number is \($0)" }
27 | }
28 |
29 | func addOne(input: Single) -> Single {
30 | return input
31 | .map { $0 + 1 }
32 | .map { "The next number is \($0)" }
33 | }
34 |
35 | example("apply a transformation") {
36 | let numbers1 = Observable.from([1, 2, 3])
37 | let numbers2 = Observable.from([100, 101, 102])
38 | let number3 = Single.just(1)
39 | let number4 = Single.just(100)
40 |
41 | print("apply() calls the transform function on the Observable sequence: ")
42 |
43 | let transformed1 = numbers1.apply(addOne)
44 | let transformed2 = numbers2.apply(addOne)
45 | let transformed3 = number3.apply(addOne)
46 | let transformed4 = number4.apply(addOne)
47 |
48 | transformed1.subscribe(onNext: { result in
49 | print(result)
50 | })
51 |
52 | transformed2.subscribe(onNext: { result in
53 | print(result)
54 | })
55 |
56 | transformed3.subscribe(onSuccess: { result in
57 | print(result)
58 | })
59 |
60 | transformed4.subscribe(onSuccess: { result in
61 | print(result)
62 | })
63 | }
64 |
65 | //: [Next](@next)
66 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/bufferWithTrigger.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 | import RxSwift
12 | import RxSwiftExt
13 | /*:
14 | ## bufferWithTrigger
15 |
16 | Collects the elements of the source observable, and emits them as an array when the trigger emits.
17 | */
18 |
19 | example("bufferWithTrigger") {
20 | let observable = Observable.interval(1, scheduler: MainScheduler.instance)
21 |
22 | let signalAtThreeSeconds = Observable.timer(3, scheduler: MainScheduler.instance).map { _ in () }
23 | let signalAtFiveSeconds = Observable.timer(5, scheduler: MainScheduler.instance).map { _ in () }
24 | let trigger = Observable.of(signalAtThreeSeconds, signalAtFiveSeconds).merge()
25 |
26 | observable.bufferWithTrigger(trigger).debug("buffer").subscribe()
27 |
28 | playgroundShouldContinueIndefinitely()
29 | }
30 | //: [Next](@next)
31 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/cascade.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## cascade
17 |
18 | The `cascade` operator takes a ordinary sequence (i.e. Array) of observables and cascades through them:
19 | - it first subscribes to all observables in the sequence
20 | - every time an observable emits an element, all previous observables in the sequence are unsubscribed from, and elements from this observables are sent through
21 | - when any of the currently subscribed-to observables errors, the resulting observable errors too
22 | */
23 |
24 | example("cascade") {
25 |
26 | // produce an infinite sequence of numbers starting at 0
27 | let a = Observable.interval(1, scheduler: MainScheduler.instance)
28 | .map { "a emitted \($0)" }
29 |
30 | // produce an infinite sequence of numbers after a 3 second delay
31 | let b = Observable.interval(1, scheduler: MainScheduler.instance)
32 | .map { "b emitted \($0)" }
33 | .delaySubscription(3, scheduler: MainScheduler.instance)
34 |
35 | // produce an infinite sequence of numbers after a 6 second delay
36 | let c = Observable.interval(1, scheduler: MainScheduler.instance)
37 | .map { "c emitted \($0)" }
38 | .delaySubscription(6, scheduler: MainScheduler.instance)
39 |
40 | // cascade subscribes to all three observables, but switches to the latest one in the
41 | // observables list, unsubscribing from previous ones. The resulting sequence will
42 | // first output values from `a' (as they are being emitted immediately) then switch to
43 | // `b' as soon as it starts emitting values (after 3 seconds) then switch to `c' as soon
44 | // as it starts emitting values (after 6 seconds), effectively cascading through the
45 | // given observables with no possible return to previous ones.
46 | Observable.cascade([a,b,c])
47 | .subscribe(onNext: {
48 | print("Cascade next: \($0)")
49 | })
50 |
51 | // watch the resulting sequence in the playground debug area!
52 | playgroundShouldContinueIndefinitely()
53 | }
54 |
55 | //: [Next](@next)
56 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/cascade.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/catchErrorJustComplete.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## catchErrorJustComplete
17 |
18 | Dismiss errors and complete the sequence instead
19 |
20 | - returns: An observable sequence that never errors and completes when an error occurs in the underlying sequence
21 | */
22 | private enum SampleErrors : Error {
23 | case fatalError
24 | }
25 |
26 | let sampleObservable = Observable.create { observer in
27 | observer.onNext("First")
28 | observer.onNext("Second")
29 | observer.onError(SampleErrors.fatalError)
30 | observer.onCompleted()
31 | return Disposables.create()
32 | }
33 |
34 | example("catchErrorJustComplete") {
35 |
36 | let _ = sampleObservable
37 | .do(onError: { print("Source observable emitted error \($0)") })
38 | .catchErrorJustComplete()
39 | .subscribe {
40 | print ("\($0)")
41 | }
42 |
43 | }
44 |
45 | //: [Next](@next)
46 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/count.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## count
17 |
18 | Count the number of items emitted by an Observable
19 | - seealso: [count operator on reactivex.io](http://reactivex.io/documentation/operators/count.html)
20 | - parameter predicate: predicate determines what elements to be counted.
21 |
22 | - returns: An Observable sequence containing a value that represents how many elements in the specified observable sequence satisfy a condition if provided, else the count of items.
23 |
24 | */
25 |
26 | example("count") {
27 |
28 | // count even number in the sequence
29 | let _ = Observable.from([1...10])
30 | .count { $0 % 2 == 0 }
31 | .subscribe(onNext: {
32 | print ($0)
33 | })
34 |
35 | }
36 |
37 | //: [Next](@next)
38 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/distinct.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## distinct
17 |
18 | Suppress duplicate items emitted by an Observable
19 | - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
20 | - returns: An observable sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence.
21 |
22 | */
23 |
24 | example("distinct") {
25 |
26 | // suppress duplicate strings from the sequence
27 | let _ = Observable.of("a","b","a","c","b","a","d")
28 | .distinct()
29 | .subscribe(onNext: {
30 | print ("\($0)")
31 | })
32 |
33 | }
34 |
35 | //: [Next](@next)
36 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/filterMap.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExt (playground)` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## filterMap()
17 |
18 | A common pattern in Rx is to filter out some values, then map the remaining ones to something else. The `filterMap` operator does this in one step:
19 | */
20 |
21 | example("filterMap") {
22 | // keep only odd numbers and double them
23 | Observable.of(1,2,3,4,5,6)
24 | .filterMap { number in
25 | return (number % 2 == 0) ? .ignore : .map(number * 2)
26 | }
27 | .subscribe { print($0) }
28 | }
29 |
30 | //: [Next](@next)
31 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/fromAsync.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExt (playground)` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 | import Foundation
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## Observable.fromAsync()
17 |
18 | This function takes as argument a function that takes up to 9 arbitrary arguments and a completionHandler
19 | and returns a function with the same signature, minus the completionHandler, and returns an Observable with
20 | the same Element type as the completionHandler
21 | */
22 | example("Turn a completion handler into an observable sequence") {
23 | func someAsynchronousTask(arg1: String, arg2: Int, completionHandler: @escaping (String) -> Void) {
24 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
25 | completionHandler("completion handler result")
26 | }
27 | }
28 |
29 | let observableService = Observable.fromAsync(someAsynchronousTask)
30 |
31 | print("Waiting for completion handler to be called...")
32 |
33 | _ = observableService("Foo", 0)
34 | .subscribe(onNext: { (result) in
35 | print("Asynchronous callback called with: \(result)")
36 | })
37 |
38 | playgroundShouldContinueIndefinitely()
39 | }
40 | //: [Next](@next)
41 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/ignore.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## ignore
17 |
18 | The `ignore` operator filters out any of the elements passed in parameters. An alternate implementation of `ignore` is provided, which takes a `SequenceType` with any number of elements to ignore.
19 | */
20 |
21 | example("ignore a single value") {
22 |
23 | let values = ["Hello", "Swift", "world"]
24 | Observable.from(values)
25 | .ignore("Swift")
26 | .toArray()
27 | .subscribe(onNext: { result in
28 | // look values on the right panel ===>
29 | values
30 | result
31 | print("ignore() transformed \(values) to \(result)")
32 | })
33 |
34 | }
35 |
36 | example("ignore multiple values") {
37 |
38 | let values = "Hello Swift world we really like Swift and RxSwift".components(separatedBy: " ")
39 | Observable.from(values)
40 | .ignore("Swift", "and")
41 | .toArray()
42 | .subscribe(onNext: { result in
43 | // look values on the right panel ===>
44 | values
45 | result
46 | print("ignore() transformed \(values) to \(result)")
47 | })
48 |
49 | }
50 |
51 | example("ignore a collection of values") {
52 |
53 | let values = "Hello Swift world we really like Swift and RxSwift".components(separatedBy: " ")
54 | let ignoreSet = Set(["and", "Swift"])
55 |
56 | Observable.from(values)
57 | .ignore(ignoreSet)
58 | .toArray()
59 | .subscribe(onNext: { result in
60 | // look values on the right panel ===>
61 | values
62 | result
63 | print("ignore() transformed \(values) to \(result)")
64 | })
65 |
66 | }
67 |
68 | /*:
69 | ## ignoreWhen
70 |
71 | The `ignoreWhen` operator works like `filter` but ignores the elements for which the predicate returns `true` instead of keeping them.
72 | */
73 |
74 | example("ignore some elements") {
75 |
76 | let values = [1, 5, 40, 12, 60, 3, 9, 18]
77 |
78 | Observable.from(values)
79 | .ignoreWhen { value in
80 | return value > 10
81 | }
82 | .toArray()
83 | .subscribe(onNext: { result in
84 | // look values on the right panel ===>
85 | values
86 | result
87 | print("ignoreWhen() transformed \(values) to \(result)")
88 | })
89 | }
90 |
91 | /*:
92 | ## ignoreErrors
93 |
94 | The `ignoreErrors` operator is a synonym for the `retry` operator: it unconditionally ignores any error emitted by the sequence,
95 | creating an sequence that never fails
96 | */
97 | enum ExampleError : Error {
98 | case SeriousError
99 | case MinorError
100 | }
101 |
102 | example("ignore all errors") {
103 |
104 | let subject = PublishSubject>()
105 |
106 | let _ = subject
107 | .asObservable()
108 | .flatMap { $0 }
109 | .ignoreErrors()
110 | .subscribe { print($0) }
111 |
112 | subject.onNext(Observable.just(1))
113 | subject.onNext(Observable.just(2))
114 | subject.onNext(Observable.just(3))
115 | subject.onNext(Observable.error(ExampleError.SeriousError))
116 | subject.onNext(Observable.just(4))
117 | subject.onNext(Observable.just(5))
118 |
119 | }
120 |
121 | example("ignore only minor errors") {
122 |
123 | let subject = PublishSubject>()
124 |
125 | let _ = subject
126 | .asObservable()
127 | .flatMap { $0 }
128 | .ignoreErrors {
129 | if case ExampleError.SeriousError = $0 {
130 | return false
131 | }
132 | return true
133 | }
134 | .subscribe { print($0) }
135 |
136 | subject.onNext(Observable.just(1))
137 | subject.onNext(Observable.just(2))
138 | subject.onNext(Observable.just(3))
139 | subject.onNext(Observable.error(ExampleError.SeriousError))
140 | subject.onNext(Observable.just(4))
141 | subject.onNext(Observable.just(5))
142 |
143 | }
144 |
145 | //: [Next](@next)
146 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/mapAt.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## mapAt(KeyPath)
17 |
18 | The `mapAt` operator transforms a sequence of elements where each element is mapped to its value at the provided key path
19 | */
20 | example("map input to the value at provided key path") {
21 | struct Person {
22 | let name: String
23 | }
24 |
25 | let people: [Person] = [
26 | Person(name: "Bart"),
27 | Person(name: "Lisa"),
28 | Person(name: "Maggie")
29 | ]
30 |
31 | Observable.from(people)
32 | .mapAt(\.name)
33 | .toArray()
34 | .subscribe(onNext: {result in
35 | // look types on the right panel ===>
36 | people
37 | result
38 | print("mapAt() transformed \(people) to \(result)")
39 | })
40 | }
41 |
42 |
43 | //: [Next](@next)
44 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/mapMany.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 | import RxSwift
12 | import RxSwiftExt
13 |
14 | example("mapMany") {
15 | let numbers = Observable.of(1...10)
16 | let strings = Observable.of(["RxSwift", "is" ,"awesome", "along", "with", "RxSwiftCommunity"])
17 |
18 | // Map many using a model initializer
19 | numbers.mapMany(SomeModel.init)
20 | .subscribe(onNext: { result in
21 | print(result)
22 | })
23 |
24 | // Map many with a transformation closure
25 | numbers.mapMany { $0 * $0 }
26 | .subscribe(onNext: { result in
27 | print(result)
28 | })
29 |
30 | strings.mapMany { $0.lowercased() }
31 | .subscribe(onNext: { result in
32 | print(result)
33 | })
34 |
35 | struct SomeModel: CustomStringConvertible {
36 | let number: Int
37 | var description: String { return "#\(number)" }
38 |
39 | init(_ number: Int) {
40 | self.number = number
41 | }
42 | }
43 | }
44 |
45 | //: [Next](@next)
46 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/mapTo.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## mapTo( Any)
17 |
18 | The `mapTo` operator takes a sequence of elements and returns a sequence of the same constant provided as a parameter. In effect it ignores its input and replaces it with a constant
19 | */
20 | example("replace any input with a value") {
21 |
22 | let numbers = Array([1, 2, 3])
23 | Observable.from(numbers)
24 | .mapTo("candy")
25 | .toArray()
26 | .subscribe(onNext: {result in
27 | // look types on the right panel ===>
28 | numbers
29 | result
30 | print("mapTo() transformed \(numbers) to \(result)")
31 | })
32 | }
33 |
34 |
35 | //: [Next](@next)
36 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/mergeWith.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## merge(with:)
17 | Merges elements from observable sequences into a single observable sequence.
18 |
19 | */
20 | example("merge(with:)") {
21 | let oddNumbers = [1, 3, 5, 7, 9]
22 | let evenNumbers = [2, 4, 6, 8, 10]
23 | let otherNumbers = [1, 5 ,6, 2]
24 |
25 | let disposeBag = DisposeBag()
26 | let oddStream = Observable.from(oddNumbers)
27 | let evenStream = Observable.from(evenNumbers)
28 | let otherStream = Observable.from(otherNumbers)
29 |
30 | oddStream
31 | .merge(with: [evenStream, otherStream])
32 | .subscribe(onNext: { result in
33 | print(result)
34 | })
35 | .disposed(by: disposeBag)
36 | }
37 |
38 | //: [Next](@next)
39 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/not.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## not
17 |
18 | The `not` operator applies a the boolean not (!) to a `Bool`
19 | */
20 | example("boolean not - example 1") {
21 |
22 | _ = Observable.just(false)
23 | .not()
24 | .subscribe(onNext: { result in
25 | assert(result)
26 | print("Success! result = \(result)")
27 | })
28 | }
29 |
30 | //: [Next](@next)
31 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/nwise.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 | import RxSwift
12 | import RxSwiftExt
13 |
14 | /*:
15 | ## nwise
16 |
17 | The `nwise` operator groups elements emitted by an Observable into arrays, where each array consists of N consecutive items; similar to a sliding window.
18 | */
19 |
20 | example("Grouping numbers into triples") {
21 | let input = [1, 2, 3, 4, 5, 6]
22 |
23 | print("Input:", input)
24 | print("Output:")
25 |
26 | Observable.from(input)
27 | .nwise(3)
28 | .subscribe(onNext: { result in
29 | print(result)
30 | })
31 | }
32 |
33 | /*:
34 | ## pairwise
35 |
36 | The `pairwise` operator is a special case of `nwise` with the size of 2, which emits the previous and current items in tuples instead of arrays.
37 | */
38 |
39 | example("Grouping numbers into pairs") {
40 | let input = [1, 2, 3, 4, 5, 6]
41 |
42 | print("Input:", input)
43 | print("Output:")
44 |
45 | Observable.from(input)
46 | .pairwise()
47 | .subscribe(onNext: { result in
48 | print(result)
49 | })
50 | }
51 |
52 | //: [Next](@next)
53 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/ofType.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## ofType()
17 |
18 | The `ofType` operator filters the elements of an observable sequence, if that is an instance of the supplied type.
19 |
20 | */
21 |
22 | example("ofType") {
23 | Observable.of(NSNumber(value: 1),
24 | NSDecimalNumber(string: "2"),
25 | NSNumber(value: 3),
26 | NSNumber(value: 4),
27 | NSDecimalNumber(string: "5"),
28 | NSNumber(value: 6))
29 | .ofType(NSDecimalNumber.self)
30 | .subscribe { print($0) }
31 | }
32 |
33 | //: [Next](@next)
34 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/once.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## Observable.once
17 |
18 | The `Observable.once` function creates a sequence that delivers an element *once* to the first subscriber then completes. The same sequence will complete immediately without delivering any element to all further subscribers.
19 |
20 | It lets you guarantee a single-time delivery of a value. Most of the time you will want to use the `Observable.just` operator (creates a sequence which delivers a single element to any observer then complete). In some cases, `Observable.once` can be useful.
21 | */
22 |
23 | let justOnce = Observable.once("Hello, world")
24 |
25 | // let's subscribe a first time
26 | justOnce.subscribe { event in
27 | switch event {
28 | case .next(let value):
29 | print("First subscriber received value \"\(value)\"")
30 | case .completed:
31 | print("First subscriber completed")
32 | default:
33 | break
34 | }
35 | }
36 |
37 | // let's subscribe a second time to the SAME sequence
38 | justOnce.subscribe { event in
39 | switch event {
40 | case .next(let value):
41 | // this will never be reached
42 | print("Second subscriber received value \"\(value)\"")
43 | case .completed:
44 | print("Second subscriber completed")
45 | default:
46 | break
47 | }
48 | }
49 |
50 | //: [Next](@next)
51 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/partition.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | example("partition") {
16 | let numbers = Observable
17 | .of(1, 2, 3, 5, 8, 13, 18, 21, 23)
18 |
19 | let (evens, odds) = numbers.partition { $0 % 2 == 0 }
20 |
21 | _ = evens.debug("even").subscribe()
22 | _ = odds.debug("odds").subscribe()
23 | }
24 | //: [Next](@next)
25 |
26 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/pausable.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## pausable
17 |
18 | The `pausable` operator pauses the elements of the source observable sequence based on the latest element from the second observable sequence.
19 | - elements from the underlying observable sequence are emitted if and only if the second sequence has most recently emitted `true`.
20 | */
21 |
22 | example("pausable") {
23 |
24 | let observable = Observable.interval(1, scheduler: MainScheduler.instance)
25 |
26 | let trueAtThreeSeconds = Observable.timer(3, scheduler: MainScheduler.instance).map { _ in true }
27 | let falseAtFiveSeconds = Observable.timer(5, scheduler: MainScheduler.instance).map { _ in false }
28 | let pauser = Observable.of(trueAtThreeSeconds, falseAtFiveSeconds).merge()
29 |
30 | let pausedObservable = observable.pausable(pauser)
31 |
32 | pausedObservable
33 | .subscribe { print($0) }
34 |
35 | playgroundShouldContinueIndefinitely()
36 | }
37 |
38 | //: [Next](@next)
39 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/pausableBuffered.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## pausableBuffered
17 |
18 | Pauses the elements of the source observable sequence based on the latest element from the second observable sequence.
19 |
20 | While paused, elements from the source are buffered, limited to a maximum number of element.
21 |
22 | When resumed, all buffered elements are flushed as single events in a contiguous stream.
23 | */
24 |
25 | example("pausableBuffered") {
26 |
27 | let observable = Observable.interval(1, scheduler: MainScheduler.instance)
28 |
29 | let trueAtThreeSeconds = Observable.timer(3, scheduler: MainScheduler.instance).map { _ in true }
30 | let falseAtFiveSeconds = Observable.timer(5, scheduler: MainScheduler.instance).map { _ in false }
31 | let pauser = Observable.of(trueAtThreeSeconds, falseAtFiveSeconds).merge()
32 |
33 | // unlimited buffering of values received while paused
34 | let pausedObservable = observable.pausableBuffered(pauser, limit: nil)
35 |
36 | pausedObservable
37 | .subscribe { print($0) }
38 |
39 | playgroundShouldContinueIndefinitely()
40 |
41 | }
42 | //: [Next](@next)
43 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/repeatWithBehavior.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import Foundation
13 | import RxSwift
14 | import RxSwiftExt
15 |
16 | private enum SampleErrors : Error {
17 | case fatalError
18 | }
19 |
20 | let completingObservable = Observable.create { observer in
21 | observer.onNext("First")
22 | observer.onNext("Second")
23 | observer.onCompleted()
24 | return Disposables.create()
25 | }
26 |
27 | let erroringObservable = Observable.create { observer in
28 | observer.onNext("First")
29 | observer.onNext("Second")
30 | observer.onError(SampleErrors.fatalError)
31 | return Disposables.create()
32 | }
33 |
34 | let delayScheduler = SerialDispatchQueueScheduler(qos: .utility)
35 |
36 | example("Immediate repeat") {
37 | // repeat immediately after completion
38 | _ = completingObservable.repeatWithBehavior(.immediate(maxCount: 2))
39 | .subscribe(onNext: { event in
40 | print("Receive event: \(event)")
41 | })
42 | }
43 |
44 |
45 |
46 | example("Immediate repeat with custom predicate") {
47 | // here we provide a custom predicate that will determines whether we should resubscribe when the sequence is complete
48 | _ = completingObservable.repeatWithBehavior(.immediate(maxCount: 2), scheduler: delayScheduler) {
49 | return true
50 | }
51 | .subscribe(onNext: { event in
52 | print("Receive event: \(event)")
53 | })
54 | }
55 |
56 | example("Delayed repeat") {
57 | // once complete, the observable will be resubscribed to after 1.0 second delay
58 | _ = completingObservable.repeatWithBehavior(.delayed(maxCount: 2, time: 1.0), scheduler: delayScheduler)
59 | .subscribe(onNext: { event in
60 | print("Receive event: \(event)")
61 | })
62 | }
63 |
64 | // sleep in order to wait until previous example finishes
65 | Thread.sleep(forTimeInterval: 2.5)
66 |
67 | example("Exponential delay") {
68 | // when the sequence completes initial delay will be 1 second,
69 | // every next delay will be doubled
70 | // delay formula is: initial * pow(1 + multiplier, Double(currentAttempt - 1)), so multiplier 1.0 means, delay will doubled
71 | _ = completingObservable.repeatWithBehavior(.exponentialDelayed(maxCount: 3, initial: 1.0, multiplier: 1.2), scheduler: delayScheduler)
72 | .subscribe(onNext: { event in
73 | print("Receive event: \(event)")
74 | })
75 | }
76 |
77 | // sleep in order to wait until previous example finishes
78 | Thread.sleep(forTimeInterval: 4.0)
79 |
80 | example("Delay with calculator") {
81 | // custom delay calculator
82 | // will be invoked to calculate delay for particular repeat
83 | // will be invoked in the beginning of repeat
84 | let customCalculator: (UInt) -> Double = { attempt in
85 | switch attempt {
86 | case 1: return 0.5
87 | case 2: return 1.5
88 | default: return 2.0
89 | }
90 | }
91 | _ = completingObservable.repeatWithBehavior(.customTimerDelayed(maxCount: 3, delayCalculator: customCalculator), scheduler: delayScheduler)
92 | .subscribe(onNext: { event in
93 | print("Receive event: \(event)")
94 | })
95 | }
96 |
97 | // sleep in order to wait until previous example finishes
98 | Thread.sleep(forTimeInterval: 4.0)
99 |
100 | example("Observable with error") {
101 | _ = erroringObservable.repeatWithBehavior(.immediate(maxCount: 2))
102 | .subscribe(onNext: { event in
103 | print("Receive event: \(event)")
104 | }, onError: { error in
105 | print("Repetition interrupted with error: \(error)")
106 | })
107 | }
108 |
109 | playgroundShouldContinueIndefinitely()
110 |
111 | //: [Next](@next)
112 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/retryWithBehavior.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import Foundation
13 | import RxSwift
14 | import RxSwiftExt
15 |
16 | private enum SampleErrors : Error {
17 | case fatalError
18 | }
19 |
20 | let sampleObservable = Observable.create { observer in
21 | observer.onNext("First")
22 | observer.onNext("Second")
23 | observer.onError(SampleErrors.fatalError)
24 | observer.onCompleted()
25 | return Disposables.create()
26 | }
27 |
28 | let delayScheduler = SerialDispatchQueueScheduler(qos: .utility)
29 |
30 | example("Immediate retry") {
31 | // after receiving error will immediate retry
32 | _ = sampleObservable.retry(.immediate(maxCount: 3))
33 | .subscribe(onNext: { event in
34 | print("Receive event: \(event)")
35 | }, onError: { error in
36 | print("Receive error \(error)")
37 | })
38 | }
39 |
40 | example("Immediate retry with custom predicate") {
41 | // in this case we provide custom predicate, that will evaluate error and decide, should we retry or not
42 | _ = sampleObservable.retry(.immediate(maxCount: 3), scheduler: delayScheduler) { error in
43 | // error checks here
44 | // in this example we simply say, that retry not allowed
45 | return false
46 | }
47 | .subscribe(onNext: { event in
48 | print("Receive event: \(event)")
49 | }, onError: { error in
50 | print("Receive error \(error)")
51 | })
52 | }
53 |
54 | example("Delayed retry") {
55 | // after error, observable will be retried after 1.0 second delay
56 | _ = sampleObservable.retry(.delayed(maxCount: 3, time: 1.0), scheduler: delayScheduler)
57 | .subscribe(onNext: { event in
58 | print("Receive event: \(event)")
59 | }, onError: { error in
60 | print("Receive error: \(error)")
61 | })
62 | }
63 |
64 | // sleep in order to wait until previous example finishes
65 | Thread.sleep(forTimeInterval: 2.5)
66 |
67 | example("Exponential delay") {
68 | // in case of an error initial delay will be 1 second,
69 | // every next delay will be doubled
70 | // delay formula is: initial * pow(1 + multiplier, Double(currentRepetition - 1)), so multiplier 1.0 means, delay will doubled
71 | _ = sampleObservable.retry(.exponentialDelayed(maxCount: 3, initial: 1.0, multiplier: 1.0), scheduler: delayScheduler)
72 | .subscribe(onNext: { event in
73 | print("Receive event: \(event)")
74 | }, onError: { error in
75 | print("Receive error: \(error)")
76 | })
77 | }
78 |
79 | // sleep in order to wait until previous example finishes
80 | Thread.sleep(forTimeInterval: 4.0)
81 |
82 | example("Delay with calculator") {
83 | // custom delay calculator
84 | // will be invoked to calculate delay for particular repetition
85 | // will be invoked in the beginning of repetition, not when error occurred
86 | let customCalculator: (UInt) -> Double = { attempt in
87 | switch attempt {
88 | case 1: return 0.5
89 | case 2: return 1.5
90 | default: return 2.0
91 | }
92 | }
93 | _ = sampleObservable.retry(.customTimerDelayed(maxCount: 3, delayCalculator: customCalculator), scheduler: delayScheduler)
94 | .subscribe(onNext: { event in
95 | print("Receive event: \(event)")
96 | }, onError: { error in
97 | print("Receive error: \(error)")
98 | })
99 | }
100 |
101 | playgroundShouldContinueIndefinitely()
102 |
103 | //: [Next](@next)
104 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/toSortedArray.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 | import RxSwift
12 | import RxSwiftExt
13 |
14 | /*:
15 | ## toSortedArray
16 |
17 | The `toSortedArray` operator transforms an observable of comparables into an observable of ordered arrays.
18 | */
19 |
20 | example("Ensure that only a sorted array is emitted") {
21 | let sequenceStream = Observable.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
22 | let array = Observable.of(10, 9, 12, 8, 4, 1, 1, 8, 14)
23 |
24 | // Ascending order
25 | array.toSortedArray()
26 | .subscribe(onNext: { result in
27 | print(result)
28 | })
29 |
30 | array.toSortedArray(ascending: true)
31 | .subscribe(onNext: { result in
32 | print(result)
33 | })
34 |
35 | // Descending order
36 | sequenceStream.toSortedArray(by: >)
37 | .subscribe(onNext: { result in
38 | print(result)
39 | })
40 |
41 | array.toSortedArray(ascending: false)
42 | .subscribe(onNext: { result in
43 | print(result)
44 | })
45 | }
46 |
47 | //: [Next](@next)
48 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/unwrap.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## unwrap()
17 |
18 | The `unwrap` operator takes a sequence of optional elements
19 | and returns a sequence of non-optional elements, filtering out any `nil` values.
20 | */
21 | example("unwrap optional values") {
22 |
23 | let numbers = Array([1, 2, 3])
24 | Observable.from(numbers)
25 | .unwrap()
26 | .toArray()
27 | .subscribe(onNext: { result in
28 | // look types on the right panel ===>
29 | numbers
30 | result
31 | print("unwrap() transformed \(numbers) to \(result)")
32 | })
33 | }
34 |
35 | Observable.of(1,2,nil,Int?(4))
36 | .unwrap()
37 | .subscribe { print($0) }
38 |
39 | example("unwrap and filter out nil values") {
40 |
41 | let numbers = [1, 2, nil, Int?(4)]
42 | Observable.from(numbers)
43 | .unwrap()
44 | .toArray()
45 | .subscribe(onNext: { result in
46 | // look types on the right panel ===>
47 | numbers
48 | result
49 | print("unwrap() transformed \(numbers) to \(result)")
50 | })
51 | }
52 |
53 | example("unwrap and filter out nil values for a Driver") {
54 |
55 | let numbers = [1, 2, nil, Int?(4)]
56 | let numbersSubject = BehaviorSubject(value: nil)
57 |
58 | numbersSubject
59 | .asDriver(onErrorJustReturn: nil)
60 | .unwrap()
61 | .asObservable()
62 | .toArray()
63 | .subscribe(onNext: { result in
64 | // look types on the right panel ===>
65 | numbers
66 | result
67 | print("unwrap() transformed \(numbers) to \(result)")
68 | })
69 |
70 | Observable.from(numbers)
71 | .bind(to: numbersSubject)
72 | }
73 |
74 | //: [Next](@next)
75 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/withUnretained.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | class TestClass: CustomStringConvertible {
16 | var description: String { return "Test Class" }
17 | }
18 |
19 | example("withUnretained") {
20 | var testClass: TestClass! = TestClass()
21 |
22 | _ = Observable
23 | .of(1, 2, 3, 5, 8, 13, 18, 21, 23)
24 | .withUnretained(testClass)
25 | .debug("Combined Object with Emitted Events")
26 | .do(onNext: { _, value in
27 | if value == 13 {
28 | // When testClass becomes nil, the next emission of the original
29 | // sequence will try to retain it and fail. As soon as it fails,
30 | // the sequence will complete.
31 | testClass = nil
32 | }
33 | })
34 | .subscribe()
35 | }
36 | //: [Next](@next)
37 |
38 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Pages/zipWith.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | > # IMPORTANT: To use `RxSwiftExtPlayground.playground`, please:
3 |
4 | 1. Make sure you have [Carthage](https://github.com/Carthage/Carthage) installed
5 | 1. Fetch Carthage dependencies from shell: `carthage bootstrap --platform ios`
6 | 1. Build scheme `RxSwiftExtPlayground` scheme for a simulator target
7 | 1. Choose `View > Show Debug Area`
8 | */
9 |
10 | //: [Previous](@previous)
11 |
12 | import RxSwift
13 | import RxSwiftExt
14 |
15 | /*:
16 | ## zip(with:)
17 | Merges the specified observable sequences into one observable sequence by using the selector function whenever all
18 | of the observable sequences have produced an element at a corresponding index.
19 |
20 | */
21 | example("zip(with:)") {
22 | let numbers = [1,2,3]
23 | let strings = ["a", "b", "c"]
24 |
25 | let first = Observable.from(numbers)
26 | let second = Observable.from(strings)
27 |
28 | first.zip(with: second) { i, s in
29 | s + String(i)
30 | }
31 | .toArray()
32 | .subscribe(onNext: { result in
33 | print("zip(with:) merged \(numbers) with \(strings) to \(result)")
34 | })
35 | }
36 |
37 | //: [Next](@next)
38 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/Sources/SupportCode.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /**
4 | Encloses each code example in its own scope. Prints a `description` header and then executes the `action` closure.
5 | - parameter description: example description
6 | - parameter action: `Void` closure
7 | */
8 | public func example(_ description: String, action: () -> ()) {
9 | printExampleHeader(description)
10 | action()
11 | }
12 |
13 | public func printExampleHeader(_ description: String) {
14 | print("\n--- \(description) example ---")
15 | }
16 |
17 | /**
18 | Executes `closure` on main thread after `delay` seconds.
19 | - parameter delay: time in seconds to wait before executing `closure`
20 | - parameter closure: `Void` closure
21 | */
22 | public func delay(_ delay: Double, closure: @escaping () -> ()) {
23 |
24 | let delayTime = DispatchTime.now() + DispatchTimeInterval.seconds(Int(delay))
25 | DispatchQueue.main.asyncAfter(deadline: delayTime) {
26 | closure()
27 | }
28 | }
29 |
30 | #if NOT_IN_PLAYGROUND
31 |
32 | public func playgroundShouldContinueIndefinitely() { }
33 |
34 | #else
35 |
36 | import PlaygroundSupport
37 |
38 | public func playgroundShouldContinueIndefinitely() {
39 | PlaygroundPage.current.needsIndefiniteExecution = true
40 | }
41 |
42 | #endif
43 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/SupportCode.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RxSwiftCommunity/RxSwiftExt/eb4adf9f00a21b3efc3869a5218a6d7517e95222/Playground/RxSwiftExtPlayground.playground/SupportCode.o
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/SupportCode.remap:
--------------------------------------------------------------------------------
1 | [
2 | ]
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Playground/RxSwiftExtPlayground.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RxSwiftExt.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "RxSwiftExt"
3 | s.version = "6.2.1"
4 | s.summary = "RxSwift operators not found in the core distribtion"
5 | s.description = <<-DESC
6 | A collection of operators for RxSwift adding commonly requested operations not found in the core distribution
7 | of RxSwift.
8 | DESC
9 | s.homepage = "https://github.com/RxSwiftCommunity/RxSwiftExt"
10 | s.license = { :type => "MIT", :file => "LICENSE" }
11 | s.authors = { "RxSwiftCommunity" => "https://github.com/RxSwiftCommunity" }
12 |
13 | s.ios.deployment_target = '12.0'
14 | s.osx.deployment_target = '10.13'
15 | s.watchos.deployment_target = '4.0'
16 | s.tvos.deployment_target = '9.0'
17 |
18 | s.source = { :git => "https://github.com/RxSwiftCommunity/RxSwiftExt.git", :tag => s.version }
19 |
20 | s.subspec "Core" do |cs|
21 | cs.source_files = "Source/RxSwift", "Source/Tools"
22 | cs.frameworks = "Foundation"
23 | cs.dependency "RxSwift", '~> 6.0'
24 | end
25 |
26 | s.subspec "RxCocoa" do |co|
27 | co.source_files = "Source/RxCocoa"
28 | co.frameworks = "Foundation"
29 | co.dependency "RxCocoa", '~> 6.0'
30 | co.dependency "RxSwiftExt/Core"
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/RxSwiftExt.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RxSwiftExt.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/RxSwiftExt.xcodeproj/xcshareddata/xcschemes/RxSwiftExt-iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
51 |
52 |
53 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
76 |
77 |
83 |
84 |
85 |
86 |
92 |
93 |
99 |
100 |
101 |
102 |
104 |
105 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/RxSwiftExt.xcodeproj/xcshareddata/xcschemes/RxSwiftExt-macOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
42 |
48 |
49 |
50 |
51 |
52 |
62 |
63 |
69 |
70 |
71 |
72 |
78 |
79 |
85 |
86 |
87 |
88 |
90 |
91 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/RxSwiftExt.xcodeproj/xcshareddata/xcschemes/RxSwiftExt-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
42 |
48 |
49 |
50 |
51 |
52 |
62 |
63 |
69 |
70 |
71 |
72 |
78 |
79 |
85 |
86 |
87 |
88 |
90 |
91 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/RxSwiftExt.xcodeproj/xcshareddata/xcschemes/RxSwiftExtPlayground.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
52 |
53 |
59 |
60 |
66 |
67 |
68 |
69 |
71 |
72 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/RxSwiftExt.xcodeproj/xcshareddata/xcschemes/RxSwiftExtTests-iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
54 |
60 |
61 |
62 |
63 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/RxSwiftExt.xcodeproj/xcshareddata/xcschemes/RxSwiftExtTests-macOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
54 |
60 |
61 |
62 |
63 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/RxSwiftExt.xcodeproj/xcshareddata/xcschemes/RxSwiftExtTests-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
37 |
38 |
44 |
45 |
47 |
48 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/RxSwiftExt.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/RxSwiftExt.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Source/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 2.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Source/RxCocoa/UIScrollView+reachedBottom.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIScrollView+reachedBottom.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Anton Nazarov on 09/05/2019.
6 | // Copyright © 2019 RxSwift Community. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 | import UIKit
11 | import RxSwift
12 | import RxCocoa
13 |
14 | public extension Reactive where Base: UIScrollView {
15 | /**
16 | Shows if the bottom of the UIScrollView is reached.
17 | - parameter offset: A threshhold indicating the bottom of the UIScrollView.
18 | - returns: ControlEvent that emits when the bottom of the base UIScrollView is reached.
19 | */
20 | func reachedBottom(offset: CGFloat = 0.0) -> ControlEvent {
21 | let source = contentOffset.map { contentOffset in
22 | let visibleHeight = self.base.frame.height - self.base.contentInset.top - self.base.contentInset.bottom
23 | let y = contentOffset.y + self.base.contentInset.top
24 | let threshold = max(offset, self.base.contentSize.height - visibleHeight)
25 | return y >= threshold
26 | }
27 | .distinctUntilChanged()
28 | .filter { $0 }
29 | .map { _ in () }
30 | return ControlEvent(events: source)
31 | }
32 | }
33 | #endif
34 |
--------------------------------------------------------------------------------
/Source/RxCocoa/UIViewPropertyAnimator+Rx.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewPropertyAnimator+Rx.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Wittemberg, Thibault on 29/03/18.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 | import Foundation
11 | import UIKit
12 | import RxSwift
13 | import RxCocoa
14 |
15 | @available(iOS 10.0, *)
16 | public extension Reactive where Base: UIViewPropertyAnimator {
17 | /**
18 | Bindable extension for `fractionComplete` property.
19 | */
20 | var fractionComplete: Binder {
21 | return Binder(base) { propertyAnimator, fractionComplete in
22 | propertyAnimator.fractionComplete = fractionComplete
23 | }
24 | }
25 |
26 | /// Provides a Completable that triggers the UIViewPropertyAnimator upon subscription
27 | /// and completes once the animation ends.
28 | ///
29 | /// - Parameter afterDelay: the delay to apply to the animation start
30 | ///
31 | /// - Returns: Completable
32 | func animate(afterDelay delay: TimeInterval = 0) -> Completable {
33 | return Completable.create { [base] completable in
34 | base.addCompletion { position in
35 | guard position == .end else { return }
36 | completable(.completed)
37 | }
38 |
39 | if delay != 0 {
40 | base.startAnimation(afterDelay: delay)
41 | } else {
42 | base.startAnimation()
43 | }
44 |
45 | return Disposables.create {
46 | base.stopAnimation(true)
47 | }
48 | }
49 | }
50 | }
51 | #endif
52 |
--------------------------------------------------------------------------------
/Source/RxCocoa/distinct+RxCocoa.swift:
--------------------------------------------------------------------------------
1 | //
2 | // distinct+RxCocoa.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Rafael Ferreira on 3/8/17.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxCocoa
10 |
11 | extension SharedSequence {
12 | /**
13 | Suppress duplicate items emitted by an SharedSequence
14 | - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
15 | - parameter predicate: predicate determines whether element distinct
16 |
17 | - returns: An shared sequence only containing the distinct contiguous elements, based on predicate, from the source sequence.
18 | */
19 | public func distinct(_ predicate: @escaping (Element) -> Bool) -> SharedSequence {
20 | var cache = [Element]()
21 |
22 | return flatMap { element -> SharedSequence in
23 | if cache.contains(where: predicate) {
24 | return SharedSequence.empty()
25 | } else {
26 | cache.append(element)
27 |
28 | return SharedSequence.just(element)
29 | }
30 | }
31 | }
32 | }
33 |
34 | extension SharedSequence where Element: Equatable {
35 | /**
36 | Suppress duplicate items emitted by an SharedSequence
37 | - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
38 | - returns: An shared sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence.
39 | */
40 | public func distinct() -> SharedSequence {
41 | var cache = [Element]()
42 |
43 | return flatMap { element -> SharedSequence in
44 | if cache.contains(element) {
45 | return SharedSequence.empty()
46 | } else {
47 | cache.append(element)
48 |
49 | return SharedSequence.just(element)
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Source/RxCocoa/mapTo+RxCocoa.swift:
--------------------------------------------------------------------------------
1 | //
2 | // mapTo+RxCocoa.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Rafael Ferreira on 3/7/17.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxCocoa
10 |
11 | extension SharedSequenceConvertibleType {
12 | /**
13 | Returns an Unit containing as many elements as its input but all of them are the constant provided as a parameter
14 |
15 | - parameter value: A constant that each element of the input sequence is being replaced with
16 | - returns: An unit containing the values `value` provided as a parameter
17 | */
18 | public func mapTo(_ value: Result) -> SharedSequence {
19 | return map { _ in value }
20 | }
21 |
22 | @available(*, deprecated, renamed: "mapTo(_:)")
23 | public func map(to value: Result) -> SharedSequence {
24 | return map { _ in value }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Source/RxCocoa/not+RxCocoa.swift:
--------------------------------------------------------------------------------
1 | //
2 | // not+RxCocoa.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Rafael Ferreira on 3/7/17.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxCocoa
10 | import RxSwift
11 |
12 | extension SharedSequenceConvertibleType where Element == Bool {
13 | /// Boolean not operator.
14 | public func not() -> SharedSequence {
15 | return map(!)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Source/RxCocoa/partition+RxCocoa.swift:
--------------------------------------------------------------------------------
1 | //
2 | // partition+RxCocoa.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Shai Mishali on 24/11/2018.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 | import RxCocoa
11 |
12 | public extension SharedSequence {
13 | /**
14 | Partition a stream into two separate streams of elements that match, and don't match, the provided predicate.
15 |
16 | - parameter predicate: A predicate used to filter matching and non-matching elements.
17 |
18 | - returns: A tuple of two streams of elements that match, and don't match, the provided predicate.
19 | */
20 | func partition(_ predicate: @escaping (Element) -> Bool) -> (matches: SharedSequence,
21 | nonMatches: SharedSequence) {
22 | let stream = self.map { ($0, predicate($0)) }
23 |
24 | let hits = stream.filter { $0.1 }.map { $0.0 }
25 | let misses = stream.filter { !$0.1 }.map { $0.0 }
26 |
27 | return (hits, misses)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Source/RxCocoa/unwrap+SharedSequence.swift:
--------------------------------------------------------------------------------
1 | //
2 | // unwrap+SharedSequence.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Hugo Saynac on 05/10/2018.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxCocoa
10 |
11 | extension SharedSequence {
12 |
13 | /**
14 | Takes a SharedSequence of optional elements and returns a SharedSequence of non-optional elements, filtering out any nil values.
15 |
16 | - returns: A SharedSequence of non-optional elements
17 | */
18 |
19 | public func unwrap() -> SharedSequence where Element == Result? {
20 | return self.filter { $0 != nil }.map { $0! }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Source/RxSwift/and.swift:
--------------------------------------------------------------------------------
1 | //
2 | // and.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Florent Pillet on 26/11/17.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType where Element == Bool {
13 | /**
14 | Emits a single Bool value indicating whether or not a Bool sequence emits only `true` values.
15 |
16 | If a `false` value is emitted, the resulting sequence immediately completes with a `false` result.
17 | If only `true` values are emitted, the resulting sequence completes with a `true` result once the
18 | source sequence completes.
19 | If no value is emitted, the resulting sequence completes with no value once the source sequence completes.
20 |
21 | Use `asSingle()` or `asObservable()` to convert to your requirements.
22 | */
23 | public func and() -> Maybe {
24 | return Maybe.create { observer in
25 | var gotValue = false
26 | return self.subscribe { event in
27 | switch event {
28 | case .next(let value):
29 | if !value {
30 | // first `false` value emits false & completes
31 | observer(.success(false))
32 | } else {
33 | gotValue = true
34 | }
35 | case .error(let error):
36 | observer(.error(error))
37 | case .completed:
38 | observer(gotValue ? .success(true) : .completed)
39 | }
40 | }
41 | }
42 | }
43 |
44 | /**
45 | Emits a single Bool value indicating whether or not a each Bool sequence in the collection emits only `true` values.
46 |
47 | Each sequence of the collection is expected to emit at least one `true` value.
48 | If any sequence does not emit anything, the produced `Maybe` will just complete.
49 | If any sequence emits a `false` value, the produiced `Maybe` will emit a `false` result.
50 | If all sequences emit at least one `true` value, the produced `Maybe` will emit a `true` result.
51 |
52 | Use `asSingle()` or `asObservable()` to convert to your requirements.
53 | */
54 | public static func and(_ collection: Collection)
55 | -> Maybe where Collection.Element: ObservableType, Collection.Element.Element == Element {
56 | return Maybe.create { observer in
57 | var emitted = [Bool](repeating: false, count: Int(collection.count))
58 | var completed = 0
59 | let lock = NSRecursiveLock()
60 | lock.lock()
61 | defer { lock.unlock() }
62 | let subscriptions = collection.enumerated().map { item in
63 | item.element.subscribe { event in
64 | lock.lock()
65 | defer { lock.unlock() }
66 | switch event {
67 | case .next(let value):
68 | if !value {
69 | // first `false` value emits false & completes
70 | observer(.success(false))
71 | } else {
72 | emitted[item.offset] = true
73 | }
74 | case .error(let error):
75 | observer(.error(error))
76 | case .completed:
77 | completed += 1
78 | guard completed == collection.count else { return }
79 | // if all emitted at least one `true`, emit true otherwise just complete
80 | if emitted.allSatisfy({ $0 }) {
81 | observer(.success(true))
82 | } else {
83 | observer(.completed)
84 | }
85 | }
86 |
87 | }
88 | }
89 | return CompositeDisposable(disposables: subscriptions)
90 | }
91 | }
92 |
93 | /**
94 | Emits a single Bool value indicating whether or not a each Bool sequence in the collection emits only `true` values.
95 |
96 | Each sequence of the collection is expected to emit at least one `true` value.
97 | If any sequence does not emit anything, the produced `Maybe` will just complete.
98 | If any sequence emits a `false` value, the produiced `Maybe` will emit a `false` result.
99 | If all sequences emit at least one `true` value, the produced `Maybe` will emit a `true` result.
100 |
101 | Use `asSingle()` or `asObservable()` to convert to your requirements.
102 | */
103 | public static func and(_ sources: Observable ...) -> Maybe where Observable.Element == Element {
104 | return and(sources)
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Source/RxSwift/apply.swift:
--------------------------------------------------------------------------------
1 | //
2 | // apply.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Andy Chou on 2/22/17.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType {
13 | /// Apply a transformation function to the Observable.
14 | public func apply(_ transform: (Observable) -> Observable) -> Observable {
15 | return transform(self.asObservable())
16 | }
17 | }
18 |
19 | extension PrimitiveSequenceType {
20 | /// Apply a transformation function to the primitive sequence.
21 | public func apply(_ transform: (PrimitiveSequence) -> PrimitiveSequence)
22 | -> PrimitiveSequence {
23 | return transform(self.primitiveSequence)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Source/RxSwift/bufferWithTrigger.swift:
--------------------------------------------------------------------------------
1 | //
2 | // bufferWithTrigger.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Patrick Maltagliati on 11/12/18.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType {
13 | /**
14 | Collects the elements of the source observable, and emits them as an array when the trigger emits.
15 |
16 | - parameter trigger: The observable sequence used to signal the emission of the buffered items.
17 | - returns: The buffered observable from elements of the source sequence.
18 | */
19 | public func bufferWithTrigger(_ trigger: Trigger) -> Observable<[Element]> {
20 | return Observable.create { observer in
21 | var buffer: [Element] = []
22 | let lock = NSRecursiveLock()
23 | let triggerDisposable = trigger.subscribe { event in
24 | lock.lock(); defer { lock.unlock() }
25 | switch event {
26 | case .next:
27 | observer.onNext(buffer)
28 | buffer = []
29 | default:
30 | break
31 | }
32 | }
33 | let disposable = self.subscribe { event in
34 | lock.lock(); defer { lock.unlock() }
35 | switch event {
36 | case .next(let element):
37 | buffer.append(element)
38 | case .completed:
39 | observer.onNext(buffer)
40 | observer.onCompleted()
41 | case .error(let error):
42 | observer.onError(error)
43 | buffer = []
44 | }
45 | }
46 | return Disposables.create([disposable, triggerDisposable])
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Source/RxSwift/catchErrorJustComplete.swift:
--------------------------------------------------------------------------------
1 | //
2 | // catchErrorJustComplete.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Florent Pillet on 21/05/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | extension ObservableType {
12 | /**
13 | Dismiss errors and complete the sequence instead
14 |
15 | - returns: An observable sequence that never errors and completes when an error occurs in the underlying sequence
16 | */
17 | public func catchErrorJustComplete() -> Observable {
18 | return `catch` { _ in
19 | return Observable.empty()
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Source/RxSwift/count.swift:
--------------------------------------------------------------------------------
1 | //
2 | // count.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Fred on 06/11/2018.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension Observable {
13 | /**
14 | Count the number of items emitted by an Observable
15 | - seealso: [count operator on reactivex.io](http://reactivex.io/documentation/operators/count.html)
16 | - returns: An Observable sequence containing a value that represents how many elements in the specified observable sequence satisfy a condition if provided, else the count of items.
17 | */
18 | public func count() -> Observable {
19 | return reduce(0) { count, _ in count + 1 }
20 | }
21 | /**
22 | Count the number of items emitted by an Observable
23 | - seealso: [count operator on reactivex.io](http://reactivex.io/documentation/operators/count.html)
24 | - parameter predicate: predicate determines what elements to be counted.
25 |
26 | - returns: An Observable sequence containing a value that represents how many elements in the specified observable sequence satisfy a condition if provided, else the count of items.
27 | */
28 | public func count(_ predicate: @escaping (Element) throws -> Bool) -> Observable {
29 | return filter(predicate).count()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Source/RxSwift/distinct.swift:
--------------------------------------------------------------------------------
1 | //
2 | // distinct.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Segii Shulga on 5/4/16.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension Observable {
13 | /**
14 | Suppress duplicate items emitted by an Observable
15 | - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
16 | - parameter predicate: predicate determines whether element distinct
17 |
18 | - returns: An observable sequence only containing the distinct contiguous elements, based on predicate, from the source sequence.
19 | */
20 | public func distinct(_ predicate: @escaping (Element) throws -> Bool) -> Observable {
21 | var cache = [Element]()
22 | return flatMap { element -> Observable in
23 | if try cache.contains(where: predicate) {
24 | return Observable.empty()
25 | } else {
26 | cache.append(element)
27 | return Observable.just(element)
28 | }
29 | }
30 | }
31 | }
32 |
33 | extension Observable where Element: Hashable {
34 | /**
35 | Suppress duplicate items emitted by an Observable
36 | - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
37 | - returns: An observable sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence.
38 | */
39 | public func distinct() -> Observable {
40 | var cache = Set()
41 | return flatMap { element -> Observable in
42 | if cache.contains(element) {
43 | return Observable.empty()
44 | } else {
45 | cache.insert(element)
46 | return Observable.just(element)
47 | }
48 | }
49 | }
50 | }
51 |
52 | extension Observable where Element: Equatable {
53 | /**
54 | Suppress duplicate items emitted by an Observable
55 | - seealso: [distinct operator on reactivex.io](http://reactivex.io/documentation/operators/distinct.html)
56 | - returns: An observable sequence only containing the distinct contiguous elements, based on equality operator, from the source sequence.
57 | */
58 | public func distinct() -> Observable {
59 | var cache = [Element]()
60 | return flatMap { element -> Observable in
61 | if cache.contains(element) {
62 | return Observable.empty()
63 | } else {
64 | cache.append(element)
65 | return Observable.just(element)
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Source/RxSwift/filterMap.swift:
--------------------------------------------------------------------------------
1 | //
2 | // filterMap.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Jeremie Girault on 31/05/2017.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | public enum FilterMap {
12 | case ignore
13 | case map(Result)
14 | }
15 |
16 | extension ObservableType {
17 |
18 | /**
19 | Filters or Maps values from the source.
20 | - The returned Observable will complete with the source. It will error with the source or with error thrown by transform callback.
21 | - `next` values will be output according to the `transform` callback result:
22 | - returning `.ignore` will filter the value out of the returned Observable
23 | - returning `.map(newValue)` will propagate newValue through the returned Observable.
24 | */
25 | public func filterMap(_ transform: @escaping (Element) throws -> FilterMap) -> Observable {
26 | return compactMap { element in
27 | switch try transform(element) {
28 | case .ignore:
29 | return nil
30 | case let .map(result):
31 | return result
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Source/RxSwift/ignore.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ignore.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Florent Pillet on 10/04/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType where Element: Equatable {
13 | public func ignore(_ valuesToIgnore: Element...) -> Observable {
14 | return self.asObservable().filter { !valuesToIgnore.contains($0) }
15 | }
16 |
17 | public func ignore(_ valuesToIgnore: Sequence) -> Observable where Sequence.Element == Element {
18 | return self.asObservable().filter { !valuesToIgnore.contains($0) }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Source/RxSwift/ignoreErrors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ignoreErrors.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Florent Pillet on 18/05/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | extension ObservableType {
12 | /**
13 | Unconditionally ignore all errors produced by the source observable, effectively producing a sequence
14 | that never fails (any error will simply have no effect on the sequence).
15 |
16 | - returns: An observable sequence that never fails
17 | - seealso: `retry` operator
18 | */
19 | public func ignoreErrors() -> Observable {
20 | return retry()
21 | }
22 |
23 | /**
24 | Conditionally ignore errors produced by the source observable
25 |
26 | - parameter predicate a predicate called when an error occurs and returns `true` to ignore the error (continuing), `false` to terminate the sequence with the given error.
27 | - returns: An observable sequence that errors only when `predicate` returns `false`
28 | */
29 | public func ignoreErrors(_ predicate : @escaping (Error) -> Bool) -> Observable {
30 | return retry(when: {
31 | return $0.flatMap { error -> Observable in
32 | return predicate(error) ? Observable.just(true) : Observable.error(error)
33 | }
34 | })
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Source/RxSwift/ignoreWhen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ignoreWhen.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Florent Pillet on 14/04/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType {
13 |
14 | /**
15 | Ignores the elements of an observable sequence based on a predicate.
16 |
17 | - seealso: [filter operator on reactivex.io](http://reactivex.io/documentation/operators/filter.html)
18 | - seealso: [ignoreElements operator on reactivex.io](http://reactivex.io/documentation/operators/ignoreelements.html)
19 |
20 | - parameter predicate: A function to test each source element for a condition.
21 | - returns: An observable sequence that contains elements from the input sequence except those that satisfy the condition.
22 | */
23 | public func ignoreWhen(_ predicate: @escaping (Element) throws -> Bool) -> Observable {
24 | return self.asObservable().filter { try !predicate($0) }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Source/RxSwift/mapAt.swift:
--------------------------------------------------------------------------------
1 | //
2 | // mapAt.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Michael Avila on 2/8/18.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | extension ObservableType {
12 |
13 | /**
14 | Returns an observable sequence containing as many elements as its input but all of them are mapped to the result at the given keyPath
15 |
16 | - parameter keyPath: A key path whose root type matches the element type of the input sequence
17 | - returns: An observable squence containing the values pointed to by the key path
18 | */
19 | public func mapAt(_ keyPath: KeyPath) -> Observable {
20 | return self.map { $0[keyPath: keyPath] }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Source/RxSwift/mapMany.swift:
--------------------------------------------------------------------------------
1 | //
2 | // mapMany.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Joan Disho on 06/05/18.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | extension ObservableType where Element: Collection {
12 | /**
13 | Projects each element of an observable collection into a new form.
14 |
15 | - parameter transform: A transform function to apply to each element of the source collection.
16 | - returns: An observable collection whose elements are the result of invoking the transform function on each element of source.
17 | */
18 | public func mapMany(_ transform: @escaping (Element.Element) throws -> Result) -> Observable<[Result]> {
19 | return map { collection -> [Result] in
20 | try collection.map(transform)
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Source/RxSwift/mapTo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // mapTo.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Marin Todorov on 4/12/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType {
13 |
14 | /**
15 | Returns an observable sequence containing as many elements as its input but all of them are the constant provided as a parameter
16 |
17 | - parameter value: A constant that each element of the input sequence is being replaced with
18 | - returns: An observable sequence containing the values `value` provided as a parameter
19 | */
20 | public func mapTo(_ value: Result) -> Observable {
21 | return map { _ in value }
22 | }
23 | }
24 |
25 | extension PrimitiveSequence where Trait == SingleTrait {
26 | /**
27 | Returns a Single which success event is mapped to constant provided as a parameter
28 |
29 | - parameter value: A constant that element of the input sequence is being replaced with
30 | - returns: A Single containing the value `value` provided as a parameter in case of success
31 | */
32 | public func mapTo(_ value: Result) -> Single {
33 | return map { _ in value }
34 | }
35 | }
36 |
37 | extension PrimitiveSequence where Trait == MaybeTrait {
38 | /**
39 | Returns a Maybe which success event is mapped to constant provided as a parameter
40 |
41 | - parameter value: A constant that element of the input sequence is being replaced with
42 | - returns: A Maybe containing the value `value` provided as a parameter in case of success
43 | */
44 | public func mapTo(_ value: Result) -> Maybe {
45 | return map { _ in value }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Source/RxSwift/materialized+elements.swift:
--------------------------------------------------------------------------------
1 | //
2 | // materialized+elements.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Andy Chou on 1/5/17.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType where Element: EventConvertible {
13 |
14 | /**
15 | Returns an observable sequence containing only next elements from its input
16 | - seealso: [materialize operator on reactivex.io](http://reactivex.io/documentation/operators/materialize-dematerialize.html)
17 | */
18 | public func elements() -> Observable {
19 | return compactMap { $0.event.element }
20 | }
21 |
22 | /**
23 | Returns an observable sequence containing only error elements from its input
24 | - seealso: [materialize operator on reactivex.io](http://reactivex.io/documentation/operators/materialize-dematerialize.html)
25 | */
26 | public func errors() -> Observable {
27 | return compactMap { $0.event.error }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Source/RxSwift/mergeWith.swift:
--------------------------------------------------------------------------------
1 | //
2 | // mergeWith.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Joan Disho on 12/05/18.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension Observable {
13 | /**
14 | Merges elements from the observable sequence with those of a different observable sequence into a single observable sequence.
15 |
16 | - parameter with: Other observable.
17 | - returns: The observable sequence that merges the elements of the observable sequences.
18 | */
19 | public func merge(with other: Observable) -> Observable {
20 | return Observable.merge(self, other)
21 | }
22 |
23 | /**
24 | Merges elements from the observable sequence with those of a different observable sequences into a single observable sequence.
25 |
26 | - parameter with: Other observables.
27 | - returns: The observable sequence that merges the elements of the observable sequences.
28 | */
29 | public func merge(with others: [Observable]) -> Observable {
30 | return Observable.merge([self] + others)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Source/RxSwift/not.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ignore.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Thane Gill on 18/04/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType where Element == Bool {
13 | /// Boolean not operator
14 | public func not() -> Observable {
15 | return self.map(!)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Source/RxSwift/nwise.swift:
--------------------------------------------------------------------------------
1 | //
2 | // nwise.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Zsolt Váradi on 09/12/2017.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | extension ObservableType {
12 | /**
13 | Groups the elements of the source observable into arrays of N consecutive elements.
14 |
15 | The resulting observable:
16 | - does not emit anything until the source observable emits at least N elements;
17 | - emits an array for every element after that;
18 | - forwards any error or completed events.
19 |
20 | For example:
21 |
22 | --(1)--(2)--(3)-------(4)-------(5)------->
23 | |
24 | | nwise(3)
25 | v
26 | ------------([1,2,3])-([2,3,4])-([3,4,5])->
27 |
28 | - parameter n: size of the groups, must be greater than 1
29 | */
30 | public func nwise(_ n: Int) -> Observable<[Element]> {
31 | assert(n > 1, "n must be greater than 1")
32 | return self
33 | .scan([]) { acc, item in Array((acc + [item]).suffix(n)) }
34 | .filter { $0.count == n }
35 | }
36 |
37 | /**
38 | Groups the elements of the source observable into tuples of the previous and current elements.
39 |
40 | The resulting observable:
41 | - does not emit anything until the source observable emits at least 2 elements;
42 | - emits a tuple for every element after that, consisting of the previous and the current item;
43 | - forwards any error or completed events.
44 |
45 | For example:
46 |
47 | --(1)--(2)--(3)-------(4)-------(5)------->
48 | |
49 | | pairwise()
50 | v
51 | -------(1,2)-(2,3)----(3,4)-----(4,5)----->
52 | */
53 | public func pairwise() -> Observable<(Element, Element)> {
54 | return self.nwise(2)
55 | .map { ($0[0], $0[1]) }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Source/RxSwift/ofType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ofType.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Nate Kim on 19/01/2018.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType {
13 |
14 | /**
15 | Filters the elements of an observable sequence, if that is an instance of the supplied type.
16 |
17 | - seealso: [filter operator on reactivex.io](http://reactivex.io/documentation/operators/filter.html)
18 |
19 | - parameter type: The Type to filter each source element.
20 | - returns: An observable sequence that contains elements which is an instance of the suppplied type.
21 | */
22 | public func ofType(_ type: Result.Type) -> Observable {
23 | return self.filterMap {
24 | guard let result = $0 as? Result else { return .ignore }
25 | return .map(result)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Source/RxSwift/once.swift:
--------------------------------------------------------------------------------
1 | //
2 | // once.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Florent Pillet on 12/04/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension Observable {
13 |
14 | /**
15 | Returns an observable sequence that contains a single element. This element will be delivered to the first observer
16 | that subscribes. Further subscriptions to the same observable will get an empty sequence.
17 |
18 | In most cases, you want to use `Observable.just()`. This one is really for specific cases where you need to guarantee
19 | unique delivery of a value.
20 |
21 | - seealso: [just operator on reactivex.io](http://reactivex.io/documentation/operators/just.html)
22 |
23 | - parameter element: Single element in the resulting observable sequence.
24 | - returns: An observable sequence containing the single specified element delivered once.
25 | */
26 |
27 | public static func once(_ element: Element) -> Observable {
28 | let lock = NSRecursiveLock()
29 | var isDelivered = false
30 | return create { observer in
31 | lock.lock()
32 | if !isDelivered {
33 | observer.onNext(element)
34 | }
35 | isDelivered = true
36 | lock.unlock()
37 | observer.onCompleted()
38 | return Disposables.create()
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/Source/RxSwift/partition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // partition.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Shai Mishali on 24/11/2018.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | public extension ObservableType {
12 | /**
13 | Partition a stream into two separate streams of elements that match, and don't match, the provided predicate.
14 |
15 | - parameter predicate: A predicate used to filter matching and non-matching elements.
16 |
17 | - returns: A tuple of two streams of elements that match, and don't match, the provided predicate.
18 | */
19 | func partition(_ predicate: @escaping (Element) throws -> Bool) -> (matches: Observable, nonMatches: Observable) {
20 | let stream = self.map { ($0, try predicate($0)) }.share()
21 |
22 | let hits = stream.filter { $0.1 }.map { $0.0 }
23 | let misses = stream.filter { !$0.1 }.map { $0.0 }
24 |
25 | return (hits, misses)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Source/RxSwift/pausable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // pausable.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Jesse Farless on 12/09/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType {
13 | /**
14 | Pauses the elements of the source observable sequence based on the latest element from the second observable sequence.
15 |
16 | Elements are ignored unless the second sequence has most recently emitted `true`.
17 |
18 | - seealso: [pausable operator on reactivex.io](http://reactivex.io/documentation/operators/backpressure.html)
19 |
20 | - parameter pauser: The observable sequence used to pause the source observable sequence.
21 | - returns: The observable sequence which is paused based upon the pauser observable sequence.
22 | */
23 |
24 | public func pausable (_ pauser: Pauser) -> Observable where Pauser.Element == Bool {
25 | return withLatestFrom(pauser) { element, paused in
26 | (element, paused)
27 | }
28 | .filter { _, paused in paused }
29 | .map { element, _ in element }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Source/RxSwift/pausableBuffered.swift:
--------------------------------------------------------------------------------
1 | //
2 | // pausableBuffered.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Tanguy Helesbeux on 24/05/2017.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType {
13 |
14 | /**
15 | Pauses the elements of the source observable sequence based on the latest element from the second observable sequence.
16 |
17 | While paused, elements from the source are buffered, limited to a maximum number of element.
18 |
19 | When resumed, all buffered elements are flushed as single events in a contiguous stream.
20 |
21 | - seealso: [pausable operator on reactivex.io](http://reactivex.io/documentation/operators/backpressure.html)
22 |
23 | - parameter pauser: The observable sequence used to pause the source observable sequence.
24 | - parameter limit: The maximum number of element buffered. Pass `nil` to buffer all elements without limit. Default 1.
25 | - parameter flushOnCompleted: If `true` buffered elements will be flushed when the source completes. Default `true`.
26 | - parameter flushOnError: If `true` buffered elements will be flushed when the source errors. Default `true`.
27 | - returns: The observable sequence which is paused and resumed based upon the pauser observable sequence.
28 | */
29 | public func pausableBuffered (_ pauser: Pauser, limit: Int? = 1, flushOnCompleted: Bool = true, flushOnError: Bool = true) -> Observable where Pauser.Element == Bool {
30 |
31 | return Observable.create { observer in
32 | var buffer: [Element] = []
33 | if let limit = limit {
34 | buffer.reserveCapacity(limit)
35 | }
36 |
37 | var paused = true
38 | var flushIndex = 0
39 | let lock = NSRecursiveLock()
40 |
41 | let flush = {
42 | while flushIndex < buffer.count {
43 | flushIndex += 1
44 | observer.onNext(buffer[flushIndex - 1])
45 | }
46 | if buffer.count > 0 {
47 | flushIndex = 0
48 | buffer.removeAll(keepingCapacity: limit != nil)
49 | }
50 | }
51 |
52 | let boundaryDisposable = pauser.distinctUntilChanged(==).subscribe { event in
53 | lock.lock(); defer { lock.unlock() }
54 | switch event {
55 | case .next(let resume):
56 | if resume && buffer.count > 0 {
57 | flush()
58 | }
59 | paused = !resume
60 |
61 | case .completed:
62 | observer.onCompleted()
63 | case .error(let error):
64 | observer.onError(error)
65 | }
66 | }
67 |
68 | let disposable = self.subscribe { event in
69 | lock.lock(); defer { lock.unlock() }
70 | switch event {
71 | case .next(let element):
72 | if paused {
73 | buffer.append(element)
74 | if let limit = limit, buffer.count > limit {
75 | buffer.remove(at: 0)
76 | }
77 | } else {
78 | observer.onNext(element)
79 | }
80 |
81 | case .completed:
82 | if flushOnCompleted { flush() }
83 | observer.onCompleted()
84 |
85 | case .error(let error):
86 | if flushOnError { flush() }
87 | observer.onError(error)
88 | }
89 | }
90 |
91 | return Disposables.create([disposable, boundaryDisposable])
92 | }
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/Source/RxSwift/repeatWithBehavior.swift:
--------------------------------------------------------------------------------
1 | //
2 | // repeatWithBehavior.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Marin Todorov on 05/08/2017.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | public typealias RepeatPredicate = () -> Bool
13 |
14 | /*
15 | Uses RepeatBehavior defined in retryWithBehavior
16 | */
17 |
18 | /** Dummy error to use with catchError to restart the observable */
19 | private enum RepeatError: Error {
20 | case catchable
21 | }
22 |
23 | extension ObservableType {
24 |
25 | /**
26 | Repeats the source observable sequence using given behavior when it completes
27 | - parameter behavior: Behavior that will be used when the observable completes
28 | - parameter scheduler: Schedular that will be used for delaying subscription after error
29 | - parameter shouldRepeat: Custom optional closure for decided whether the observable should repeat another round
30 | - returns: Observable sequence that will be automatically repeat when it completes
31 | */
32 | public func repeatWithBehavior(_ behavior: RepeatBehavior, scheduler: SchedulerType = MainScheduler.instance, shouldRepeat: RepeatPredicate? = nil) -> Observable {
33 | return repeatWithBehavior(1, behavior: behavior, scheduler: scheduler, shouldRepeat: shouldRepeat)
34 | }
35 |
36 | /**
37 | Repeats the source observable sequence using given behavior when it completes
38 | - parameter currentRepeat: Number of the current repetition
39 | - parameter behavior: Behavior that will be used in case of completion
40 | - parameter scheduler: Schedular that will be used for delaying subscription after error
41 | - parameter shouldRepeat: Custom optional closure for decided whether the observable should repeat another round
42 | - returns: Observable sequence that will be automatically repeat when it completes
43 | */
44 | internal func repeatWithBehavior(_ currentRepeat: UInt, behavior: RepeatBehavior, scheduler: SchedulerType = MainScheduler.instance, shouldRepeat: RepeatPredicate? = nil)
45 | -> Observable {
46 | guard currentRepeat > 0 else { return Observable.empty() }
47 |
48 | // calculate conditions for bahavior
49 | let conditions = behavior.calculateConditions(currentRepeat)
50 |
51 | return concat(Observable.error(RepeatError.catchable))
52 | .catch { error in
53 | //if observable errors, forward the error
54 | guard error is RepeatError else {
55 | return Observable.error(error)
56 | }
57 |
58 | //repeat
59 | guard conditions.maxCount > currentRepeat else { return Observable.empty() }
60 |
61 | if let shouldRepeat = shouldRepeat, !shouldRepeat() {
62 | // also return error if predicate says so
63 | return Observable.empty()
64 | }
65 |
66 | guard conditions.delay != .never else {
67 | // if there is no delay, simply retry
68 | return self.repeatWithBehavior(currentRepeat + 1, behavior: behavior, scheduler: scheduler, shouldRepeat: shouldRepeat)
69 | }
70 |
71 | // otherwise retry after specified delay
72 | return Observable.just(()).delaySubscription(conditions.delay, scheduler: scheduler).flatMapLatest {
73 | self.repeatWithBehavior(currentRepeat + 1, behavior: behavior, scheduler: scheduler, shouldRepeat: shouldRepeat)
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Source/RxSwift/toSortedArray.swift:
--------------------------------------------------------------------------------
1 | //
2 | // toSortedArray.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Joan Disho on 17/02/18.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | public extension ObservableType {
13 | /**
14 | Converts an Observable into another Observable that emits the whole sequence as a single array sorted using the provided closure and then terminates.
15 |
16 | - parameter by: A comparator closure to sort emitted elements.
17 | - returns: An observable sequence containing all the sorted emitted elements as an array.
18 | */
19 | func toSortedArray(by: @escaping (Element, Element) -> Bool) -> Single<[Element]> {
20 | return toArray().map { $0.sorted(by: by) }
21 | }
22 | }
23 |
24 | public extension ObservableType where Element: Comparable {
25 | /**
26 | Converts an Observable into another Observable that emits the whole sequence as a single sorted array and then terminates.
27 |
28 | - parameter ascending: Should the emitted items be ascending or descending.
29 | - returns: An observable sequence containing all the sorted emitted elements as an array.
30 | */
31 | func toSortedArray(ascending: Bool = true) -> Single<[Element]> {
32 | return toSortedArray(by: { ascending ? $0 < $1 : $0 > $1 })
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Source/RxSwift/unwrap.swift:
--------------------------------------------------------------------------------
1 | //
2 | // unwrap.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Marin Todorov on 4/7/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | extension ObservableType {
13 |
14 | /**
15 | Takes a sequence of optional elements and returns a sequence of non-optional elements, filtering out any nil values.
16 |
17 | - returns: An observable sequence of non-optional elements
18 | */
19 |
20 | public func unwrap() -> Observable where Element == Result? {
21 | return self.compactMap { $0 }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Source/RxSwift/zipWith.swift:
--------------------------------------------------------------------------------
1 | //
2 | // zipWith.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Arjan Duijzer on 26/12/2017.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | extension ObservableConvertibleType {
12 |
13 | /**
14 | Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
15 |
16 | - seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
17 |
18 | - Parameters:
19 | - with: A second Observable to zip with `self`
20 | - resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
21 |
22 | - Returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
23 | */
24 | public func zip(with second: Other, resultSelector: @escaping (Element, Other.Element) throws -> ResultType) -> Observable {
25 | return Observable.zip(asObservable(), second.asObservable(), resultSelector: resultSelector)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Source/Tools/Observable+Alias.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Observable+Alias.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Shai Mishali on 23/08/2023.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 |
11 | public typealias Observable = RxSwift.Observable
12 |
--------------------------------------------------------------------------------
/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Tests/RxCocoa/MapToTests+RxCocoa.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapToTests+RxCocoa.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Rafael Ferreira on 3/7/17.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import RxCocoa
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class MapToCocoaTests: XCTestCase {
16 |
17 | let numbers: [Int?] = [1, nil, Int?(3)]
18 | fileprivate var observer: TestableObserver!
19 |
20 | override func setUp() {
21 | super.setUp()
22 |
23 | let scheduler = TestScheduler(initialClock: 0)
24 | observer = scheduler.createObserver(String.self)
25 |
26 | _ = Observable.from(numbers)
27 | .asDriver(onErrorJustReturn: nil)
28 | .mapTo("candy")
29 | .drive(observer)
30 |
31 | scheduler.start()
32 | }
33 |
34 | func testReplaceWithResultCount() {
35 | XCTAssertEqual(
36 | observer.events.count-1 /*complete event*/,
37 | numbers.count
38 | )
39 | }
40 |
41 | func testReplaceWithResultValues() {
42 | // test elements values and type
43 | let correctValues = Recorded.events([
44 | .next(0, "candy"),
45 | .next(0, "candy"),
46 | .next(0, "candy"),
47 | .completed(0)
48 | ])
49 |
50 | XCTAssertEqual(observer.events, correctValues)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Tests/RxCocoa/NotTests+RxCocoa.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NotTests+RxCocoa.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Rafael Ferreira on 3/7/17.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import RxCocoa
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class NotCocoaTests: XCTestCase {
16 | func testNot() {
17 | let values = [true, false, true]
18 |
19 | let scheduler = TestScheduler(initialClock: 0)
20 | let observer = scheduler.createObserver(Bool.self)
21 |
22 | _ = Observable.from(values).asDriver(onErrorJustReturn: false)
23 | .not()
24 | .drive(observer)
25 |
26 | scheduler.start()
27 |
28 | let correct = Recorded.events([
29 | .next(0, false),
30 | .next(0, true),
31 | .next(0, false),
32 | .completed(0)
33 | ])
34 |
35 | XCTAssertEqual(observer.events, correct)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Tests/RxCocoa/PartitionTests+RxCocoa.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PartitionTests+RxCocoa.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Shai Mishali on 24/11/2018.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 | import RxCocoa
12 | import RxTest
13 | import XCTest
14 | import RxSwiftExt
15 |
16 | class PartitionSharedStrategyTests: XCTestCase {
17 | private var scheduler = TestScheduler(initialClock: 0)
18 | private var stream: Driver!
19 |
20 | override func setUp() {
21 | super.setUp()
22 | scheduler = TestScheduler(initialClock: 0)
23 | let events = (0...10).map { i -> Recorded> in
24 | return .next(i * 10, i)
25 | } + [.completed(100)]
26 |
27 | stream = scheduler
28 | .createHotObservable(events)
29 | .asDriver(onErrorDriveWith: .never())
30 | }
31 |
32 | func testPartitionBothMatch() {
33 | let (evens, odds) = stream.partition { $0 % 2 == 0 }
34 |
35 | let evensObserver = scheduler.createObserver(Int.self)
36 | _ = evens.drive(evensObserver)
37 |
38 | let oddsObserver = scheduler.createObserver(Int.self)
39 | _ = odds.drive(oddsObserver)
40 |
41 | scheduler.start()
42 |
43 | XCTAssertEqual(oddsObserver.events, Recorded.events([
44 | .next(10, 1),
45 | .next(30, 3),
46 | .next(50, 5),
47 | .next(70, 7),
48 | .next(90, 9),
49 | .completed(100)
50 | ]))
51 |
52 | XCTAssertEqual(evensObserver.events, Recorded.events([
53 | .next(0, 0),
54 | .next(20, 2),
55 | .next(40, 4),
56 | .next(60, 6),
57 | .next(80, 8),
58 | .next(100, 10),
59 | .completed(100)
60 | ]))
61 | }
62 |
63 | func testPartitionOneSideMatch() {
64 | let (all, none) = stream.partition { $0 <= 10 }
65 |
66 | let allObserver = scheduler.createObserver(Int.self)
67 | _ = all.drive(allObserver)
68 |
69 | let noneObserver = scheduler.createObserver(Int.self)
70 | _ = none.drive(noneObserver)
71 |
72 | scheduler.start()
73 |
74 | XCTAssertEqual(allObserver.events, Recorded.events([
75 | .next(0, 0),
76 | .next(10, 1),
77 | .next(20, 2),
78 | .next(30, 3),
79 | .next(40, 4),
80 | .next(50, 5),
81 | .next(60, 6),
82 | .next(70, 7),
83 | .next(80, 8),
84 | .next(90, 9),
85 | .next(100, 10),
86 | .completed(100)
87 | ]))
88 |
89 | XCTAssertEqual(noneObserver.events, [.completed(100)])
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Tests/RxCocoa/UIScrollView+reachedBottomTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIScrollView+reachedBottomTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Anton Nazarov on 09/05/2019.
6 | // Copyright © 2019 RxSwift Community. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import XCTest
11 | import RxCocoa
12 | import RxSwift
13 | import RxTest
14 | import RxSwiftExt
15 |
16 | final class UIScrollViewReachedBottomTests: XCTestCase {
17 | private var tableView: UITableView!
18 | private var dataSource: StubDataSource!
19 | private var scheduler: TestScheduler!
20 | private var observer: TestableObserver!
21 | private var disposeBag: DisposeBag!
22 |
23 | override func setUp() {
24 | super.setUp()
25 | dataSource = StubDataSource()
26 | tableView = UITableView()
27 | tableView.dataSource = dataSource
28 | tableView.rowHeight = 44
29 | tableView.reloadData()
30 | scheduler = TestScheduler(initialClock: 0)
31 | observer = scheduler.createObserver(Void.self)
32 | disposeBag = DisposeBag()
33 | }
34 |
35 | override func tearDown() {
36 | dataSource = nil
37 | tableView = nil
38 | scheduler = nil
39 | observer = nil
40 | super.tearDown()
41 | }
42 |
43 | func testReachedBottomNotEmitsIfNotScrolledToBottom() {
44 | // Given
45 | let almostBottomY = CGFloat(dataSource.cellsCount) * tableView.rowHeight - 1
46 | tableView.rx.reachedBottom().bind(to: observer).disposed(by: disposeBag)
47 | // When
48 | tableView.contentOffset = CGPoint(x: 0, y: almostBottomY)
49 | // Then
50 | XCTAssertTrue(observer.events.isEmpty)
51 | }
52 |
53 | func testReachedBottomNotEmitsIfNotScrolledToBottomWithNonZeroOffset() {
54 | // Given
55 | let offset: CGFloat = 40.0
56 | let almostBottomY = CGFloat(dataSource.cellsCount) * tableView.rowHeight - offset - 1
57 | tableView.rx.reachedBottom(offset: offset).bind(to: observer).disposed(by: disposeBag)
58 | // When
59 | tableView.contentOffset = CGPoint(x: 0, y: almostBottomY)
60 | // Then
61 | XCTAssertTrue(observer.events.isEmpty)
62 | }
63 |
64 | func testReachedBottomEmitsIfScrolledToBottom() {
65 | // Given
66 | let actual: [Recorded>] = [.next(0, ())]
67 | let bottomY = CGFloat(dataSource.cellsCount) * tableView.rowHeight
68 | tableView.rx.reachedBottom().bind(to: observer).disposed(by: disposeBag)
69 | // When
70 | tableView.contentOffset = CGPoint(x: 0, y: bottomY)
71 | // Then
72 | XCTAssertEqual(actual.count, observer.events.count)
73 | }
74 | }
75 |
76 | private extension UIScrollViewReachedBottomTests {
77 | final class StubDataSource: NSObject, UITableViewDataSource {
78 | let cellsCount = 28
79 |
80 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
81 | return cellsCount
82 | }
83 |
84 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
85 | return UITableViewCell()
86 | }
87 | }
88 | }
89 | #endif
90 |
--------------------------------------------------------------------------------
/Tests/RxCocoa/UIViewPropertyAnimatorTests+Rx.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewPropertyAnimatorTests+Rx.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Thibault Wittemberg on 3/4/18.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import XCTest
11 | import RxSwift
12 | import RxCocoa
13 | import RxSwiftExt
14 | import RxTest
15 | import UIKit
16 |
17 | @available(iOS 10.0, *)
18 | class UIViewPropertyAnimatorTests: XCTestCase {
19 | var disposeBag: DisposeBag!
20 |
21 | override func setUp() {
22 | disposeBag = DisposeBag()
23 | }
24 |
25 | func testAnimationCompleted() {
26 | let expectations = expectation(description: "Animation completed")
27 |
28 | let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
29 | let animator = UIViewPropertyAnimator(duration: 0.5, curve: .linear) {
30 | view.transform = CGAffineTransform(translationX: 100, y: 100)
31 | }
32 |
33 | animator
34 | .rx.animate()
35 | .subscribe(onCompleted: {
36 | XCTAssertEqual(100, view.frame.origin.x)
37 | XCTAssertEqual(100, view.frame.origin.y)
38 | expectations.fulfill()
39 | })
40 | .disposed(by: disposeBag)
41 |
42 | waitForExpectations(timeout: 1)
43 | }
44 |
45 | func testBindToFractionCompleted() {
46 | let animator = UIViewPropertyAnimator(
47 | duration: 0, curve: .linear, animations: { }
48 | )
49 |
50 | let subject = PublishSubject()
51 |
52 | subject
53 | .bind(to: animator.rx.fractionComplete)
54 | .disposed(by: disposeBag)
55 |
56 | subject.onNext(0.3)
57 | XCTAssertEqual(animator.fractionComplete, 0.3)
58 |
59 | subject.onNext(0.5)
60 | XCTAssertEqual(animator.fractionComplete, 0.5)
61 | }
62 | }
63 | #endif
64 |
--------------------------------------------------------------------------------
/Tests/RxCocoa/unrwapTests+SharedSequence.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UnwrapTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Hugo Saynac on 05/10/2018.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class UnwrapSharedStrategyTests: XCTestCase {
16 | let numbers: [Int?] = [1, nil, Int?(3), 4]
17 | private var observer: TestableObserver!
18 | private let numbersSubject = BehaviorSubject(value: nil)
19 |
20 | override func setUp() {
21 | super.setUp()
22 |
23 | let scheduler = TestScheduler(initialClock: 0)
24 | observer = scheduler.createObserver(Int.self)
25 |
26 | _ = numbersSubject
27 | .asDriver(onErrorJustReturn: nil)
28 | .unwrap()
29 | .asObservable()
30 | .subscribe(observer)
31 |
32 | _ = Observable.from(numbers)
33 | .bind(to: numbersSubject)
34 |
35 | scheduler.start()
36 | }
37 |
38 | func testUnwrapFilterNil() {
39 | //test results count
40 | print(observer.events)
41 | XCTAssertEqual(
42 | observer.events.count,
43 | numbers.count
44 | )
45 | }
46 |
47 | func testUnwrapResultValues() {
48 | // test elements values and type
49 | let correctValues = Recorded.events(
50 | .next(0, 1),
51 | .next(0, 3),
52 | .next(0, 4),
53 | .completed(0)
54 | )
55 | XCTAssertEqual(observer.events, correctValues)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Tests/RxSwift/BufferWithTriggerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BufferWithTriggerTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Patrick Maltagliati on 11/12/18.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class BufferWithTriggerTests: XCTestCase {
16 | let testError = NSError(domain: "dummyError", code: -232, userInfo: nil)
17 | let scheduler = TestScheduler(initialClock: 0)
18 |
19 | func testBuffersUntilBoundaryEmits() {
20 | let underlying = scheduler.createHotObservable(
21 | [
22 | .next(150, 1),
23 | .next(201, 2),
24 | .next(230, 3),
25 | .next(300, 4),
26 | .next(350, 5),
27 | .next(375, 6),
28 | .next(400, 7),
29 | .next(430, 8),
30 | .completed(500)
31 | ]
32 | )
33 |
34 | let boundary = scheduler.createHotObservable(
35 | [
36 | .next(201, ()),
37 | .next(301, ()),
38 | .next(401, ())
39 | ]
40 | )
41 |
42 | let res = scheduler.start(disposed: 1000) {
43 | underlying.bufferWithTrigger(boundary.asObservable())
44 | }
45 |
46 | let expected = Recorded.events([
47 | .next(201, [2]),
48 | .next(301, [3, 4]),
49 | .next(401, [5, 6, 7]),
50 | .next(500, [8]),
51 | .completed(500)
52 | ])
53 |
54 | XCTAssertEqual(res.events, expected)
55 |
56 | XCTAssertEqual(underlying.subscriptions, [Subscription(200, 500)])
57 | }
58 |
59 | func testPausedError() {
60 | let underlying = scheduler.createHotObservable(
61 | [
62 | .next(150, 1),
63 | .next(210, 2),
64 | .error(230, testError),
65 | .completed(500)
66 | ]
67 | )
68 |
69 | let boundary = scheduler.createHotObservable(
70 | [
71 | .next(201, ()),
72 | .next(211, ())
73 | ]
74 | )
75 |
76 | let res = scheduler.start(disposed: 1000) {
77 | underlying.bufferWithTrigger(boundary.asObservable())
78 | }
79 |
80 | let expected = Recorded.events([
81 | .next(201, []),
82 | .next(211, [2]),
83 | .error(230, testError)
84 | ])
85 | XCTAssertEqual(res.events, expected)
86 |
87 | XCTAssertEqual(underlying.subscriptions, [Subscription(200, 230)])
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Tests/RxSwift/MapAtTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapToKeyPath.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Michael Avila on 2/8/18.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class MapAtTests: XCTestCase {
16 | struct Person {
17 | let name: String
18 | }
19 |
20 | let people: [Person] = [
21 | Person(name: "Bart"),
22 | Person(name: "Lisa"),
23 | Person(name: "Maggie")
24 | ]
25 |
26 | private var observer: TestableObserver!
27 |
28 | override func setUp() {
29 | let scheduler = TestScheduler(initialClock: 0)
30 | observer = scheduler.createObserver(String.self)
31 |
32 | _ = Observable.from(people)
33 | .mapAt(\.name)
34 | .subscribe(observer)
35 |
36 | scheduler.start()
37 | }
38 |
39 | func testResultSequenceHasSameNumberOfItemsAsSourceSequence() {
40 | XCTAssertEqual(
41 | observer.events.count - 1, // completed event
42 | people.count
43 | )
44 | }
45 |
46 | func testResultSequenceHasValuesAtProvidedKeypath() {
47 | // test elements values and type
48 | let correctValues = Recorded.events([
49 | .next(0, "Bart"),
50 | .next(0, "Lisa"),
51 | .next(0, "Maggie"),
52 | .completed(0)
53 | ])
54 |
55 | XCTAssertEqual(observer.events, correctValues)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Tests/RxSwift/MapManyTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapManyTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Joan Disho on 06/05/18.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import RxSwiftExt
11 | import RxSwift
12 | import RxTest
13 |
14 | class MapManyTests: XCTestCase {
15 | func runAndObserve(_ sequence: Observable) -> TestableObserver {
16 | let scheduler = TestScheduler(initialClock: 0)
17 | let observer = scheduler.createObserver(C.self)
18 | _ = sequence.asObservable().subscribe(observer)
19 | scheduler.start()
20 | return observer
21 | }
22 |
23 | func testMapManyWithModel() {
24 | // swiftlint:disable:next nesting
25 | struct SomeModel: CustomStringConvertible {
26 | let number: Int
27 | var description: String { return "#\(number)" }
28 |
29 | init(_ number: Int) {
30 | self.number = number
31 | }
32 | }
33 |
34 | let sourceArray = Observable.of(1...4)
35 |
36 | let observer = runAndObserve(sourceArray.mapMany(SomeModel.init))
37 | let correct = Recorded.events([
38 | .next(0, [SomeModel(1), SomeModel(2), SomeModel(3), SomeModel(4)]),
39 | .completed(0)
40 | ])
41 |
42 | XCTAssertEqual(observer.events.description, correct.description)
43 | }
44 |
45 | func testMapManyWithInts() {
46 | let sourceArray = Observable.of(1...10)
47 |
48 | let observer = runAndObserve(sourceArray.mapMany { $0 + 1 })
49 | let correct = Recorded.events([
50 | .next(0, [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]),
51 | .completed(0)
52 | ])
53 |
54 | XCTAssertEqual(observer.events, correct)
55 | }
56 |
57 | func testMapManyWithStrings() {
58 | let sourceArray = Observable.just(["a", "b", "C"])
59 |
60 | let observer = runAndObserve(sourceArray.mapMany { ($0 + "x").lowercased() })
61 | let correct = Recorded.events([
62 | .next(0, ["ax", "bx", "cx"]),
63 | .completed(0)
64 | ])
65 |
66 | XCTAssertEqual(observer.events, correct)
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Tests/RxSwift/MergeWithTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MergeWithTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Joan Disho on 28/05/18.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import RxSwift
11 | import RxTest
12 |
13 | class MergeWithTests: XCTestCase {
14 | fileprivate func runAndObserve(_ sequence: Observable) -> TestableObserver {
15 | let scheduler = TestScheduler(initialClock: 0)
16 | let observer = scheduler.createObserver(T.self)
17 | _ = sequence.asObservable().subscribe(observer)
18 | scheduler.start()
19 | return observer
20 | }
21 |
22 | func testMergeWith_Numbers() {
23 | let oddNumbers = [1, 3, 5, 7, 9]
24 | let evenNumbers = [2, 4, 6, 8, 10]
25 |
26 | let oddStream = Observable.from(oddNumbers)
27 | let evenStream = Observable.from(evenNumbers)
28 |
29 | let observer1 = runAndObserve(oddStream.merge(with: evenStream))
30 | let observer2 = runAndObserve(evenStream.merge(with: oddStream))
31 |
32 | let correct1 = Recorded.events([
33 | .next(0, 1),
34 | .next(0, 2),
35 | .next(0, 3),
36 | .next(0, 4),
37 | .next(0, 5),
38 | .next(0, 6),
39 | .next(0, 7),
40 | .next(0, 8),
41 | .next(0, 9),
42 | .next(0, 10),
43 | .completed(0)
44 | ])
45 |
46 | let correct2 = Recorded.events([
47 | .next(0, 2),
48 | .next(0, 1),
49 | .next(0, 4),
50 | .next(0, 3),
51 | .next(0, 6),
52 | .next(0, 5),
53 | .next(0, 8),
54 | .next(0, 7),
55 | .next(0, 10),
56 | .next(0, 9),
57 | .completed(0)
58 | ])
59 |
60 | XCTAssertEqual(observer1.events, correct1)
61 | XCTAssertEqual(observer2.events, correct2)
62 | }
63 |
64 | func testMergeWith_Error() {
65 | let numberStream = Observable.from([1, 2, 3, 4])
66 | let errorStream = Observable.error(testError)
67 |
68 | let observer = runAndObserve(numberStream.merge(with: errorStream))
69 |
70 | let expected = Recorded>.events([.error(0, testError)])
71 |
72 | XCTAssertEqual(observer.events, expected)
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Tests/RxSwift/OnceTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OnceTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Florent Pillet on 12/04/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class OnceTests: XCTestCase {
16 | func testOnce() {
17 | let onceObservable = Observable.once("Hello")
18 | let scheduler = TestScheduler(initialClock: 0)
19 |
20 | let observer1 = scheduler.createObserver(String.self)
21 | _ = onceObservable.subscribe(observer1)
22 |
23 | let observer2 = scheduler.createObserver(String.self)
24 | _ = onceObservable.subscribe(observer2)
25 |
26 | scheduler.start()
27 |
28 | let correct1 = Recorded.events([
29 | .next(0, "Hello"),
30 | .completed(0)
31 | ])
32 |
33 | let correct2: [Recorded>] = [
34 | .completed(0)
35 | ]
36 |
37 | XCTAssertEqual(observer1.events, correct1)
38 | XCTAssertEqual(observer2.events, correct2)
39 |
40 | }
41 |
42 | func testMultipleOnce() {
43 | let onceObservable1 = Observable.once("Hello")
44 | let onceObservable2 = Observable.once("world")
45 | let scheduler = TestScheduler(initialClock: 0)
46 |
47 | let observer1 = scheduler.createObserver(String.self)
48 | _ = onceObservable1.subscribe(observer1)
49 | let observer2 = scheduler.createObserver(String.self)
50 | _ = onceObservable1.subscribe(observer2)
51 |
52 | let observer3 = scheduler.createObserver(String.self)
53 | _ = onceObservable2.subscribe(observer3)
54 | let observer4 = scheduler.createObserver(String.self)
55 | _ = onceObservable2.subscribe(observer4)
56 |
57 | scheduler.start()
58 |
59 | let correct1 = Recorded.events([
60 | .next(0, "Hello"),
61 | .completed(0)
62 | ])
63 |
64 | let correct2: [Recorded>] = [
65 | .completed(0)
66 | ]
67 |
68 | let correct3 = Recorded.events([
69 | .next(0, "world"),
70 | .completed(0)
71 | ])
72 |
73 | let correct4: [Recorded>] = [
74 | .completed(0)
75 | ]
76 |
77 | XCTAssertEqual(observer1.events, correct1)
78 | XCTAssertEqual(observer2.events, correct2)
79 | XCTAssertEqual(observer3.events, correct3)
80 | XCTAssertEqual(observer4.events, correct4)
81 |
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Tests/RxSwift/PartitionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PartitionTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Shai Mishali on 24/11/2018.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 | import RxTest
12 | import XCTest
13 | import RxSwiftExt
14 |
15 | class PartitionTests: XCTestCase {
16 | private var scheduler = TestScheduler(initialClock: 0)
17 | private var stream: TestableObservable!
18 |
19 | override func setUp() {
20 | super.setUp()
21 | scheduler = TestScheduler(initialClock: 0)
22 | let events = (0...10).map { i -> Recorded> in
23 | return .next(i * 10, i)
24 | } + [.completed(100)]
25 |
26 | stream = scheduler.createHotObservable(events)
27 | }
28 |
29 | func testPartitionBothMatch() {
30 | let (evens, odds) = stream.partition { $0 % 2 == 0 }
31 |
32 | let evensObserver = scheduler.createObserver(Int.self)
33 | _ = evens.bind(to: evensObserver)
34 |
35 | let oddsObserver = scheduler.createObserver(Int.self)
36 | _ = odds.bind(to: oddsObserver)
37 |
38 | scheduler.start()
39 |
40 | XCTAssertEqual(oddsObserver.events, Recorded.events([
41 | .next(10, 1),
42 | .next(30, 3),
43 | .next(50, 5),
44 | .next(70, 7),
45 | .next(90, 9),
46 | .completed(100)
47 | ]))
48 |
49 | XCTAssertEqual(evensObserver.events, Recorded.events([
50 | .next(0, 0),
51 | .next(20, 2),
52 | .next(40, 4),
53 | .next(60, 6),
54 | .next(80, 8),
55 | .next(100, 10),
56 | .completed(100)
57 | ]))
58 | }
59 |
60 | func testPartitionOneSideMatch() {
61 | let (all, none) = stream.partition { $0 <= 10 }
62 |
63 | let allObserver = scheduler.createObserver(Int.self)
64 | _ = all.bind(to: allObserver)
65 |
66 | let noneObserver = scheduler.createObserver(Int.self)
67 | _ = none.bind(to: noneObserver)
68 |
69 | scheduler.start()
70 |
71 | XCTAssertEqual(allObserver.events, Recorded.events([
72 | .next(0, 0),
73 | .next(10, 1),
74 | .next(20, 2),
75 | .next(30, 3),
76 | .next(40, 4),
77 | .next(50, 5),
78 | .next(60, 6),
79 | .next(70, 7),
80 | .next(80, 8),
81 | .next(90, 9),
82 | .next(100, 10),
83 | .completed(100)
84 | ]))
85 |
86 | XCTAssertEqual(noneObserver.events, [.completed(100)])
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Tests/RxSwift/ToSortedArrayTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ToSortedArrayTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Joan Disho on 28/04/18.
6 | // Copyright © 2018 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import RxSwift
11 | import RxTest
12 |
13 | class ToSortedArrayTests: XCTestCase {
14 | func runAndObserve(_ sequence: Observable) -> TestableObserver {
15 | let scheduler = TestScheduler(initialClock: 0)
16 | let observer = scheduler.createObserver(T.self)
17 | _ = sequence.asObservable().subscribe(observer)
18 | scheduler.start()
19 | return observer
20 | }
21 |
22 | func testDefaultToSortedArray() {
23 | let source = Observable.of(1, 4, 6, 1, 7, 8)
24 | let observer = runAndObserve(source.toSortedArray().asObservable())
25 | let correct = Recorded.events([
26 | .next(0, [1, 1, 4, 6, 7, 8]),
27 | .completed(0)
28 | ])
29 | XCTAssertEqual(observer.events, correct)
30 | }
31 |
32 | func testAscCase() {
33 | let source = Observable.of(1, 4, 6, 1, 7, 8)
34 | let observer = runAndObserve(source.toSortedArray(ascending: true).asObservable())
35 | let correct = Recorded.events([
36 | .next(0, [1, 1, 4, 6, 7, 8]),
37 | .completed(0)
38 | ])
39 | XCTAssertEqual(observer.events, correct)
40 | }
41 |
42 | func testDescCase() {
43 | let source = Observable.of(1, 4, 6, 1, 7, 8)
44 | let observer = runAndObserve(source.toSortedArray(ascending: false).asObservable())
45 | let correct = Recorded.events([
46 | .next(0, [8, 7, 6, 4, 1, 1]),
47 | .completed(0)
48 | ])
49 | XCTAssertEqual(observer.events, correct)
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Tests/RxSwift/UnwrapTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UnwrapTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Marin Todorov on 4/7/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class UnwrapTests: XCTestCase {
16 | let numbers: [Int?] = [1, nil, Int?(3), 4]
17 | fileprivate var observer: TestableObserver!
18 |
19 | override func setUp() {
20 | super.setUp()
21 |
22 | let scheduler = TestScheduler(initialClock: 0)
23 | observer = scheduler.createObserver(Int.self)
24 |
25 | _ = Observable.from(numbers)
26 | .unwrap()
27 | .subscribe(observer)
28 |
29 | scheduler.start()
30 | }
31 |
32 | func testUnwrapFilterNil() {
33 | //test results count
34 | XCTAssertEqual(
35 | observer.events.count,
36 | numbers.count - 1/* the nr. of nil elements*/ + 1 /* complete event*/
37 | )
38 | }
39 |
40 | func testUnwrapResultValues() {
41 | // test elements values and type
42 | let correctValues = Recorded.events([
43 | .next(0, 1),
44 | .next(0, 3),
45 | .next(0, 4),
46 | .completed(0)
47 | ])
48 |
49 | XCTAssertEqual(observer.events, correctValues)
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Tests/RxSwift/WeakTarget.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeakTarget.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Ian Keen on 17/04/2016.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 |
12 | enum RxEvent {
13 | case next, error, completed, disposed
14 | init(event: Event) {
15 | switch event {
16 | case .next: self = .next
17 | case .error: self = .error
18 | case .completed: self = .completed
19 | }
20 | }
21 | }
22 |
23 | var weakTargetReferenceCount: Int = 0
24 |
25 | class WeakTarget {
26 | let listener: ([RxEvent: Int]) -> Void
27 | fileprivate let observable: Observable
28 | fileprivate var disposeBag = DisposeBag()
29 | fileprivate var events: [RxEvent: Int] = [.next: 0, .error: 0, .completed: 0, .disposed: 0]
30 | fileprivate func updateEvents(_ event: RxEvent) {
31 | self.events[event] = (self.events[event] ?? 0) + 1
32 | self.listener(self.events)
33 | }
34 |
35 | init(obs: Observable, listener: @escaping ([RxEvent: Int]) -> Void) {
36 | weakTargetReferenceCount += 1
37 | self.listener = listener
38 | self.observable = obs
39 | }
40 | deinit { weakTargetReferenceCount -= 1 }
41 |
42 | // MARK: - Subscribers
43 | fileprivate func subscriber_on(_ event: Event) { self.updateEvents(RxEvent(event: event)) }
44 | fileprivate func subscriber_onNext(_ element: Type) { self.updateEvents(.next) }
45 | fileprivate func subscriber_onError(_ error: Error) { self.updateEvents(.error) }
46 | fileprivate func subscriber_onComplete() { self.updateEvents(.completed) }
47 | fileprivate func subscriber_onDisposed() { self.updateEvents(.disposed) }
48 |
49 | // MARK: - Subscription Setup
50 | func useSubscribe() {
51 | self.observable.subscribe(weak: self, WeakTarget.subscriber_on).disposed(by: self.disposeBag)
52 | }
53 | func useSubscribeNext() {
54 | //self.observable.subscribeNext(self.subscriber_onNext).addDisposableTo(self.disposeBag) //uncomment this line to create a retain cycle
55 | self.observable.subscribeNext(weak: self, WeakTarget.subscriber_onNext).disposed(by: self.disposeBag)
56 | }
57 | func useSubscribeError() {
58 | self.observable.subscribeError(weak: self, WeakTarget.subscriber_onError).disposed(by: self.disposeBag)
59 | }
60 | func useSubscribeComplete() {
61 | self.observable.subscribeCompleted(weak: self, WeakTarget.subscriber_onComplete).disposed(by: self.disposeBag)
62 | }
63 | func useSubscribeMulti() {
64 | self.observable
65 | .subscribe(
66 | weak: self,
67 | onNext: WeakTarget.subscriber_onNext,
68 | onError: WeakTarget.subscriber_onError,
69 | onCompleted: WeakTarget.subscriber_onComplete,
70 | onDisposed: WeakTarget.subscriber_onDisposed
71 | )
72 | .disposed(by: self.disposeBag)
73 | }
74 |
75 | func dispose() {
76 | self.disposeBag = DisposeBag()
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Tests/RxSwift/WeakTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeakTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Ian Keen on 17/04/2016.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class WeakTests: XCTestCase {
16 | var target: WeakTarget?
17 | var events = [RxEvent: Int]()
18 | let source = PublishSubject()
19 |
20 | override func setUp() {
21 | super.setUp()
22 |
23 | self.events = [RxEvent: Int]()
24 | XCTAssertTrue(weakTargetReferenceCount == 0)
25 | self.target = WeakTarget(obs: self.source) {
26 | self.events = $0
27 | }
28 | XCTAssertTrue(weakTargetReferenceCount == 1)
29 | }
30 |
31 | override func tearDown() {
32 | super.tearDown()
33 | self.target = nil
34 |
35 | // If a retain cycle was present this would not return to 0
36 | XCTAssertTrue(weakTargetReferenceCount == 0)
37 | }
38 |
39 | // MARK: - Event Subscriber
40 | func testSubscribe() {
41 | self.target?.useSubscribe()
42 |
43 | self.source.onNext(0)
44 | self.source.onNext(0)
45 | self.target = nil
46 | self.source.onNext(0)
47 | self.source.onError(testError)
48 | self.target?.dispose()
49 |
50 | let expected: [RxEvent: Int] = [.next: 2, .error: 0, .completed: 0, .disposed: 0]
51 | XCTAssertTrue(events == expected)
52 | }
53 |
54 | // MARK: - Individual Subscribers
55 | func testSubscribeNext() {
56 | self.target?.useSubscribeNext()
57 |
58 | self.source.onNext(0)
59 | self.source.onNext(0)
60 | self.target = nil
61 | self.source.onNext(0)
62 |
63 | // If a retain cycle was present, the .next count would be 3
64 | let expected: [RxEvent: Int] = [.next: 2, .error: 0, .completed: 0, .disposed: 0]
65 | XCTAssertTrue(events == expected)
66 | }
67 |
68 | func testSubscribeError() {
69 | self.target?.useSubscribeError()
70 |
71 | self.source.onError(testError)
72 | self.target = nil
73 |
74 | // Errors only emit once
75 | let expected: [RxEvent: Int] = [.next: 0, .error: 1, .completed: 0, .disposed: 0]
76 | XCTAssertTrue(events == expected)
77 | }
78 |
79 | func testSubscribeCompleted() {
80 | self.target?.useSubscribeComplete()
81 |
82 | self.source.onCompleted()
83 | self.target = nil
84 |
85 | // Completed only emit once
86 | let expected: [RxEvent: Int] = [.next: 0, .error: 0, .completed: 1, .disposed: 0]
87 | XCTAssertTrue(events == expected)
88 | }
89 |
90 | // MARK: - Multiple Subscribers
91 | func testSubscribeOn_Next() {
92 | self.target?.useSubscribeMulti()
93 |
94 | self.source.onNext(0)
95 | self.source.onNext(0)
96 | self.target = nil
97 | self.source.onNext(0)
98 |
99 | // If a retain cycle was present, the .next count would be 3
100 | let expected: [RxEvent: Int] = [.next: 2, .error: 0, .completed: 0, .disposed: 0]
101 | XCTAssertTrue(events == expected)
102 | }
103 |
104 | func testSubscribeOn_Error() {
105 | self.target?.useSubscribeMulti()
106 |
107 | self.source.onError(testError)
108 | self.target = nil
109 |
110 | // Errors only emit once
111 | let expected: [RxEvent: Int] = [.next: 0, .error: 1, .completed: 0, .disposed: 1]
112 | XCTAssertTrue(events == expected)
113 | }
114 |
115 | func testSubscribeOn_Completed() {
116 | self.target?.useSubscribeMulti()
117 |
118 | self.source.onCompleted()
119 | self.target = nil
120 |
121 | // completed only emit once
122 | let expected: [RxEvent: Int] = [.next: 0, .error: 0, .completed: 1, .disposed: 1]
123 | XCTAssertTrue(events == expected)
124 | }
125 |
126 | func testSubscribeOn_Disposed() {
127 | self.target?.useSubscribeMulti()
128 |
129 | self.target?.dispose()
130 | self.target = nil
131 |
132 | // Completed only emit once
133 | let expected: [RxEvent: Int] = [.next: 0, .error: 0, .completed: 0, .disposed: 1]
134 | XCTAssertTrue(events == expected)
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/Tests/RxSwift/ZipWithTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ZipWithTest.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Arjan Duijzer on 26/12/2017.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import RxSwift
11 | import RxTest
12 |
13 | struct Pair {
14 | let first: F
15 | let second: S
16 | }
17 |
18 | extension Pair: Equatable {
19 | }
20 |
21 | func ==(lhs: Pair, rhs: Pair) -> Bool {
22 | return lhs.first == rhs.first && lhs.second == rhs.second
23 | }
24 |
25 | class ZipWithTest: XCTestCase {
26 | func testZipWith_SourcesNotEmpty_ZipCompletes() {
27 | let scheduler = TestScheduler(initialClock: 0)
28 | let source1 = Observable.from([1, 2, 3])
29 | let source2 = Observable.from(["a", "b"])
30 |
31 | let res = scheduler.start {
32 | source1.zip(with: source2) {
33 | Pair(first: $0, second: $1)
34 | }
35 | }
36 |
37 | let expected = Recorded.events([
38 | .next(200, Pair(first: 1, second: "a")),
39 | .next(200, Pair(first: 2, second: "b")),
40 | .completed(200)
41 | ])
42 |
43 | XCTAssertEqual(res.events, expected)
44 | }
45 |
46 | func testZipWith_SourceEmpty_ZipCompletesEmpty() {
47 | let scheduler = TestScheduler(initialClock: 0)
48 | let source1 = Observable.from([1, 2, 3])
49 | let source2 = Observable.empty()
50 |
51 | let res = scheduler.start {
52 | source1.zip(with: source2) {
53 | Pair(first: $0, second: $1)
54 | }
55 | }
56 |
57 | let expected: [Recorded>>] = [.completed(200)]
58 | XCTAssertEqual(res.events, expected)
59 | }
60 |
61 | func testZipWith_SourceError_ZipCompletesWithError() {
62 | let scheduler = TestScheduler(initialClock: 0)
63 | let source1 = Observable.just(1)
64 | let source2 = Observable.error(testError)
65 |
66 | let res = scheduler.start {
67 | source1.zip(with: source2) {
68 | Pair(first: $0, second: $1)
69 | }
70 | }
71 |
72 | let expected: [Recorded>>] = [.error(200, testError)]
73 | XCTAssertEqual(res.events, expected)
74 | }
75 |
76 | func testMaybeZipWith_SourcesNotEmpty_ZipCompletes() {
77 | let scheduler = TestScheduler(initialClock: 0)
78 | let source1 = Maybe.just(1)
79 | let source2 = Observable.from(["a", "b", "c"])
80 |
81 | let res = scheduler.start {
82 | source1.zip(with: source2) {
83 | Pair(first: $0, second: $1)
84 | }
85 | }
86 |
87 | let expected = Recorded.events([
88 | .next(200, Pair(first: 1, second: "a")),
89 | .completed(200)
90 | ])
91 | XCTAssertEqual(res.events, expected)
92 | }
93 |
94 | func testSingleZipWith_SourcesNotEmpty_ZipCompletes() {
95 | let scheduler = TestScheduler(initialClock: 0)
96 | let source1 = Single.just(1)
97 | let source2 = Observable.just(2)
98 |
99 | let res = scheduler.start {
100 | source1.zip(with: source2) {
101 | Pair(first: $0, second: $1)
102 | }
103 | }
104 |
105 | let expected = Recorded.events([
106 | .next(200, Pair(first: 1, second: 2)),
107 | .completed(200)
108 | ])
109 | XCTAssertEqual(res.events, expected)
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Tests/RxSwift/catchErrorJustCompleteTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CatchErrorJustCompleteTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Florent Pillet on 31/07/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class CatchErrorJustCompleteTests: XCTestCase {
16 | let testError = NSError(domain: "dummyError", code: -232, userInfo: nil)
17 |
18 | func testCatchErrorJustComplete_Empty() {
19 | let scheduler = TestScheduler(initialClock: 0)
20 |
21 | let events: [Recorded>] = [
22 | .completed(250)]
23 | let xs = scheduler.createColdObservable(events)
24 |
25 | let res = scheduler.start {
26 | xs.catchErrorJustComplete()
27 | }
28 |
29 | XCTAssertEqual(res.events, [
30 | .completed(450)
31 | ])
32 |
33 | XCTAssertEqual(xs.subscriptions, [
34 | Subscription(200, 450)
35 | ])
36 | }
37 |
38 | func testCatchErrorJustComplete_NoError() {
39 | let scheduler = TestScheduler(initialClock: 0)
40 |
41 | let xs = scheduler.createColdObservable([
42 | .next(100, 1),
43 | .next(150, 2),
44 | .next(200, 3),
45 | .completed(250)
46 | ])
47 |
48 | let res = scheduler.start {
49 | xs.catchErrorJustComplete()
50 | }
51 |
52 | XCTAssertEqual(res.events, [
53 | .next(300, 1),
54 | .next(350, 2),
55 | .next(400, 3),
56 | .completed(450)
57 | ])
58 |
59 | XCTAssertEqual(xs.subscriptions, [
60 | Subscription(200, 450)
61 | ])
62 | }
63 |
64 | func testCatchErrorJustComplete_Infinite() {
65 | let scheduler = TestScheduler(initialClock: 0)
66 |
67 | let xs = scheduler.createColdObservable([
68 | .next(100, 1),
69 | .next(150, 2),
70 | .next(200, 3)
71 | ])
72 |
73 | let res = scheduler.start {
74 | xs.catchErrorJustComplete()
75 | }
76 |
77 | XCTAssertEqual(res.events, [
78 | .next(300, 1),
79 | .next(350, 2),
80 | .next(400, 3)
81 | ])
82 |
83 | XCTAssertEqual(xs.subscriptions, [
84 | Subscription(200, 1000)
85 | ])
86 | }
87 |
88 | func testCatchErrorJustComplete_Error() {
89 | let scheduler = TestScheduler(initialClock: 0)
90 |
91 | let xs = scheduler.createColdObservable([
92 | .next(100, 1),
93 | .next(150, 2),
94 | .error(250, testError)
95 | ])
96 |
97 | let res = scheduler.start {
98 | xs.catchErrorJustComplete()
99 | }
100 |
101 | XCTAssertEqual(res.events, [
102 | .next(300, 1),
103 | .next(350, 2),
104 | .completed(450)
105 | ])
106 |
107 | XCTAssertEqual(xs.subscriptions, [
108 | Subscription(200, 450)
109 | ])
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Tests/RxSwift/filterMapTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FilterMapTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Jeremie Girault on 31/05/2017.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | final class FilterMapTests: XCTestCase {
16 | private var scheduler: TestScheduler!
17 |
18 | override func setUp() {
19 | super.setUp()
20 | scheduler = TestScheduler(initialClock: 0)
21 | }
22 |
23 | override func tearDown() {
24 | scheduler = nil
25 | super.tearDown()
26 | }
27 |
28 | func testIgnoreEvenAndEvenizeOdds() {
29 | let observer = scheduler.createObserver(Int.self)
30 |
31 | let values = 1..<10
32 | _ = Observable.from(values)
33 | .filterMap { $0 % 2 == 0 ? .ignore : .map(2*$0) }
34 | .subscribe(observer)
35 |
36 | scheduler.start()
37 |
38 | var correct = values
39 | .filter { $0 % 2 != 0 }
40 | .map { Recorded.next(0, 2 * $0) }
41 |
42 | correct.append(.completed(0))
43 |
44 | XCTAssertEqual(observer.events, correct)
45 | }
46 |
47 | func testErrorsWithSource() {
48 | let observer = scheduler.createObserver(Int.self)
49 |
50 | let subject = PublishSubject()
51 | _ = subject
52 | .filterMap { $0 % 2 == 0 ? .ignore : .map(2*$0) }
53 | .subscribe(observer)
54 |
55 | subject.on(.next(1))
56 | subject.on(.next(2))
57 | subject.on(.next(3))
58 | subject.on(.error(testError))
59 |
60 | scheduler.start()
61 |
62 | let correct = Recorded.events([
63 | .next(0, 2),
64 | .next(0, 6),
65 | .error(0, testError)
66 | ])
67 |
68 | print(observer.events)
69 |
70 | XCTAssertEqual(observer.events, correct)
71 | }
72 |
73 | func testThrownError() {
74 | // Given
75 | let expectedErrored = 203
76 | let expectedEvents = Recorded.events([
77 | .next(201, 2),
78 | .error(expectedErrored, testError)
79 | ])
80 | let source = scheduler.createHotObservable([
81 | .next(201, 1),
82 | .next(202, 2),
83 | .next(expectedErrored, 3), // should not fire due to error on 3
84 | .next(204, 4),
85 | .completed(205)
86 | ])
87 | // When
88 | let result = scheduler.start {
89 | source.filterMap { element -> FilterMap in
90 | guard !element.isMultiple(of: 2) else {
91 | return .ignore
92 | }
93 | guard element != 3 else {
94 | throw testError
95 | }
96 | return .map(2 * element)
97 | }
98 | }
99 | // Then
100 | XCTAssertEqual(source.subscriptions, [Subscription(TestScheduler.Defaults.subscribed, expectedErrored)])
101 | XCTAssertEqual(result.events, expectedEvents)
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Tests/RxSwift/fromAsyncTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FromAsyncTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Vincent on 12/08/2017.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class FromAsyncTests: XCTestCase {
16 | private var scheduler: TestScheduler!
17 | private var observer: TestableObserver!
18 |
19 | override func setUp() {
20 | super.setUp()
21 | scheduler = TestScheduler(initialClock: 0)
22 | observer = scheduler.createObserver(String.self)
23 | }
24 |
25 | override func tearDown() {
26 | scheduler = nil
27 | observer = nil
28 | super.tearDown()
29 | }
30 |
31 | func testResultEquality() {
32 | var correct = [Recorded>]()
33 |
34 | service(arg1: "Foo", arg2: 0) { result in
35 | correct.append(.next(0, result))
36 | correct.append(.completed(0))
37 | }
38 |
39 | _ = Observable
40 | .fromAsync(service(arg1:arg2:completionHandler:))("Foo", 2)
41 | .subscribe(observer)
42 |
43 | scheduler.start()
44 |
45 | XCTAssertEqual(observer.events, correct)
46 | }
47 |
48 | func testSingleResultEqualitySuccessCase() {
49 | // given
50 | let result = "result"
51 | let expectedEvents: [Recorded>] = [.next(0, result), .completed(0)]
52 | // when
53 | _ = Single
54 | .fromAsync(serviceWithError)(result)
55 | .asObservable()
56 | .subscribe(observer)
57 | scheduler.start()
58 | // then
59 | XCTAssertEqual(observer.events, expectedEvents)
60 | }
61 |
62 | func testSingleOptionalResultEqualitySuccessCase() {
63 | // given
64 | let result: String? = nil
65 | let expectedEvents: [Recorded>] = [.next(0, result), .completed(0)]
66 | let observer = scheduler.createObserver(Optional.self)
67 | // when
68 | _ = Single
69 | .fromAsync(serviceWithOptionalResult)(result)
70 | .asObservable()
71 | .subscribe(observer)
72 | scheduler.start()
73 | // then
74 | XCTAssertEqual(observer.events, expectedEvents)
75 | }
76 |
77 | func testSingleResultEqualityErrorCase() {
78 | // given
79 | let expectedEvents: [Recorded>] = [.error(0, testError)]
80 | // when
81 | _ = Single
82 | .fromAsync(serviceThrowingError)
83 | .asObservable()
84 | .subscribe(observer)
85 | scheduler.start()
86 | // then
87 | XCTAssertEqual(observer.events, expectedEvents)
88 | }
89 |
90 | private func service(arg1: String, arg2: Int, completionHandler: (String) -> Void) {
91 | completionHandler("Result")
92 | }
93 |
94 | private func serviceWithError(result: String, completionHandler: (String?, TestError?) -> Void) {
95 | completionHandler(result, nil)
96 | }
97 |
98 | private func serviceWithOptionalResult(result: String?, completionHandler: (String??, TestError?) -> Void) {
99 | completionHandler(result, nil)
100 | }
101 |
102 | private func serviceThrowingError(completionHandler: (String?, TestError?) -> Void) {
103 | completionHandler(nil, testError)
104 | }
105 |
106 | private func serviceWithOptionalResult(completionHandler: (String??, TestError?) -> Void) {
107 | completionHandler(.some(nil), nil)
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/Tests/RxSwift/mapToTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapToTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Marin Todorov on 4/12/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class MapToTests: XCTestCase {
16 |
17 | private let numbers: [Int?] = [1, nil, Int?(3)]
18 | private var observer: TestableObserver!
19 |
20 | override func setUp() {
21 | super.setUp()
22 |
23 | let scheduler = TestScheduler(initialClock: 0)
24 | observer = scheduler.createObserver(String.self)
25 |
26 | _ = Observable.from(numbers)
27 | .mapTo("candy")
28 | .subscribe(observer)
29 |
30 | scheduler.start()
31 | }
32 |
33 | func testReplaceWithResultCount() {
34 | XCTAssertEqual(
35 | observer.events.count - 1, // completed event
36 | numbers.count
37 | )
38 | }
39 |
40 | func testReplaceWithResultValues() {
41 | // test elements values and type
42 | let correctValues = Recorded.events([
43 | .next(0, "candy"),
44 | .next(0, "candy"),
45 | .next(0, "candy"),
46 | .completed(0)
47 | ])
48 | XCTAssertEqual(observer.events, correctValues)
49 | }
50 | }
51 |
52 | // MARK: - Single
53 | extension MapToTests {
54 | func testSingleReplaceSuccess() {
55 | // Given
56 | let expectedValue = "candy"
57 | let scheduler = TestScheduler(initialClock: 0)
58 | // When
59 | let result = scheduler.start {
60 | Single.just(1).mapTo(expectedValue).asObservable()
61 | }
62 | // Then
63 | XCTAssertEqual(result.events, [
64 | .next(TestScheduler.Defaults.subscribed, expectedValue),
65 | .completed(TestScheduler.Defaults.subscribed)
66 | ])
67 | }
68 |
69 | func testSingleNoReplaceFailure() {
70 | // Given
71 | let scheduler = TestScheduler(initialClock: 0)
72 | // When
73 | let result = scheduler.start {
74 | Single.error(testError).mapTo("candy").asObservable()
75 | }
76 | // Then
77 | XCTAssertEqual(result.events, [.error(TestScheduler.Defaults.subscribed, testError)])
78 | }
79 | }
80 |
81 | // MARK: - Maybe
82 | extension MapToTests {
83 | func testMaybeReplaceSuccess() {
84 | // Given
85 | let expectedValue = "candy"
86 | let scheduler = TestScheduler(initialClock: 0)
87 | // When
88 | let result = scheduler.start {
89 | Maybe.just(1).mapTo(expectedValue).asObservable()
90 | }
91 | // Then
92 | XCTAssertEqual(result.events, [
93 | .next(TestScheduler.Defaults.subscribed, expectedValue),
94 | .completed(TestScheduler.Defaults.subscribed)
95 | ])
96 | }
97 |
98 | func testMaybeNoReplaceFailure() {
99 | // Given
100 | let scheduler = TestScheduler(initialClock: 0)
101 | // When
102 | let result = scheduler.start {
103 | Maybe.error(testError).mapTo("candy").asObservable()
104 | }
105 | // Then
106 | XCTAssertEqual(result.events, [.error(TestScheduler.Defaults.subscribed, testError)])
107 | }
108 |
109 | func testMaybeNoReplaceEmpty() {
110 | // Given
111 | let scheduler = TestScheduler(initialClock: 0)
112 | // When
113 | let result = scheduler.start {
114 | Maybe.empty().mapTo("candy").asObservable()
115 | }
116 | // Then
117 | XCTAssertEqual(result.events, [.completed(TestScheduler.Defaults.subscribed)])
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/Tests/RxSwift/materialized+elementsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Materialized+elementsTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Adam Borek on 12/04/2017.
6 | // Copyright © 2017 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import RxTest
11 | import RxSwift
12 | import RxSwiftExt
13 |
14 | final class MaterializedElementsTests: XCTestCase {
15 | private var testScheduler: TestScheduler!
16 | private var eventObservable: Observable>!
17 | private let dummyError = NSError(domain: "dummy", code: -102)
18 | private var disposeBag = DisposeBag()
19 |
20 | override func setUp() {
21 | super.setUp()
22 | testScheduler = TestScheduler(initialClock: 0)
23 | eventObservable = testScheduler.createHotObservable([
24 | .next(0, Event.next(0)),
25 | .next(100, Event.next(1)),
26 | .next(200, Event.error(dummyError)),
27 | .next(300, Event.next(2)),
28 | .next(400, Event.error(dummyError)),
29 | .next(500, Event.next(3))
30 | ]).asObservable()
31 | }
32 |
33 | override func tearDown() {
34 | super.tearDown()
35 | disposeBag = DisposeBag()
36 | }
37 |
38 | func test_elementsReturnsOnlyNextEvents() {
39 | let observer = testScheduler.createObserver(Int.self)
40 |
41 | eventObservable
42 | .elements()
43 | .subscribe(observer)
44 | .disposed(by: disposeBag)
45 | testScheduler.start()
46 |
47 | XCTAssertEqual(observer.events, [
48 | .next(0, 0),
49 | .next(100, 1),
50 | .next(300, 2),
51 | .next(500, 3)
52 | ])
53 | }
54 |
55 | func test_errorsReturnsOnlyErrorEvents() {
56 | let observer = testScheduler.createObserver(Error.self)
57 |
58 | eventObservable
59 | .errors()
60 | .subscribe(observer)
61 | .disposed(by: disposeBag)
62 | testScheduler.start()
63 |
64 | XCTAssertEqual(observer.events.map { $0.time }, [200, 400])
65 | XCTAssertEqual(observer.events.map { $0.value.element! as NSError }, [dummyError, dummyError])
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Tests/RxSwift/notTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NotTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Thane Gill on 10/18/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class NotTests: XCTestCase {
16 |
17 | func testNot() {
18 | let values = [true, false, true]
19 |
20 | let scheduler = TestScheduler(initialClock: 0)
21 | let observer = scheduler.createObserver(Bool.self)
22 |
23 | _ = Observable.from(values)
24 | .not()
25 | .subscribe(observer)
26 |
27 | scheduler.start()
28 |
29 | let correct = Recorded.events([
30 | .next(0, false),
31 | .next(0, true),
32 | .next(0, false),
33 | .completed(0)
34 | ])
35 |
36 | XCTAssertEqual(observer.events, correct)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Tests/RxSwift/pausableTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PausableTests.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Jesse Farless on 12/09/16.
6 | // Copyright © 2016 RxSwift Community. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | import RxSwift
12 | import RxSwiftExt
13 | import RxTest
14 |
15 | class PausableTests: XCTestCase {
16 | let testError = NSError(domain: "dummyError", code: -232, userInfo: nil)
17 | let scheduler = TestScheduler(initialClock: 0)
18 |
19 | func testPausedNoSkip() {
20 | let underlying = scheduler.createHotObservable([
21 | .next(150, 1),
22 | .next(210, 2),
23 | .next(230, 3),
24 | .next(301, 4),
25 | .next(350, 5),
26 | .next(399, 6),
27 | .completed(500)
28 | ])
29 |
30 | let pauser = scheduler.createHotObservable([
31 | .next(201, true),
32 | .next(205, false),
33 | .next(209, true)
34 | ])
35 |
36 | let res = scheduler.start(disposed: 1000) {
37 | underlying.pausable(pauser)
38 | }
39 |
40 | XCTAssertEqual(res.events, [
41 | .next(210, 2),
42 | .next(230, 3),
43 | .next(301, 4),
44 | .next(350, 5),
45 | .next(399, 6),
46 | .completed(500)
47 | ])
48 |
49 | XCTAssertEqual(underlying.subscriptions, [
50 | Subscription(200, 500)
51 | ])
52 |
53 | }
54 |
55 | func testPausedSkips() {
56 | let underlying = scheduler.createHotObservable([
57 | .next(150, 1),
58 | .next(210, 2),
59 | .next(230, 3),
60 | .next(301, 4),
61 | .next(350, 5),
62 | .next(399, 6),
63 | .completed(500)
64 | ])
65 |
66 | let pauser = scheduler.createHotObservable([
67 | .next(220, true),
68 | .next(300, false),
69 | .next(400, true)
70 | ])
71 |
72 | let res = scheduler.start(disposed: 1000) {
73 | underlying.pausable(pauser)
74 | }
75 |
76 | XCTAssertEqual(res.events, [
77 | .next(230, 3),
78 | .completed(500)
79 | ])
80 |
81 | XCTAssertEqual(underlying.subscriptions, [
82 | Subscription(200, 500)
83 | ])
84 |
85 | }
86 |
87 | func testPausedError() {
88 | let underlying = scheduler.createHotObservable([
89 | .next(150, 1),
90 | .next(210, 2),
91 | .error(230, testError),
92 | .next(301, 4),
93 | .next(350, 5),
94 | .next(399, 6),
95 | .completed(500)
96 | ])
97 |
98 | let pauser = scheduler.createHotObservable([
99 | .next(201, true),
100 | .next(300, false),
101 | .next(400, true)
102 | ])
103 |
104 | let res = scheduler.start(disposed: 1000) {
105 | underlying.pausable(pauser)
106 | }
107 |
108 | XCTAssertEqual(res.events, [
109 | .next(210, 2),
110 | .error(230, testError)
111 | ])
112 |
113 | XCTAssertEqual(underlying.subscriptions, [
114 | Subscription(200, 230)
115 | ])
116 |
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Tests/TestErrors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestErrors.swift
3 | // RxSwiftExt
4 | //
5 | // Created by Anton Nazarov on 25/05/2019.
6 | // Copyright © 2019 RxSwift Community. All rights reserved.
7 | //
8 |
9 | enum TestError: Error {
10 | case dummyError
11 | }
12 |
13 | let testError = TestError.dummyError
14 |
--------------------------------------------------------------------------------
/scripts/bootstrap-if-needed.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | RED='\033[1;31m'
6 | GREEN='\033[1;32m'
7 | NC='\033[0m' # No Color
8 |
9 | checksum_file="Carthage/cartSum.txt"
10 |
11 | # Computes current Carthage checksum using 'Cartfile.resolved' file, Swift version and checksum version.
12 | computeChecksum() {
13 | local version="1"
14 | { cat Cartfile.resolved; xcrun swift -version; echo "${version}"; } | md5
15 | }
16 |
17 | # Get previous checksum
18 | mkdir -p "Carthage"
19 | touch "${checksum_file}"
20 | if [ ! -f "${checksum_file}" ]; then
21 | prevSum="null";
22 | else
23 | prevSum=`cat Carthage/cartSum.txt`;
24 | fi
25 |
26 | # Get checksum
27 | cartSum=`computeChecksum`
28 |
29 | if [ "$prevSum" != "$cartSum" ] || [ ! -d "Carthage/Build/iOS" ]; then
30 | printf "${RED}Dependencies out of date with cache.${NC} Bootstrapping...\n"
31 | rm -rf Carthage
32 | scripts/bootstrap.sh
33 |
34 | echo `computeChecksum` > "${checksum_file}"
35 |
36 | else
37 | printf "${GREEN}Cache up-to-date.${NC} Skipping bootstrap...\n"
38 | fi
39 |
--------------------------------------------------------------------------------
/scripts/bootstrap.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | # https://github.com/mapbox/mapbox-navigation-ios/blob/master/scripts/wcarthage.sh
6 | applyXcode12Workaround() {
7 | echo "Applying Xcode 12 workaround..."
8 |
9 | xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
10 | trap 'rm -f "${xcconfig}"' INT TERM HUP EXIT
11 |
12 | # For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise
13 | # the build will fail on lipo due to duplicate architectures.
14 | echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = arm64 arm64e armv7 armv7s armv6 armv8' >> $xcconfig
15 | echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig
16 |
17 | export XCODE_XCCONFIG_FILE="${xcconfig}"
18 | echo "Workaround applied. xcconfig here: ${XCODE_XCCONFIG_FILE}"
19 | }
20 |
21 | applyXcode12Workaround
22 | carthage bootstrap
23 |
--------------------------------------------------------------------------------