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

Redux + (RxKotlin | RxSwift) = Awesome Native Mobile Apps — Part 2— Code Examples and More

DZone's Guide to

Redux + (RxKotlin | RxSwift) = Awesome Native Mobile Apps — Part 2— Code Examples and More

We continue this series on developer experience in mobile development with Redux by looking deeper into the ReSwift and ReKotlin libraries.

· 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 have learned about application state, how it is updated in a typical app, and how the redux changes the application state in a predictable way in part 1. Let’s continue our exploration of needs.

Unidirectional

If you notice, the data flow in the example is unidirectional; the view triggers the action, Reducer changes the state according to the action, and once the state is changed, the view is notified. Simple, isn't it?

Unidirectional data flow

Redux Libs

Now that we understand the concepts, let’s look at the choice of the libraries for redux in Android and iOS.

A super popular library in iOS is ReSwift. Android has reductor and ReKotlin. I have chosen ReKotlin as it gives routing capabilities as well and it is a port of the popular ReSwift.

Show Me the Code

Enough explanation! Let’s start with an example. Let’s create a login page for GitHub. The app is a simple app; it will let the user login and list the repositories of the user.

When the login button is pressed, it will show the progressBar overlay until the API returns a success or failure.

We are using Kotlin examples, but iOS developers should not feel disheartened; relevant Swift constructs are provided side-by-side.

If you want to see the complete code and run the app, check here.

Image title

Dependencies

Let’s declare the necessary libraries in the Gradle file of the module app.

ext {
    rekotlin_version = "0.1.0-SNAPSHOT"
    rxkotlin_version = "2.1.0"
    rxandroid = "2.0.1"
    rekotlin_router_version = "0.1.1"
    github_api_version = "1.89"
    }

    implementation "tw.geothings.rekotlin:rekotlin:$rekotlin_version"
    implementation "org.rekotlinrouter:rekotlin-router:$rekotlin_router_version"
    implementation "org.kohsuke:github-api:$github_api_version"
    implementation "io.reactivex.rxjava2:rxkotlin:$rxkotlin_version"
    implementation "io.reactivex.rxjava2:rxandroid:$rxandroid"

Sub-States

Let’s start defining the sub-states. We should not define the states according to the need of the page, but according to the need of the entity that is spread across the pages.

What are the examples of the entity in the current app? Authentication, Repository, etc.

In Kotlin, you should define a data class that extends StateType. In Swift, you should define a struct.

data class AuthenticationState(var loggedInState: LoggedInState,
                               var userName: String,
                               var isFetching: Boolean = false,
                               var isCompleted: Boolean = false,
                               var errorMessage: String? = null
                               ): StateType

LoggedInState is an enum class:

enum class LoggedInState {
    notLoggedIn,
    loggedIn
}

When you start defining the state, you may not have an exhaustive list of the members, you will update the state progressively as the functionality grows.

Remember that AuthenticationState is sub-state. Let’s define an application state that contains the AuthenticationState:

data class GitHubAppState( var authenticationState: AuthenticationState): StateType

Why do we need Global App State?

In a simple app, you may not need many sub-states, in a typical mobile app, you may have more than one entity, more sub-states. So we define GibHubAppState, which will have authenticationState and many other sub-states in future.

Store

Now we have figured out the states for the GitHub login page, let’s declare the store and call it mainStore

var mainStore = Store(state = null,
        reducer = ::appReducer,
        middleware = emptyList())

class AppController : Application() {

     override fun onCreate() {
        super.onCreate()
        val authenticationState = AuthenticationState(
          loggedInState = loginState.loginStatus,
                userName = loginState.userName)
        val state = GitHubAppState(authenticationState = authenticationState)
        mainStore = Store(state = state,
                reducer = ::appReducer,
                middleware = emptyList(),
                automaticallySkipRepeats = true)
      }
}

In iOS, you should do the similar steps in AppDelegate.swift.

Activity

Let’s create the Activity:

lass LoginActivity : Activity(), StoreSubscriber<AuthenticationState> {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        mEmailSignInButton.setOnClickListener {
            mainStore.dispatch(LoginAction(userName = mETEmail.text.toString(),
                    password = mETPassword.text.toString()))
        }
        mainStore.subscribe(this){
            it.select {
                it.authenticationState
            }.skipRepeats { oldState,newState ->
                oldState == newState
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        mainStore.unsubscribe(this)
    }

    override fun newState(state: AuthenticationState) {
        if (state.isFetching) {
            ViewHelper.showProgress(show = true,
                        view = mViewForm,
                        progressView = mViewProgress,
                        resources = resources)
            } else {
                ViewHelper.showProgress(show = false,
                        view = mViewForm,
                        progressView = mViewProgress,
                        resources = resources)
            }         
        }  
}

Activity and ViewControllers

In case of iOS, you may use ViewController in place of Activity,viewWillAppear function in place of onCreate function doing subscribe to the state and viewWillDisappear in place onDestroy function to unsubscribe.

A few subtle differences should be noted in the above code from the typical Android Activity:

  • Activity subscribes to the store in the function OnCreate
  • Activity itself unsubscribes to the store in the function onDestroy
  • Activity implements the interface StoreSubscriber and the function newState where the Activity responds to the changes to the state it subscribes. In the above example, Activity shows or hides the progressBar based on the AuthenticationState

You may notice that the Activity class is simple and clean, and does not have any code other than handling events and responding to the state changes. We are in process of achieving a goal — separation of concerns (SOC). You need not unit test Activity or ViewController; focus on other classes that are pure functions and easier to unit test.

Who will update the AuthenticationState?

Reducer; we will explain that in the next post.

In the Next Post…

We will look at more deeply into the Reducer, other Redux concepts, and code examples, where you will start appreciating how Redux changes the developer experience.

Topics:
redux ,rxswift ,rxandroid ,reactive architecture ,kotlin ,swift ,mobile ,mobile app development ,ios ,android

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}