Redux + (RxKotlin | RxSwift) == Awesome Native Mobile Apps — Router — Part 6
These libraries can be a boon for native app developers. See how they change the developer experience in part six of this series.
We have learned about the unit testing in Part 5, now let’s learn about the router.
Why we need the Router?
When there is a need to change the flow or introduce a new screen in the existing flow in an app, the changes are cumbersome. To understand that let’s work an example, say we have a simple app that has 4 screens
Now we have a need to introduce a new screen in the existing flow such as
Think about the changes we need to make.
Both in iOS and Android, we need to make the changes in the classes that trigger the new screens, typically those classes are Activity or Fragment in Android, ViewController in iOS. Also, we need to carry and forward the data which screen B sending to screen C when we introduce a new screen B1.
This needs a lot of code changes. Painstaking work.
Using Redux
If the App is built using the Redux, you may not need to pass the data between the screens, as the data can be stored as the state(s).
Want to learn more about Redux in Native apps — refer here
Now we achieved the greater level of separation of concerns (SOC) with help of Redux, let’s move the routing logic out of Activities/Fragments or ViewControllers, respecting the principle of SOC.
What are the libs we have to achieve the same?
We will use rekotlin-router or ReSwift-Router , depending upon the platforms.
Redux helps the Routing or Navigation
Redux Router takes advantage of the redux state to define the navigation. It uses a state called NavigationState
. Using the NavigationState
, the router externalizes the logic of navigation, moved the code out of Activities/Fragment or ViewControllers.
Android developers, the examples used in this post uses Activity for the sake of simplicity, you can achieve the same routing when the app uses Fragments as well, it will be explained later, Hang on…
How does Router work?
When the RoutingAction is dispatched by Activity/Fragment or ViewController, it is intercepted by the navigationReducer
provided by the Router.
The Router
uses the Routable
of the dispatching Activity or ViewController to derive the next possible Activity or ViewController. Routable apart from creating the Activity or ViewController, it creates the Routable for the next Activity or ViewController.
For example, when an ActivityA
dispatches a RoutingAction that has two routes ActivityA, ActivityB, Router creates an instance of ActivityB
and the routable for ActivityB
namely RoutableB
. Now ActivityB is the active Activity in your App
What happens when ActivityB decides to change the screen?
When ActivityB
dispatches a routing change, it will be the responsibility of RoutableB
to decide the next possible Activity.
Yes, each Activity or ViewController must have a Routable Class defined. Routable class separates the navigation logic from the Activity or ViewController class.
Show me the code
Let’s start with adding the dependencies as shown below
implementation 'org.rekotlinrouter:rekotlin-router:0.1.9'
iOS developers, refer here
Let’s update the state to implement the interfaceNavigationState
Once the navigation state is defined in the AppState, we need to create an object of Router
. TheRouter
takes care of the navigation, as long as the Routable
is defined for each Activity or ViewController.
In order for Routable to work, we need to create an instance of Router
and initialize the same.
Where should we do that?
Remember where we did create the Store
as a global variable in part 2 of this series?
Yes, we create them in AppController, let’s update the class to accommodate the Router
Router initialization takes three inputs
- store
- root routable
- A lambda expression that describes how to access the
navigationState
of the application state
What is Root Routable?
It is the first routable in the series of Routable's that takes care of the navigation. It is routable for the first Activity or the ViewController that is presented by the app.
Routable
Now let’s look at the Routable
interface
The Router
works with routes that are defined, similar to URLs, as a sequence of identifiers e.g. ["Home", "User", "UserDetail"]
. It uses Routable
to implement that interaction.
Each route segment is mapped to one responsible Routable
. The Routable
needs to be able to present a child, hide a child or replace a child with another child.
Now when we add the routes, for example from ["Home"]
to ["Home", "User"]
Router will execute the function pushRouteSegment
of Routable Home.
when the opposite happens, for example when the route changes from ["Home", "User"]
to ["Home"]
, Router will execute the function popRouteSegment
of Routable User.
The code pretty much does nothing, as Android takes care of the dismissing the current Activity. You may have some post dismissing work in completionHandler
In case of iOS, you dismiss the current ViewController
What happens when we want to change the routes for example from ["Home", "User"]
to ["Home", "Detail"]
?
The Router
will execute the function changeRouteSegment
of Routable User.
Want to see the complete example using Router, refer to AndroidExample using Activity here and iOS example here. You may like to clone the projects and run it.
Router using Fragments
You can dispatch the actions that invoke the Fragments
similar to Activity
.
The function SetRouteSpecificData
passes the data to Router to create the fragment.
We need the reference of Activity to create the Fragments. We will pass the Activity as WeakReference
We will use a class FragmentDataValue
to pass such data
Routable that creates Fragments
Let’s override the functions to create the Fragments, exactly similar to how we do it when using Router with Activity
Let’s use the WeakReference
of Activity to create the Fragment
It does the following
- get the current route from
navigationState
- From the current route, get the fragment’s
intentData
- With the
intentData
, get the activity from itsWeakReference
and Boolean value whether to add to the stack. - Call the function
addFragment
, helper method to create the fragment.
Want to see the example of Android using Fragments, refer here.
Conclusion
I hope you are able to appreciate the fact, the routing logic is externalized completely using the Redux and Redux-Router, achieving the SOC.