Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Redux + (RxKotlin | RxSwift) = Awesome Native Mobile Apps — Reactive Programming— Part 4

DZone's Guide to

Redux + (RxKotlin | RxSwift) = Awesome Native Mobile Apps — Reactive Programming— Part 4

In this installment, we'll talk more about the benefits of ReactiveX programming and look at Observables and Subscribers.

· Mobile Zone ·
Free Resource

These libraries can be a boon for native app developers. See how they change the developer experience in the second part of this series.

Image title

We learned about store, state, and Reducer in part 3, and started to explore the need for Reactive programming. Let’s dive in…

Just to review, we have updated the authenticationState when the GitHub Login API is called, as we need to show the progress bar.

Refer to the video that shows the flow here.

We have achieved the above by using the traditional callbacks or listeners. Refer to this section to look over the code.

When the API returns success or failure or timeouts, we need to dismiss the progress bar.

Looking at the code, you may see callbacks and logic inside the callbacks. Welcome to the call back hell. Think about the unit testing this code, you need to spend a lot of energy there.

Is there any better way?

ReactiveX Programming

ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences.

In our example, we are calling GitHub API the results will be emitted asynchronously. We will be using Observables to capture the asynchronous results.

Why We Use Observables

The ReactiveX Observable model allows you to treat streams of asynchronous events with the same sort of simple, composable operations that you use for collections of data items like arrays.

We have transformed our old UserLogin Class to the below

class GHLoginTask (val mEmail: String,
                   val mPassword: String,
                   var githubService: GitHubApi = GitHubApiService()) {

    var mGHLoginSingle = Single.fromCallable {
        Pair(githubService.createToken(mEmail, mPassword), mainStore as Store<StateType>)
    }.map { it  ->
        val loginResultAction = LoginResultAction(it.first)
        loginResultAction.createdAt = SimpleDateFormat("MMM dd, yyy").format(it.first.createdAt)
        val ghLoginObservable = Pair(loginResultAction,it.second)
        ghLoginObservable
    }


    fun getGHLoginObservable(): Single<GHLoginObservableType> {
        return mGHLoginSingle
    }
}

You may appreciate that the above code frees you from tangled webs of callbacks, and thereby makes your code more readable and less prone to bugs. More importantly, it is unit testable.

To summarize the work of the above code, it

  • Calls the GitHub API
  • Create an RxSingle instead of other types, as it will emit only one event, either login success or failure
  • Once the result is available it converts to the LoginResultAction
  • Formats the date
  • Creates a Pair of LoginResultAction and mainStrore
  • Exposes the Pair as Observable

Now, let’s look at Subscriber.

typealias GHLoginObservableType = Pair<LoginResultAction,Store<StateType>>
fun getGHLoginSingleSubscriber(): SingleObserver<GHLoginObservableType> {
    return object : SingleObserver<Pair<LoginResultAction,Store<StateType>>> {

        override fun onSubscribe(d: Disposable) {}

        override fun onSuccess(value: GHLoginObservableType) {

            val (loginResultAction,store) = value
            if (loginResultAction.loginStatus == LoggedInState.notLoggedIn) {
                store.dispatch(LoginFailedAction(userName = loginResultAction.userName,
                        message = loginResultAction.message as String))

            } else {
                store.dispatch(LoginCompletedAction(loginResultAction))
                store.dispatch(LoggedInDataSaveAction(userName = loginResultAction.userName,
                        token = loginResultAction.token as String, loginStatus = LoggedInState.loggedIn))
            }

        }

        override fun onError(e: Throwable) {
            mainStore.dispatch(LoginFailedAction(userName = "",
                    message = "Internal Error"))
        }
    }
}

The subscriber or observer, based on the outcome of the Async action, it takes the next step.

If the API call results in an error, when the API is down, it calls onErrorfunctions, which will dispatch a call to start LoginFailedAction. If the API call succeeds, based on the login success outcome, it starts either LoginFailedAction or starting LoginCompletedAction and LoggedInDataSaveAction

Who Will Tie Up the Observable and the Subscriber?

Middleware. It observer the GHLoginObserver in the main thread and subscribe on the background thread with the subscription created earlier.

fun executeGitHubLoginTask(action: LoginAction, dispatch: DispatchFunction) {

    val ghLoginTask = GHLoginTask(action.userName,action.password)
    ghLoginTask.getGHLoginObserver().subscribeOn(Schedulers.io())
            ?.observeOn(AndroidSchedulers.mainThread())
            ?.subscribe(getGHLoginSingleSubscriber())

    dispatch(LoginStartedAction(action.userName))
}

What about unit testing?

In the next post, we will discuss unit testing the reducers, middleware

Links to the Series

Topics:
kotlin ,redux ,swift ,mobile ,mobile app development ,ios ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}