What’s new in RxSwift 5

Learn about the newest additions to RxSwift

Shai Mishali
May 6 · 6 min read

RxSwift 5 was finally released just a few days ago, and I thought this would be a great opportunity to share a quick list of the most note-worthy changes pushed into this release.

No worries though, as this release is mostly source-compatible with only a few deprecations and renames. But it also packs a bunch of underlying improvements I’ll detail below.


Relays are now a separate framework — RxRelay

Relays are a great abstraction layer on top of Subjects that lets you relay elements without worrying about errors or completion events. Since they were added to RxSwift, they lived as part of the RxCocoa project.

Some developers were unhappy with this, since it meant RxCocoa must be imported to use Relays even on code layers where it didn’t necessarily make sense. It also made it impossible to use Relays under Linux, where RxCocoa can’t be used.

For the reasons above, we’ve moved Relays into their own frameworkRxRelay — and adjusted RxSwift’s dependency graph as follows:

On the left: RxSwift 4’s dependency graph. On the right: RxSwift 5’s dependency graph.

This lets you use RxSwift and RxRelay only, without depending on RxCocoa if you don’t need it, and also aligns with RxJava where it’s a separate framework.

Note: This is a backward compatible change, since RxCocoa imports RxRelay directly. Meaning, you can keep importing RxCocoa without also importing RxRelay and everything will work as it did before.

TimeInterval → DispatchTimeInterval

Schedulers have been refactored in RxSwift 5 to deprecate the usage of TimeInterval in favor of DispatchTimeInterval. This allows for better granularity of event scheduling and higher stability when sub-second timings are needed.

This affects all time-based operators such as throttle, timeout, delay, take etc. As a fortunate side-effect, this disambiguates take, where it wasn’t obvious if a developer refers to seconds or number of elements.

RxSwift 4.x:

RxSwift 4 uses TimeInterval

RxSwift 5.x:

RxSwift 5 uses DispatchTImeInterval

Variable is finally deprecated

Variable is a concept added into RxSwift in its early days which basically let you create an imperative bridge by “setting” and “getting” a current value to and from it. It was a seemingly helpful measure to get developers started with RxSwift until they fully understand “Reactive Thinking”.

This construct proved to be problematic as it was heavily abused by developers to create highly-imperative systems instead of using Rx’s declarative nature. This was especially common with beginners to Reactive Programming and conceptually prevented many from understanding this is a bad practice and a code smell. This is why Variable was soft-deprecated with a runtime warning, already in RxSwift 4.x.

In RxSwift 5, It is now officially and completely deprecated, and the recommended approach is to use BehaviorRelay (or BehaviorSubject) instead if you need this sort of behavior.

RxSwift 4.x:

RxSwift 4.x has a soft-deprecation for Variable

RxSwift 5.x:

RxSwift 5.x completely deprecates Variable

Additional do(on:) overloads

do is a great operator to use when you want to perform some side-effect such as logging, or simply “listen in” the middle of your stream.

To align with RxJava, RxSwift now offers not only do(onNext:) but also after overloads, such as do(afterNext:). onNext represents the moment the element has been emitted, whereas afterNext represents the moment after it has been emitted and pushed downstream.

RxSwift 4.x:

RxSwift 4.x provides do(onNext:onError:onCompleted:)

RxSwift 5.x:

RxSwift 5.x also has do(afterNext:afterError:afterCompleted:)

bind(to:) now supports multiple observers

There are scenarios where you have to bind a stream to multiple observers. In RxSwift 4, you would usually simply duplicate the binding code:

RxSwift 4 only allows binding to a single observer at a time

RxSwift 5 now supports binding to multiple observers:

RxSwift 5 allows binding to a variadic list of observers

This still resolves to a single Disposable, which means it’s backward compatible with the single-observer variation.

A new compactMap operator

As developers, you often deal with streams of Optional values. To unwrap these values, the community has had its own solutions to it, such as the unwrap operator from RxSwiftExt or filterNil from RxOptional.

RxSwift 5 adds a new compactMap operator to align with the Swift Standard Library, bringing this ability into the core library.

RxSwift 4.x:

Unwrapping optional streams is not built-in to RxSwift 4

RxSwift 5.x:

RxSwift 5.x provides compactMap to unwrap optional streams

toArray() now returns Single<T>

toArray() is an operator that emits the entire stream as an array once the stream completes.

Since the inception of RxSwift, this operator always returned an Observable<T>, but due to the introduction of Traits — specifically, Single, it made sense to change the return type to Single<T> to provide that type safety and guarantee of only getting a single emitted value from this operator.

RxSwift 4.x:

toArray() returns an Observable<T> in RxSwift 4.x

RxSwift 5.x:

toArray() returns a Single<T> in RxSwift 5.x

Generic constraints naming overhaul

RxSwift is a heavy consumer of generic constraints. Since its early days, the library used single-letter constraints to describe certain types. For example, ObservableType.E represents the generic type of the Observable stream.

This works fine but causes some confusion with constraints such as O which represents both Observable and Observer in different scenarios, or S which represents Subject and Sequence.

Furthermore, these single-letter constraints weren’t providing good self-documenting code and made it hard for non-contributors to understand the references.

For these reasons we’ve overhauled most generic constraints for both private and public interfaces to be more informational and verbose.

The most widely impacting rename is E and ElementType to simply Element.

RxSwift 4.x:

Extending Observable in RxSwift 4 uses the E generic constraint

RxSwift 5.x:

Extending Observable in RxSwift 5 uses the Element generic constraint

The generic renames were quite extensive. Here’s a mostly-complete list of them. Most of these changes relate to the internal APIs of RxSwift, and only a few of these would affect you as developers:

  • E and ElementType were renamed to Element.
  • TraitType was renamed to Trait.
  • SharedSequence.S was renamed to SharedSequence.SharingStrategy.
  • O was renamed to Observer and Source wherever applicable.
  • C and S were renamed to Collection and Sequence, respectively.
  • S was also renamed to Subject where applicable.
  • R was renamed to Result.
  • ReactiveCompatible.CompatibleType was renamed to ReactiveCompatible.ReactiveBase.

Community Projects

Many RxSwift Community projects already migrated to RxSwift 5 and released appropriate versions, so the migration process should be relatively smooth. Some of the projects that already migrated are: RxSwiftExt, RxDataSources, RxAlamofire, RxOptional, and more.

Wrapping up

The changes listed above are the majority of developer-facing changes, but there are many more smaller fixes that are out-of-scope for this sort of post such as fully fixing compatibility with Swift 5 under Linux, minor anomalies, etc.

Feel free to check out the full Change Log and participate in the discussions in the official repository: https://github.com/ReactiveX/RxSwift

Hope you’ve enjoyed this post :-)

Got any questions? Feel free to share them below, in the comments section.

Thanks to Ian Keen and Gil Goldenberg.

Shai Mishali

Written by

iOS Tech Lead @ Gett 🚕 RxSwift & RxSwiftCommunity core contributor. International speaker and worldwide hackathon winner. Fiddling with tech for a living. 🤓