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:
modalif a modal is presented,defaultotherwise. - Context is the value of the
contextproperty on the tapped link:modalordefault. No value defaults todefault. - Presentation is the value of the
presentationproperty 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 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/httpsurls to a SFSafariViewController in your app.BrowserTabRouteDecisionHandler: (Android Only) Routes all externalhttp/httpsurls 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()