Skip to content

[Sample Code] How do I...?

Gabriel Peal edited this page Sep 2, 2018 · 7 revisions

Use Fragment arguments in initial state?

Follow the docs here.

Use constructor dependency injection in my ViewModel?

Follow the docs here.

Create a ViewModel from my Fragment?

// Scoped to a Fragment
private val viewModel by fragmentViewModel(YourViewModel::class)
// Scoped to an Activity
private val viewModel by activityViewModel(YourViewModel::class)
// Scoped to an Activity but will throw if it wasn't created by another Fragment
private val viewModel by existingViewModel(YourViewModel::class)  

Subscribe to state changes in my Fragment?

The viewModel delegates above will automatically subscribe to changes and call invalidate()

Subscribe to specific state changes in my Fragment/ViewModel?

If you need to handle logging or subscribe to specific changes in state, you can:

In your Fragment:
viewModel.subscribe { ... }
viewModel.selectSubscribe(YourState::yourProp) { yourProp -> ... }
viewModel.selectSubscribe(YourState::yourProp1, YourState::yourProp2) { yourProp1, prop2 -> ... }
viewModel.subscribe(shouldUpdate = onSuccess(YourState::yourAsyncProp)) { state -> ... }
viewModel.subscribe(shouldUpdate = onFail(YourState::yourAsyncProp)) { state -> ... }
In your ViewModel:

Use the same functions above minus the viewModel. prefix. Subscriptions are usually made in the init { ... } of your ViewModel.

Subscribe to a map key or derived state

You can make properties on your state class that are derived from your constructor properties and can selectSubscribe to them like this:

data class MyState(myMap: Map<String, String> = emptyMap()) : MvRxState {
    val fooKey = myMap['foo']
}
...
selectSubscribe(MyState::fooKey) { ... }

Share state across Fragments?

To share state between flows, use the activityViewModel or existingViewModel delegates explained above and you will get the exact same instance in each Fragment.

Execute asynchronous (Observable) actions?

Ensure that your network request, data repository, or any other task is an Observable<T>. Inside of your ViewModel, there is an extension function execute that will map your Observable<T> stream to an Async<T> stream.

// Inside of your ViewMode
fun fetchListing() {
    YourRequest.create().execute { copy(yourProp = it) }
}

Refer to the Async docs for more info.

Have multiple instance of the same ViewModel class in the same activity scope?

Use the optional keyFactory parameter in your activityViewModel delegate.

Chain obervables

If you have one request that depends on another:

class YourViewModel(override val initialState: YourState) : MvRxViewModel<YourState>() {
    init {
        fetchListing()
        subscribe(shouldUpdate = onSuccess(YourState::listing) { state ->
            fetchSimilarListing()
        }
    }

    fun fetchListing() = withState { state ->
        if (!state.listing.shouldLoad) return@withState
        ListingRequest.create(state.id).execute { copy(listing = it) }
    }

    fun fetchSimilarListings() = withState { state ->
        if (!state.similarListings.shouldLoad) return@withState
        SimilarListingsRequest.create(state.listing()).execute { copy(similarListings = it) }
    }
}

Handle pagination

Use two properties on your state class: one of the current pagination request and one for the cumulative list of data. Use the request to determine whether to show things like loading states. Append the successful response to the list of data to use for rendering the items or to get the offset to fetch the next page. See the demo here.

Handle onSaveInstanceState and persistence across process restoration?

Follow the docs here.

Handle failure cases, snow snackbars, toasts, etc?

Subscribe to state changes. Most likely, you want to trigger these when an Async prop changes to Fail. To do that:

fun onViewCreated(...) {
    viewModes.asyncSubscribe(YourState::asyncProp, onFail = {
        Snackbar.make(...).show
    {
}