EntwineRx is a lightweight library of two operators to bridge from RxSwift to Combine and back again. You can use EntwineRx to make your existing RxSwift view models and operators work seamlessly with any Combine based code you might be developing.
Be sure to checkout the main Entwine repository for general testing tools and operators for Combine.
From Combine to RxSwift
bridgeToRx() operator that EntwineRx adds to Combine's
Publisher type means that you can bridge to RxSwift as simply as:
import Combine import RxSwift import EntwineRx let myRxObservable = Publishers.Just<Int, Never>(1).bridgeToRx()
From RxSwift to Combine
This direction requires a little more thought as we now have to account for Combine's requirement that any publisher obeys its strict back pressure requirements.
As RxSwift has no such restriction we have to give some guidance for how we would like any unrequested elements from our
Observable stream to be handled. We do this by introducing an element buffer to store our elements until they're requested by a subscriber.
We must then decide:
- How big that buffer should be
- What we should do when our buffer is full
As long as we are able to reason about the source of our upstream
Observable's elements, and the rate at which we expect our subscriber to consume them – this should be relatively straightforward.
In this case we can safely set a low buffer count, and fail if we enter a situation that is unexpected. (If failing is not an option, you can also choose to drop any new elements or drop the buffer's oldest elements.)
So what number? At least one. And one is often enough. A sequence would require a buffer size of at least the count of the sequence.
import Combine import RxSwift import EntwineRx // both `.just(_)` and `from(_)` deliver their elements immediately upon subscription – we need a buffer! let combinePublisher1 = Observable.just(1) .bridgeToCombine(bufferSize: 1, whenFull: .fail) let combinePublisher2 = Observable.from([1,2,3,4,]) .bridgeToCombine(bufferSize: 4, whenFull: .fail)
If the buffer does overflow the publisher will complete with an
RxBridgeFailure.bufferOverflow failure. From here you can decide how you wish to recover.
If you're feeling especially self-assured you can chain the
.assertBridgeBufferNeverOverflows() operator after the bridge operator. Be warned though – as the name suggests this will cause an assertion failure if your hypothesis proves incorrect.
Back pressure and Combine
Back pressure is a mechanism that Combine employs to enforce that publishers only produce as many elements as the subscriber has requested.
The reason the back pressure mechanism exists is to prevent a situation where elements build up in the buffer of some publisher faster than they can be processed by a downstream subscriber. This can very quickly consume system resources causing performance degradation – and ultimately out-of-memory errors.
Combine applies 'back pressure' upstream by contractually obliging a publisher to only emit an element if it is has been signalled by its subscriber though 'demand' requests. A subscriber can signal at any point during the lifetime of a subscription – and may decide not to signal at all. With back pressure, it becomes possible to tightly control the rate of production of any arbitrary publisher.
For publishers that rely on a source with an unbounded rate of production – an
Observable, a timer, mouse clicks, etc. – the publisher must either maintain a buffer or drop elements to maintain its obligations to the downstream subscriber.
As part of another Swift Package:
- Include it in your
Package.swiftfile as both a dependency and a dependency of your target.
import PackageDescription let package = Package( ... dependencies: [ .package(url: "http://github.com/tcldr/EntwineRx.git", .upToNextMajor(from: "0.0.0")), ], ... targets: [ .target(name: "MyTarget", dependencies: ["EntwineRx"]), ] )
- Then run
swift package updatefrom the root directory of your SPM project. If you're using Xcode 11 to edit your SPM project this should happen automatically.
As part of an Xcode 11 or greater project:
- Select the
File -> Swift Packages -> Add package dependency...menu item.
- Enter the repository url
https://github.com/tcldr/EntwineRxand tap next.
- Select 'version, 'up to next major', enter
0.0.0, hit next.
- Select the EntwineRx library and specify the target you wish to use it with.
n.b. EntwineRx is pre-release software and as such the API may change prior to reaching 1.0. For finer-grained control please use
.upToNextMinor(from:) in your SPM dependency declaration
Full documentation for EntwineRx can be found at http://tcldr.github.io/Entwine/EntwineRxDocs.
Copyright and license
Copyright 2019 © Tristan Celder
EntwineRx is made available under the MIT License