What is the idiomatic way of refreshing using RxBinding on a SwipeRefreshLayout

623 Views Asked by At

I'm struggling to understand how to properly use RxBinding, if I want to call a network request when a user swipes down on a SwipeRefreshLayout, I would expect to say something like

    RxSwipeRefreshLayout.refreshes(swipeContainer)
            .flatMap { networkRequest() }
            .subscribeBy(
                    onNext = { list: List<Data> -> Timber.d(data) },
                    onError = { showErrorSnackbar(it) },
                    onComplete = { Timber.d("On Complete") })

But this doesn't work for me, because I have that wrapped in a function called setupSwipeRefresh() which I call in onStart, so as soon as onStart is called the network request is made because that's when the layout is subscribed to.

Now I feel unsure about what to do. I could just put the whole thing in refreshListener but that kind of defeats the purpose of RxBinding.

Or I could execute the networkRequest in the onNext of the swipeContainer. But then it would look something like

       RxSwipeRefreshLayout.refreshes(swipeContainer)
            .subscribeBy(
                    onNext = {
                        networkRequest()
                                .subscribeBy(
                                        onNext = { list: List<Data> ->
                                            Timber.d(data)
                                        })
                    },
                    onError = { showErrorSnackbar(it) },
                    onComplete = { Timber.d("On Complete") })

But calling subscribe twice just seems like an Anti-Pattern, So yeah, since SwipeRefreshLayout is in the RxBinding library, there must be an idiomatic way of doing this, because it seems like the most common use case.

1

There are 1 best solutions below

0
On

You are looking for something like this:

SwipeRefreshLayout viewById = findViewById(R.id.activity_main_swipe_refresh_layout);

Observable<State> swipe = RxSwipeRefreshLayout.refreshes(viewById)
        .map(o -> new IsLoading());

Observable<State> stateObservable = Observable.<State>just(new IsLoading())
        .mergeWith(swipe)
        .switchMap(state -> Observable.concat(
                Observable.just(new IsLoading()),
                Observable.<State>timer(500, TimeUnit.MILLISECONDS)
                        .map(aLong -> new LoadingResult(Collections.emptyList())
                        )
                )
        ).distinct();

stateObservable
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(
                state -> {
                    if (state instanceof IsLoading) {
                        Log.d("state", "isLoading");
                    } else if (state instanceof LoadingResult) {
                        Log.d("state", "loadingResult");
                        viewById.setRefreshing(false);
                    }
                });

Events

interface State { }

class IsLoading implements State { }

class LoadingResult implements State {
    private final List<String> result;
    public LoadingResult(List<String> result) {
        this.result = result;
    }
}

SwitchMap is like FlatMap but it will switch over to the new observable and discard incomming events from previouse observable.