Happy new 2021! It's been quite a rough year, and I'm sure we're all hoping for a much better year where we'll finally be able to go to our normal lives.
To kick off the year, what's better than starting with a new release? Say hello to RxSwift 6.
This blog post will give you a quick rundown of some of the most note-worthy changes that might affect you.
Note: This is just a partial list of some interesting changes, that obviously don't include a massive amount of smaller bug fixes and improvements.
For the full change log, checkout the release notes.
Not a technical change, for sure, but definitely one worth mentioning.
RxSwift always used Reactive Extensions' original Volta Eel logo, but I felt that this major release can be a great opportunity to add a bit of a unique edge to RxSwift's logo.
An opportunity to make it unique with its own spirit and identity, in a way that still gives honor to the original ReactiveX logo as well as Swift's logo.
I give you, the new RxSwift logo!
Binder moves from RxCocoa to RxSwift
This is a small but highly requested change that just made sense. Binder, as the name suggests, lets you define a way to bind an Observable stream into it, to reactively feed that bound input.
Uses an underlying Binder to let you bind into rx.isEnabled
Binder always lived inside RxCocoa, but use-cases by our community and various discussions showed that it is a super useful entity that serves the broader RxSwift audience, so it is now part of it and RxCocoa isn't required to use Binder.
Automatic synthesis of Binders using @dynamicMemberLookup
RxSwift includes a namespace called .rx which lets you put your own Reactive Extensions on it for specific objects.
For example, given a custom MyView that looks like this:
Unfortunately, this sort of code is also quite repetitive and boilerplate-y. All it really does is mirror the underlying Base's properties.
Fortunately, since Swift 5.1, we have a better solution for this problem - @dynamicMemberLookup.
RxSwift 6 will automatically synthesize all of these Binders for any class, which means that all of the Binder code I showed above can be entirely removed, and really clean up your code.
Just start writing .rx on any AnyObject-inheriting class, and you'll immediately see automatically synthesized binders for every property of the extended base object:
Don't worry though, your own custom reactive extensions still take precedence over the synthesized dynamic member ones, which lets you have more granular control.
withUnretained comes to RxSwift
A common pattern when working with RxSwift and Cocoa / iOS code is to get a weak reference to self so you could pass an emitted value to the owner, for example:
Note: Be careful using this operator with a buffering operator such as share(replay: 1) as it would also buffer the retained object, which might cause a retain cycle. If you want a simpler alternative to this, check out subscribe(with:onNext:onError:onCompleted:onDisposed:) from RxSwift 6.1.
This might seem fine for a single output, but imagine how frequently this pops in a single code base.
Luckily RxSwiftExt, a community project that holds various additional operators that aren't part of RxSwift itself, has an operator for this very case, called withUnretained. It was initially implemented by a good friend and fellow iOS speaker, Vincent Pradeilles.
Due to the popularity of this operator, and how common this use case is, it made sense to bring it into RxSwift itself.
Starting with RxSwift 6, you can rewrite the above code like so:
viewModel.importantInfo.withUnretained(self)// Tuple of (Object, Element).subscribe(onNext:{owner,infoinowner.doImportantTask(with:info)}).disposed(by:disposeBag)
Much cleaner!
Infallible
Infallible is a new type of stream that is identical to Observable with only one difference — it is guaranteed to not fail. This means you cannot emit errors from it, guaranteed by the compiler.
For example, you can create one similarly to Observable.create, using Infallible.create:
Infallible<String>.create{observerinobserver(.next("Hello"))observer(.next("World"))observer(.completed)// No way to error herereturnDisposables.create{// Clean-up}}
Note that you can only pass a .next(Element) or .completed event. There is no way for you to fail this stream. All other operators that work around Infallible have the same guarantee (for example, you cannot call Infallible.error as opposed to Observable.error)
If you worked with RxCocoa, you're probably thinking — hey, what's the difference between this, Driver, and Signal?
First of all, Infallible lives inside RxSwift, while the other two live in RxCocoa. But more importantly, both Driver and Signal always use the MainScheduler and share their resources (using share()). This isn't the case with Infallible which is entirely a basic observable, only with compile-time guarantee for infallibility.
New decode(type:decoder:) operator for Observable<Data>
RxSwift 6 adds a decode operator that specifically works on Observables that emit Data, similarly to Combine's:
If you're looking at this and saying, hey - this looks a lot like a Result<Element, Swift.Error>, you're absolutely right!
Starting with RxSwift 6, SingleEvent is simply an alias for Result<Element, Swift.Error>.
This change is also reflected in other APIs such as subscribe:
// RxSwift 5single.subscribe(onSuccess:{valueinprint("Got a value: \(value)")},onError:{errorinprint("Something went wrong: \(error)")})// RxSwift 6single.subscribe(onSuccess:{valueinprint("Got a value: \(value)")},onFailure:{errorinprint("Something went wrong: \(error)")})
New distinctUntilChange(at:) operator for Key Paths
distinctUntilChanged is a super useful operator which lets you drop identical value emissions to avoid wastefully processing them.
RxSwift 6 includes a new DisposeBag function builder for SwiftUI-like, comma-less syntax:
vardisposeBag=DisposeBag{observable1.bind(to:input1)observable2.drive(input2)observable3.subscribe(onNext:{valinprint("Got \(val)")})}// Also works for insertionsdisposeBag.insert{observable4.subscribe()observable5.bind(to:input5)}
Many, many (many) operator renames
We took the time in RxSwift 6 and renamed many operators to better respect Swift's code guidelines where possible.
Here is a mostly complete list:
RxSwift 5
RxSwift 6
catchError(_:)
catch(_:)
catchErrorJustReturn(_:)
catchAndReturn(_:)
elementAt(_:)
element(at:)
retryWhen(_:)
retry(when:)
takeUntil(_:)
take(until:)
takeUntil(behavior:_:)
take(until:behavior:)
takeWhile(_:)
take(while:)
takeWhile(behavior:_:)
take(while:behavior:)
take(.seconds(3))
take(for: .seconds(3))
skipWhile(_:)
skip(while:)
takeUntil(_:)
take(until:)
observeOn(_:)
observe(on:)
subscribeOn(_:)
subscribe(on:)
Better support for XCFrameworks
Every release of RxSwift 6 will now have a set of XCFrameworks bundled with the release.
This allows easily linking against a prebuilt copy of RxSwift without worrying about forward compatibility when upgrading to the next version of Swift, thanks to binary module stability.
Wrapping up!
Hope you've enjoyed this quick rundown of some of the most interesting features and updates to RxSwift 6, but it's not all that was fixed.
There are a ton of bug fixes, improvements and small additions that are worth checking out. Be sure to take a moment to review the release notes.
Rx is a generic abstraction of computation expressed through Observable<Element> interface, which lets you broadcast and subscribe to values and other events from an Observable stream.
RxSwift is the Swift-specific implementation of the Reactive Extensions standard.
While this version aims to stay true to the original spirit and naming conventions of Rx, this project also aims to provide a true Swift-first API for Rx APIs.
Cross platform documentation can be found on ReactiveX.io.
Like other Rx implementations, RxSwift's intention is to enable easy composition of asynchronous operations and streams of data in the form of Observable objects and a suite of methods to transform and compose these pieces of asynchronous work.
KVO observation, async operations, UI Events and other streams of data are all unified under abstraction of sequence. This is the reason why Rx is so simple, elegant and powerful.