2 min read
Navigation & Routing
Last Updated - Platform 25.0 - SDK 20.0Navigation within the SDK is mostly handled via deeplinks, or in some cases directly.
Coordinators
Coordinators create and handle the navigation to (and of) a specific feature. They were added to hide the need to write code to inject routes; which can be messy and hard to find or understand.
Coordinators, within the Poq SDK, are more like feature specific navigators that handle navigation to that feature rather than coordinator for a specific instance of that feature. This ambiguity will be addressed in a future version of the SDK.
The PoqSDK automatically sets up features, with no code, by injecting hardcoded deeplink routes on app launch. Coordinators use the main navigator to handle navigation to and from the view controller.
Navigators
A Navigator
is used to handle deeplink resolution and presenting view controllers.
The main navigator is used via Container.shared.navigator()
which defaults to the PoqPlatform's NavigationHelper
.
Routes can be resolved using navigator.resolve("route")
which executes the handler of that route to navigate to that feature.
The PoqSDK supplies a convenience function for working with optional query params when creating a route.
let navigator = Container.shared.navigator()navigator.resolve(.route("something/\(id)", params: [ "title": titleOrNil]))
Use navigate(to:)
, present
, presentNavigationController(with:
or push
to handle direct navigation to a view controller from within a custom route's closure.
NavigationHelper
The NavigationHelper
is a legacy singleton mega-class that sets up all of the PoqPlatform's features by injecting their routes on app launch.
It also handles building the NavigationController
and resolving external deeplinks via the PoqPlatformModule
.
This will eventually be removed or improved into the PoqSDK.
MockNavigator
For testing, the PoqSDK provides the MockNavigator
to allow you to inject view controllers for routes that are simple matches using contains
.
Inject closures into the MockNavigator/viewControllers
dictionary to handle your test routes.
From v23 you can set the MockNavigator/resolveUsingTurnpike
to true to additionally resolve deeplinks via the SDK's defaults for UI testing.
Routes
Under the hood the PoqSDK stores a dictionary of deeplink route strings against their handling closures. Deeplink routes are set up in a similar way to containers; closures are injected for parameterised URL-based routes. When the route is resolved (navigated to) the closure is called with the passed path and query parameters.
Turnpike.map(route: "something/:id") { route in guard let id = route.routeParams["id"] else { return Log.error("Missing something id") } let viewController = SomethingViewController(id: id) NavigationHelper.shared.navigate(to: viewController, isModalInNavigation: route.isModal) .withTitle(route.queryParams?["title"])}
The PoqPlatform and PoqSDK use hardcoded deeplink routes for their features.
These are specified in the same files as the coordinators or as static variables within the NavigationHelper
.
Route Parameters
Dynamic URL component values (for example 132
in products/132
) can be pulled from deeplinks being handled by the route's closure.
Turnpike.map(route: "products/:product_id") { route in guard let productId = route.routeParameters?["product_id"] else { return }}
Query Parameters
Query parameters can be added to pass additional identifers or optional modifiers. They are retrieved in a similar way to route parameters.
Turnpike.map(route: "products/:product_id") { route in let variantId = route.queryParameters?["variant_id"]}
Additionally, we commonly use is_modal
and title
query parameters within the NavigationHelper
.
These parameters have their own convenient accessors.
let isModal = route.isModallet title = route.title
Context
Objects can be passed when resolving routes as additional context to the navigation. This can be useful to avoid unnecessary loads by forwarding data or even the state to the feature.
let navigator = Container.shared.navigator()navigator.resolve(deeplink, context: ["state": ProductState()])
These objects can be retrieved similarly to parameters, via the route.
Turnpike.map(route: "products/:product_id") { route in let state = route.context?["product"] as? ProductState}