Navigation
Navigating between screens is a core concept of building Hotwire Native apps. By default, all screens will be pushed onto the main navigation stack with animation. You can customize the navigation behavior by providing path configuration rules or manually routing in Swift or Kotlin.
﹟ Routing
Set context
or presentation
to a path configuration rule to apply the logic in the following table.
- State describes what state the app is currently in:
modal
if a modal is presented,default
otherwise. - Context is the value of the
context
property on the tapped link:modal
ordefault
. No value defaults todefault
. - Presentation is the value of the
presentation
property on the tapped link:replace
,pop
,refresh
,clear_all
,replace_root
,none
, ordefault
. No value defaults todefault
.
State | Context | Presentation | Behavior |
---|---|---|---|
default |
default |
default |
Push on main stack (or) Replace if visiting same page (or) Pop then visit if previous screen is same URL |
default |
default |
replace |
Replace screen on main stack |
default |
modal |
default |
Present a modal with only this screen |
default |
modal |
replace |
Present a modal with only this screen |
modal |
default |
default |
Dismiss then Push on main stack |
modal |
default |
replace |
Dismiss then Replace on main stack |
modal |
modal |
default |
Push on the modal stack |
modal |
modal |
replace |
Replace screen on modal stack |
default |
(any) | pop |
Pop screen off main stack |
default |
(any) | refresh |
Pop on main stack then |
modal |
(any) | pop |
Pop screen off modal stack (or) Dismiss if one modal screen |
modal |
(any) | refresh |
Pop screen off modal stack then Refresh last screen on modal stack (or) Dismiss if one modal screen then Refresh last screen on main stack |
(any) | (any) | clear_all |
Dismiss if modal screen then Pop to root then Refresh root screen on main stack |
(any) | (any) | replace_root |
Dismiss if modal screen then Pop to root then Replace root screen on main stack |
(any) | (any) | none |
Nothing |
﹟ Server-Driven Routing in Rails
If you’re using Ruby on Rails, the turbo-rails gem provides the following additional historical location routes. Use these to manipulate the navigation stack for Hotwire Native apps, falling back to redirecting elsewhere.
recede_or_redirect_to(url, **options)
- First, pops any modal screen (if present) off the navigation stack. Then, pops the visible screen off of the navigation stack.refresh_or_redirect_to(url, **options)
- First, pops any modal screen (if present) off the navigation stack. Then, reloads the visible screen by performing a new web request and invalidating the cache.resume_or_redirect_to(url **options)
- Pops any modal screen (if present) off the navigation stack. No further action is taken.
The iOS and Android frameworks (starting in version 1.2.0
) automatically support these these historical location urls.
﹟ Route Decision Handlers
By default, all external urls outside of your app’s domain open externally. The specific behavior can be customized, though. Out-of-the-box, Hotwire Native registers these route decision handlers to control how urls are routed:
AppNavigationRouteDecisionHandler
: Routes all internal urls on your app’s domain through your app.SafariViewControllerRouteDecisionHandler
: (iOS Only) Routes all externalhttp
/https
urls to a SFSafariViewController in your app.BrowserTabRouteDecisionHandler
: (Android Only) Routes all externalhttp
/https
urls to a Custom Tab in your app.SystemNavigationRouteDecisionHandler
: Routes all remaining external urls (such assms:
ormailto:
) through device’s system navigation.
If you’d like to customize this behavior you can subclass the RouteDecisionHandler
class in your app to provide your own implementation(s). Register your app’s decision handlers in order of importance. To decide how a url should be routed, the registered RouteDecisionHandler
instances are called in order. When a matching RouteDecisionHandler
is found for a given url, its handle()
function is called and no other RouteDecisionHandler
instances will be subsequently called.
Example for iOS:
Hotwire.registerRouteDecisionHandlers([
AppNavigationRouteDecisionHandler(),
MyCustomExternalRouteDecisionHandler()
])
Example for Android:
Hotwire.registerRouteDecisionHandlers(
AppNavigationRouteDecisionHandler(),
MyCustomExternalRouteDecisionHandler()
)
﹟ Manual Navigation
Navigator
can be used to navigate from a native screen to another native screen or back to a web context.
﹟ iOS
let rootURL = URL(string: "...")!
let navigator = Navigator()
// Visit a new page.
navigator.route(rootURL.appending(path: "foo"))
// Pop the top controller off the stack.
navigator.pop()
// Pop the entire stack of controllers.
navigator.clearAll()
Disable the animation via the optional animated
parameter.
navigator.route(rootURL.appending(path: "foo"), animated: false)
navigator.pop(animated: false)
navigator.clearAll(animated: false)
﹟ Android
Inside of a HotwireActivity
class:
val location = "https://..."
val navigator = delegate.currentNavigator
// Visit a new page.
navigator?.route("$location/foo")
// Pop the backstack to the previous destination.
navigator?.pop()
// Clear the navigation backstack to the start destination.
navigator?.clearAll()
Inside of a HotwireFragment
class:
val location = "https://..."
// Visit a new page.
navigator.route("$location/foo")
// Pop the backstack to the previous destination.
navigator.pop()
// Clear the navigation backstack to the start destination.
navigator.clearAll()