RxSwift is a Swift implementation of the ReactiveX (Rx) APIs. While ReactiveCocoa was inspired and heavily influenced by Rx, ReactiveSwift is an opinionated implementation of functional reactive programming, and intentionally not a direct port like RxSwift.
ReactiveSwift differs from RxSwift/ReactiveX where doing so:
- Results in a simpler API
- Addresses common sources of confusion
- Matches closely to Swift, and sometimes Cocoa, conventions
The following are a few important differences, along with their rationales.
One of the most confusing aspects of Rx is that of “hot”, “cold”, and “warm” observables (event streams).
In short, given just a method or function declaration like this, in C#:
IObservable<string> Search(string query)
… it is impossible to tell whether subscribing to (observing) that
IObservable
will involve side effects. If it does involve side effects, it’s
also impossible to tell whether each subscription has a side effect, or if only
the first one does.
This example is contrived, but it demonstrates a real, pervasive problem that makes it extremely hard to understand Rx code (and pre-3.0 ReactiveCocoa code) at a glance.
ReactiveSwift addresses this by distinguishing side effects with the separate
Signal
and SignalProducer
types. Although this
means there’s another type to learn about, it improves code clarity and helps
communicate intent much better.
In other words, ReactiveSwift’s changes here are simple, not easy.
When Signals and SignalProducers are allowed to fail in ReactiveSwift,
the kind of error must be specified in the type system. For example,
Signal<Int, AnyError>
is a signal of integer values that may fail with an error
of type AnyError
.
More importantly, RAC allows the special type NoError
to be used instead,
which statically guarantees that an event stream is not allowed to send a
failure. This eliminates many bugs caused by unexpected failure events.
In Rx systems with types, event streams only specify the type of their values—not the type of their errors—so this sort of guarantee is impossible.
In most versions of Rx, Streams over time are known as Observable
s, which
parallels the Enumerable
type in .NET. Additionally, most operations in Rx.NET
borrow names from LINQ,
which uses terms reminiscent of relational databases, like Select
and Where
.
ReactiveSwift, on the other hand, focuses on being a native Swift citizen first and foremost, following the Swift API Guidelines as appropriate. Other naming differences are typically inspired by significantly better alternatives from Haskell or Elm (which is the primary source for the “signal” terminology).
Rx is basically agnostic as to how it’s used. Although UI programming with Rx is very common, it has few features tailored to that particular case.
ReactiveSwift takes a lot of inspiration from ReactiveUI, including the basis for Actions.
Unlike ReactiveUI, which unfortunately cannot directly change Rx to make it more friendly for UI programming, ReactiveSwift has been improved many times specifically for this purpose—even when it means diverging further from Rx.