From RAC to RxSwift: The (Mini) Survival Guide
Do we all agree that Reactive Programming is definitely a thing during these days? I think yes, so it is also true that one of the trending projects in the Swift world is definitely the new port of Reactive Extensions, RxSwift. This project seems to be constantly attracting newcomers, people new to Reactive Programming but also people who previously worked with Reactive Cocoa. Questions about how to migrate from one world to the other are constant, let’s see to answer at some of them.
What is what?
Scheduler… Oh Dear!
The first thing to understand is that there’s no real 1:1 translation from RAC to RxSwift, for a certain number of reasons. RAC is reported to be inspired by functional reactive programming, but later is, while explaining how is related to Rx, described as originally inspired, and therefore heavily influenced, by Microsoft’s Reactive Extensions (Rx) library. Even if it’s inspired by Rx, the influence of FRP, mostly for naming, is still strong and an easy migration is not possible.
FRP and Rx are two different things, the former notation takes in consideration a time-continuous system with denotative semantics, the latter is a discrete collection API on purpose, as explained in the previous article I wrote. RAC’s terminology has origin from FRP, but the current 3.0 version shares some concepts from Rx, creating some rough edges.
Back to the important thing, how do I translate a
Signal to the Rx world? Simple: to an
Observable. An observable is just a producer and we can subscribe to it to consume all the produced values. What about
SignalProducer? Same thing, it’s an
Let’s find out…
Both frameworks are relying on events, the common part is that an event can be of the following 3 types:
- Next: sending a new value to observers
- Error: sending an error to observers, completing the stream
- Completion: termination the current stream unsubscribing all observers
RAC has an extra one that is Interruption, used to notify subscribers when an event producer is disposed when has not being terminated.
In RAC a
Signal is something that can be observed and is producing events. Next, error and completion are shared concepts in RxSwift and RAC. As previously said, RxSwift doesn’t have th concept of interruption that can be modeled just disposing the resource. So a
Signal has some things in common with an
Observable, even if in my humble opinion, is more closer to an old fashion Observer Pattern’s implementation, rather than an Rx’s observable.
pipe() call on a
Signal in RxSwift generates a new type that is a
Subject. In RxSwift an
Observable is not controllable by the outside world, for this purpose there’s a
Subject. A good analogy is with Future/Promise: a Future is a read-only placeholder of a value that will be available at some point, similar to an
Promise instead can be written and manipulated from outside, similar to what a
Composition multiple instances of
Observer is similar for both implementations using operators, with the difference that a
Signal can be lifted to the world of
SignalProducer to allow composition between them.
SignalProducer has a lot more to share with an
Observable rather than a
Signal, could seem strange but it’s true. Similar to what a
SignalProducer does, an
Observable returns a
Disposable that can be used to cancel the associated work.
The few differences are subtle, but can be very important conceptually. The first one is that a
SignalProducer has a
start() method, creating a sort of imperative requirement that in the Rx world cannot exist, being declarative on every aspect. The second one is that disposing it will send an interruption signal, behavior that doesn’t happen in the Rx’s observable world, disposing it will not send any extra event. These are 2 big differences.
Composition is very similar, like for
Signal, instances of
SignalProducer can be combined to create new ones. Composability is a strong component in both worlds.
Schedulers are very similar, the big difference here is that on RAC a
Scheduler is serial-only, on the other hand in Rx can be also concurrent. Both implementations are oriented to make asynchronous computation easier and, mainly for test purposes, have a synchronous immediate scheduler implementation to immediately perform a task.
To recap what I wrote, here’s an easy list:
Transition from RAC to RxSwift is not just a matter of implementation and type names, it’s also about understanding how the 2 frameworks handle the same situation, but differently and is required to adapt the code (and design) accordingly. Even if they look similar in a first instance, they are deeply different and are based on different concepts.