---
title: 'Swift Talk: Comparing RxSwift and ReactiveSwift'
teaser: 'Many types make light work.

  '
tags: rxswift,rxcocoa,reactiveswift,reactivecocoa,swift,reactive programming,frp,ios
author: Adam Sharp
published_on: 2017-02-02
---

In June 2016, [Chris Eidhof][] and [Florian Kugler][] (of [objc.io][] fame)
started a new Swift screencast series called [Swift Talk][]. The format is
simple: for 15 to 20 minutest each week, two experienced programmers
pair-program, problem-solve and refactor together. The result is some excellent
insight into the _process_ of programming and design.

[Chris Eidhof]: https://twitter.com/chriseidhof
[Florian Kugler]: https://twitter.com/floriankugler
[objc.io]: https://www.objc.io/
[Swift Talk]: https://talk.objc.io/

Last week in [episode #34][e34], Chris and Florian did their first episode on one
of my favourite topics: Reactive Programming. They work through a
reactive programming problem using [RxSwift][], exploring some of the most
common use cases for a reactive programming library: asynchronous networking,
combining event streams from multiple sources, and binding values to UI
controls. The [source code][e34-source] for the episode is available for free
on GitHub. Whether or not you've watched the episode, I recommend cloning the
repo and playing around with the app in the simulator.

[e34]: https://talk.objc.io/episodes/S01E34-reactive-programming
[e34-source]: https://github.com/objcio/S01E34-reactive-programming
[RxSwift]: https://github.com/ReactiveX/RxSwift

I haven't yet had the opportunity to work with RxSwift, but I have a lot of
experience with [ReactiveCocoa][], and have made a few contributions. Since
the recent release of [ReactiveCocoa 5][], which overhauled its UI binding
APIs for Swift 3, and [ReactiveSwift][] (the cross-platform core of
ReactiveCocoa), I thought it would be interesting to use this example app from
Swift Talk to compare the two libraries.

[ReactiveSwift]: https://github.com/ReactiveCocoa/ReactiveSwift
[ReactiveCocoa]: https://github.com/ReactiveCocoa/ReactiveCocoa
[ReactiveCocoa 5]: https://github.com/ReactiveCocoa/ReactiveCocoa/releases/tag/5.0.0

A quick note on terminology: throughout this article, I'll be using RxSwift to
also refer to RxCocoa, which is bundled alongside it and provides the UI
binding layer. In the same way, I'll be using ReactiveSwift and ReactiveCocoa
interchangeably.

## Porting from RxSwift to ReactiveSwift

Let's take a first pass and port examples in the source directly to the
equivalent ReactiveSwift APIs. Even though the changes are fairly minimal, we
can learn a lot about about the philosophical differences between the two
libraries.

### Observing changes to a simple UI control

```diff
-let priceSignal = priceSlider.rx.value
+let priceSignal = priceSlider.reactive.values
     .map { floor(Double($0)) }

-priceSignal
+priceLabel.reactive.text <~ priceSignal
     .map { "\($0) USD" }
-    .bindTo(priceLabel.rx.text)
-    .addDisposableTo(disposeBag)
```

[Original source](https://github.com/objcio/S01E34-reactive-programming/blob/master/ReactiveUIExample/ViewController.swift#L49-L50)

RxSwift's `bindTo(_:)` and `addDisposableTo(_:)` functions are both unified in
ReactiveSwift under the binding operator, `<~`. On the left side of the
operator is the "binding target", defined by `BindingTargetProtocol`.  Binding
targets do two things: consume values from a stream, and define a
[`Lifetime`][Lifetime], i.e., when the binding should end (usually when the
target is deinitialised).

[Lifetime]: https://github.com/ReactiveCocoa/ReactiveSwift#lifetime-limits-the-scope-of-an-observation

### Wrapping a network call

```diff
-func load<A>(_ resource: Resource<A>) -> Observable<A> {
-    return Observable.create { observer in
+func load<A>(_ resource: Resource<A>) -> SignalProducer<A, AnyError> {
+    return SignalProducer { observer, disposable in
         print("start loading")
         self.load(resource) { result in
             sleep(1)
             switch result {
             case .error(let error):
-                observer.onError(error)
+                observer.send(error: AnyError(error))
             case .success(let value):
-                observer.onNext(value)
-                observer.onCompleted()
+                observer.send(value: value)
+                observer.sendCompleted()
             }
         }
-        return Disposables.create()
     }
 }
```

[Original source](https://github.com/objcio/S01E34-reactive-programming/blob/master/ReactiveUIExample/ViewController.swift#L14-L29)

The biggest difference here is the [`SignalProducer`][SignalProducer] type.
Here's where ReactiveSwift begins to diverge from RxSwift: the notion of
"hot" and "cold" signals are separated out into two different types. You can
compare RxSwift's [explanation of the difference][rxswift-hot-cold] with
ReactiveSwift's [TV streaming analogy][reactiveswift-hot-cold].

[SignalProducer]: https://github.com/ReactiveCocoa/ReactiveSwift#signalproducer-deferred-work-that-creates-a-stream-of-values
[rxswift-hot-cold]: https://github.com/ReactiveX/RxSwift/blob/master/Documentation/HotAndColdObservables.md
[reactiveswift-hot-cold]: https://github.com/ReactiveCocoa/ReactiveSwift#signal-a-unidirectional-stream-of-events

The second major difference between the two libraries is ReactiveSwift's
`Signal` and `SignalProducer` add a second generic type parameter: the
`Error` type. If you're familiar with [Swift's error handling
model][swift-error-handling], it might seem like an unconventional choice to
have strongly-typed errors. One benefit is that errors can become
self-documenting: you have a type whose documentation or source code can be
looked up to discover what errors might occur. But the real benefit is the
existence of the [`NoError`][NoError] type, which allows you to _prove at
compile time_ that no event will ever occur, by being impossible to construct.
Many of ReactiveSwift's error handling features are made possible by
`NoError`.

In this case, because we're bridging untyped Swift errors into the
strongly-typed model of ReactiveSwift, we use the [`AnyError`][AnyError]
wrapper type.

[swift-error-handling]: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html
[NoError]: https://github.com/antitypical/Result/blob/d7f10e2b1745d189434d262072fac764c3021ba8/Result/Result.swift#L179-L184
[AnyError]: https://github.com/antitypical/Result/blob/d7f10e2b1745d189434d262072fac764c3021ba8/Result/Result.swift#L190-L192

### Combining the results

```diff
-let vatSignal = countriesDataSource.selectedIndex.asObservable()
-    .distinctUntilChanged()
+let vatSignal = countriesDataSource.selectedIndex.producer
+    .skipRepeats()
     .map { [unowned self] index in
         self.countriesDataSource.countries[index].lowercased()
-    }.flatMap { [unowned self] country in
-        self.webservice.load(vat(country: country)).map { Optional.some($0) }.startWith(nil)
-    }.shareReplay(1)
+    }.flatMap(.latest) { [unowned self] country in
+        self.webservice.load(vat(country: country)).map { Optional.some($0) }.prefix(value: nil)
+    }.replayLazily(upTo: 1)
```

[Original source](https://github.com/objcio/S01E34-reactive-programming/blob/master/ReactiveUIExample/ViewController.swift#L57-L63)

There's a little more going on here:

- `countriesDataSource.selectedIndex` has changed from a `Variable<Int>` to a
  `MutableProperty<Int>`. To observe the current value, followed by all future
  changes, we use the `producer` property, which returns a
  `SignalProducer<Int, NoError>`.

- There's a new `.latest` parameter to `flatMap()`. You can learn about the
  [differences between flattening strategies][flatten-strategies] in
  ReactiveSwift's documentation. These strategies are all possible in RxSwift,
  but the `flatMap` operator chooses a default strategy. In ReactiveSwift, no
  strategy is considered to be a sensible default for all cases, and so you
  instead choose the strategy that is most appropriate for your use case.

- The `shareReplay()` operator is basically the same, but under the name
  `replayLazily(upTo:)`.

RxSwift provides the `Variable` type, a mutable box that holds a current value
and lets you observe changes to its value over time. In order to combine
`Variable`s with other streams, you have to convert it to an `Observable`,
losing its "`Variable`-ness" in the process. ReactiveSwift's `MutableProperty`
is equivalent, but along with `PropertyProtocol`, there are a number of
interesting ways you can combine properties, while preserving the fact that a)
they are guaranteed to have a current value, and b) they are guaranteed to
never fail. You can [read more about `Property`][Property] in ReactiveSwift's
documentation.

[flatten-strategies]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/BasicOperators.md#flattening-event-streams
[Property]: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/FrameworkOverview.md#properties

## What next?

RxSwift and ReactiveSwift are two different approaches — perhaps surprisingly
so — to the same underlying problem. Both make heavy use of Swift's type
system to help you describe your application's asynchronous logic in a
high-level way, and with very different results.

This episode of Swift Talk was an introduction to reactive programming, and not
intended to prescribe the "right way" of solving the problem. There's
certainly a more idiomatic way to write this code with either library. By
comparing these two libraries, we've been able to see how even apparently
subtle differences in API design can influence the code we write.
