Routing with ReactiveReSwift and RxSwift
ReRouter is library that helps you handle the navigation in the app with ReactiveReSwift and RxSwift. ReRouter provides a Path
type that incapsulated the current navigation of the App and whenever the path is changed, the router will perform all the changes automatically.
If you are looking for concrete understanding on how to use this library, there is a demo target in the project where you can find some examples on how to create an app with simple navigation flow.
There are 3 main components in Router:
The general flow is the following: You set the new Path
to the app state, the Router listens to the Path
changes, generates the difference between two Paths
and then applys the sequence of navigation side effect, by calling the respective functions in corresponding Coordinators
The actual navigation is done in coordinators. Coordinator is not a new concept and here is a great introduction to it. In ReReouter Coordinators provide 2 things: enum Key
which you can think of as a Path Item and a method of how to perform the navigation for a give Key
.
Here's an example of Coordinator
:
final class Coordinator: CoordinatorType {
enum Key: PathKey {
case presentOther, pushOther
}
let navigationController: UINavigationController // Initialize navigation controller from storyboard
func item(for key: Key) -> NavigationItem {
switch key {
case .presentOther:
let otherController: UIViewController // Initialize other controller
return NavigationItem(self, otherController, push: { (animated, source, target, completion) in
source.navigationController.present(target, animated: animated, completion: completion)
}, pop: { (animated, source, target, completion) in
target.dismiss(animated: animated, completion: completion)
})
case .pushOther:
let otherController: UIViewController // Initialize other controller
return NavigationItem(self, otherController, push: { (animated, source, target, completion) in
source.navigationController.pushViewController(target, animated: animated)
completion()
}, pop: { (animated, source, target, completion) in
source.navigationController.popViewController(animated: animated)
completion()
})
}
}
}
As you can see, the in method func item(for key: Key) -> NavigationItem
we have to return NavigationItem
. NavigationItem is a Type that incapsulates the transition to the next coordinator or view controller.
NavigationItem
contains of 4 things:
Source
is the coordinator that initiates the transition. It's alwaysself
.Target
- this is the next coordinator orUIViewController
that needs to be presented. In general, anything that conforms to protocolRoutable
can be the targetPush
function. Here we actually perform the transition toTarget
. Animated property indicates whether the transition should be animated. We have to call the completion once the transition is done.Pop
function. Here we perform the transition back toSource
fromTarget
. Animated property indicates whether the transition should be animated. We have to call the completion once the transition is done.
The actual state of the navigation is incapsulated in a Path
type. You can think of the Path
as the URL in the web, although it is more powerful, as it not represented as a string. Path is sequence of Key
that you defined in the Coordinators.
Here is an example of the Path
:
Path<AppCoordinator.Key>(.routeList).push(RouteCoordinator.Key.addRoute)
Here is an example of the State:
struct State {
var counter = 0
var path = Path<AppCoordinator.Key>(.logIn)
}
The state needs to contain the variable with the path. The router will listen to the changes of the Path
and perform the side effects.
The actual navigation is done by NavigationRouter
. You need to initialiaze the NavigationRouter
with the initial Coordinator
, the store that contains your state and the function of how to access the path from your state. You can keep in AppDelegate
, for example.
Here's the example of your AppDelegate
:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let router = NavigationRouter(AppCoordinator(), store: mainStore, { $0.path })
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
router.setupUpdate()
return true
}
}
That's it! You are now ready to use ReRouter