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 framework — RxRelay — and adjusted RxSwift’s dependency graph as follows:
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
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.
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
BehaviorSubject) instead if you need this sort of behavior.
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
onNext represents the moment the element has been emitted, whereas
afterNext represents the moment
after it has been emitted and pushed downstream.
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 5 now supports binding to multiple 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.
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.
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
Observer in different scenarios, or
S which represents
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
ElementType to simply
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:
ElementTypewere renamed to
TraitTypewas renamed to
SharedSequence.Swas renamed to
Owas renamed to
Swere renamed to
Swas also renamed to
Rwas renamed to
ReactiveCompatible.CompatibleTypewas renamed to
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.
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.