docs/new-2x.md
The first thing you'll notice with Mavericks 2.0 is the change from MvRx to Mavericks. When MvRx was first built in 2017, it was nearly impossible to build a complex app without RxJava. Today, many apps are transitioning to Kotlin coroutines and many new apps will never need RxJava at all. To modernize Mavericks, we completely rewrote the internals with coroutines and removed the RxJava 2 dependency from the core artifact.
All APIs in the core library are now coroutines based.
APIs that take suspending lambdas will auto-cancel the previous instance if it hasn't finished by the time the next value emits. In other words, they behave like Flow<T>.mapLatest.
| MvRx 1.x | Mavericks 2.x |
|---|---|
| <pre lang="kotlin">subscribe(...): Disposable</pre> | <pre lang="json">onEach(...): Job</pre> |
| <pre lang="kotlin">selectSubscribe( |
stateProp: KProperty1<S, T>,
action: (T) -> Unit,
): Disposable</pre>Plus multi-prop overloads |<pre lang="kotlin">onEach(
stateProp: KProperty1<S, T>,
action: suspend (T) -> Unit,
): Job</pre>Plus multi-prop overloads |
| <pre lang="kotlin">asyncSubscribe(
stateProp: KProperty1<S, Async<T>>,
onFail: (Throwable) -> Unit,
onSuccess: (T) -> Unit,
): Disposable</pre> |<pre lang="kotlin">onAsync(
stateProp: KProperty1<S, Async<T>>,
onFail: suspend (Throwable) -> Unit,
onSuccess: suspend (T) -> Unit,
): Job</pre> |
| execute is extension on Observable<T>, Single<T>, and Completable| execute is extension on Flow<T>, suspend () -> T, and Deferred<T>|
| No equivalent |<pre lang="kotlin">Flow<T>.setOnEach(dispatcher: Dispatcher, reducer: suspend (T) -> Unit): Job</pre> |
| No equivalent |<pre lang="kotlin">ViewModel<S>.stateFlow: Flow<S></pre>|
| No equivalent |<pre lang="kotlin">suspend ViewModel<S>.awaitState(): S</pre>|
Mavericks can now retain previously successful values across subsequent Loading and Fail states. You can read more about it here.
Flow<T>.setOnEach { copy(...) }Sometimes, you don't want to map a stream of data to Async<T>, you just want to map a stream of data to a state property. To do that, you can call Flow<T>.setOnEach { copy(...) }. When you do that, Mavericks will subscribe to the flow, call your reducer each time with each item, then cancel the job when the ViewModel is cleared.
MavericksViewModel can be used and will behave exactly like it used to. However, the underlying implementation no longer extends Jetpack's own ViewModel. Instead, it is just a normal Kotlin class which gets wrapped in a Jetpack ViewModel under the hood. This means that you can create your own instances of MavericksViewModels and use them outside of the standard delegates.
Upgrading from MvRx 1.x should be fairly simple. Mavericks 2.0 includes a mavericks-rxjava2 artifact which adds back all existing RxJava based APIs (although they now wrap the internal coroutines implementation).
Previously, you needed to pass debugMode into your BaseMvRxViewModel super class. Now, you initialize MvRx one time in Application.onCreate. To do so, call Mavericks.initialize(this). Because debugMode is no longer passed into each ViewModel individually, you don't even need a base ViewModel class anymore. You can simply extend BaseMvRxViewModel each time or migrate to just using MavericksViewModel if you no longer need the rxjava2 APIs.
With MvRx 1.x, you had to make your base Fragment class extend BaseMvRxFragment. Now, you can make it just implement MavericksView. The rxjava2 artifact still ships with BaseMvRxFragment but it is deprecated and everything will continue to work with the MavericksView interface.
In MvRx 1.x, MvRxTestRule would set global RxJava schedulers to be synchronous (trampoline) schedulers for tests. This was required for MvRx state stores to behave synchronously. However, your tests may have implicitly relied on this behavior. As a result, you may need to create your own RxRule to add back this behavior. One such example of an RxRule can be found here.