From 2efa67a14894188dc4eb55c6b4a97226b6a21104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Thu, 29 Oct 2026 21:55:54 +0100 Subject: [PATCH] docs(navigation): guides for route options, Map Matching, free drive, guidance controls and styling Hand-written pages for features released in @rnmapbox/navigation 0.1.1. Also corrects the day/night notes on NavigationView and RNNavigationView, and lists their style props. --- docs/navigation/free-drive.md | 62 +++++++++++++++++++++ docs/navigation/guidance-controls.md | 71 ++++++++++++++++++++++++ docs/navigation/map-matching.md | 51 +++++++++++++++++ docs/navigation/navigation-view.md | 6 +- docs/navigation/overview.md | 4 +- docs/navigation/rn-navigation-view.md | 6 +- docs/navigation/route-options.md | 79 +++++++++++++++++++++++++++ docs/navigation/styling.md | 71 ++++++++++++++++++++++++ docs/navigation/troubleshooting.md | 2 +- 9 files changed, 345 insertions(+), 7 deletions(-) create mode 100644 docs/navigation/free-drive.md create mode 100644 docs/navigation/guidance-controls.md create mode 100644 docs/navigation/map-matching.md create mode 100644 docs/navigation/route-options.md create mode 100644 docs/navigation/styling.md diff --git a/docs/navigation/free-drive.md b/docs/navigation/free-drive.md new file mode 100644 index 00000000..e5db653d --- /dev/null +++ b/docs/navigation/free-drive.md @@ -0,0 +1,62 @@ +--- +id: free-drive +sidebar_position: 6 +title: "Free drive: road name and speed limit without a route" +sidebar_label: Free drive +description: "Track the device on the road network without a route with @rnmapbox/navigation: road name, speed limit, the navigation puck and the session state." +--- + +Outside guidance the navigation session is idle: no location events and no puck on navigation maps. `Navigation.startFreeDrive()` matches the device's location to the road network without a route, so `addLocationListener` reports the road name and speed limit, and navigation maps show the puck. `Navigation.stopNavigation()` ends it. + +```tsx +import { useEffect, useState } from 'react'; +import Navigation, { type LocationEvent } from '@rnmapbox/navigation'; + +export function useFreeDrive() { + const [location, setLocation] = useState(); + + useEffect(() => { + const subscription = Navigation.addLocationListener(setLocation); + Navigation.startFreeDrive().catch((error) => { + // error.code === 'location_permission': ask for the permission first + console.warn(error); + }); + return () => { + subscription.remove(); + Navigation.stopNavigation(); + }; + }, []); + + return location; // location?.roadName, location?.speedLimit?.speed, location?.speedLimit?.unit +} +``` + +- Mapbox bills free drive trips like guidance trips, see [Navigation SDK pricing](https://www.mapbox.com/pricing#navigation). Nothing in this package starts free drive on its own, CarPlay included. It runs from `startFreeDrive` until `stopNavigation` or a JS reload. +- Without location permission, `startFreeDrive` rejects with `code: 'location_permission'`, see [Location permission](install.md#location-permission). +- During guidance, `startFreeDrive()` ends the guidance and keeps the session in free drive. +- `` and `` started from free drive return to it when they unmount or the user ends the trip. + +`speedLimit` is missing where the road has no known limit. Its `sign` is `'mutcd'` (US-style rectangle) or `'vienna'` (red circle), see [`LocationEvent`](api/Events/LocationEvent.md). + +## Simulated location + +`startFreeDrive({ simulatedLocation: [longitude, latitude] })` parks a simulated device at that position instead of using the device's location, and needs no location permission. After simulated guidance, `startFreeDrive()` keeps the simulated device where guidance stopped. + +## Android background tracking + +On Android, free drive runs without the Navigation SDK's foreground service, so Android throttles its location updates while the app is in the background. `startFreeDrive({ foregroundService: true })` keeps tracking in the background with the SDK's notification, which also keeps the billed trip going while the app isn't visible, until `stopNavigation`. Live guidance always uses the foreground service. iOS and simulated free drive ignore the option. + +## Session state + +`Navigation.getSessionState()` returns `'idle'`, `'freeDrive'` or `'activeGuidance'`, and `addSessionStateListener` reports changes, whether they come from JS, `` or the car screen. + +```tsx +const [sessionState, setSessionState] = useState(Navigation.getSessionState); + +useEffect(() => { + const subscription = Navigation.addSessionStateListener(setSessionState); + return () => subscription.remove(); +}, []); +``` + +On iOS they follow the SDK's session; on Android they follow this package's calls. diff --git a/docs/navigation/guidance-controls.md b/docs/navigation/guidance-controls.md new file mode 100644 index 00000000..e26aef38 --- /dev/null +++ b/docs/navigation/guidance-controls.md @@ -0,0 +1,71 @@ +--- +id: guidance-controls +sidebar_position: 7 +title: "Guidance controls: stops, simulation speed, voice and screen" +sidebar_label: Guidance controls +description: "Control a running @rnmapbox/navigation guidance session: advance between stops, simulation speed, voice volume and mute, and keeping the screen on." +--- + +These settings apply to the app's single navigation session, so they also affect `` and ``. + +## Multi-stop routes + +Each non-silent waypoint after the origin ends a leg, see [Waypoints](route-options.md#waypoints). When guidance arrives at one, `addArriveListener` reports `{ waypointIndex, isFinalDestination }` and guidance moves on to the next leg by itself. + +To wait at each stop, e.g. until a delivery is confirmed, turn that off and advance yourself: + +```tsx +import { Alert } from 'react-native'; +import Navigation from '@rnmapbox/navigation'; + +Navigation.setAutoAdvanceLegs(false); + +const subscription = Navigation.addArriveListener(({ isFinalDestination }) => { + if (!isFinalDestination) { + Alert.alert('Delivered?', undefined, [ + { text: 'Next stop', onPress: () => Navigation.advanceToNextLeg() }, + ]); + } +}); +``` + +- With auto-advance off, guidance stays at the waypoint until `advanceToNextLeg()`, simulated guidance included. Turning it back on while guidance waits doesn't move on; call `advanceToNextLeg()`. +- `advanceToNextLeg()` also skips a stop. Away from the waypoint, guidance reroutes from where it is; simulated guidance jumps to the start of the next leg and reroutes again. +- It does nothing on the last leg or without active guidance. + +## Simulation speed + +`Navigation.setSimulationSpeed(multiplier)` drives simulated guidance faster or slower than the route's expected speeds, e.g. `2` for twice as fast. The default is `1`. It applies to the running simulation and later ones, and throws unless `multiplier` is positive. + +```tsx +Navigation.setSimulationSpeed(2); +await Navigation.startActiveGuidance(route, { simulate: true }); +``` + +- Simulated locations keep reporting the route's speed, not multiplied. +- Higher speeds can make guidance go off route at sharp turns and reroute. This was seen at U-turns from 4× on Android and at 8× on iOS. +- On Android the simulation doesn't follow reroutes, so after one it can stop short of the destination. + +## Voice + +The SDK speaks voice instructions during any guidance. + +| Function | Description | +| ------------------------ | ----------------------------------------------------------------------------------- | +| `setVoiceMuted(muted)` | Mutes or unmutes them. ``'s mute button toggles the same setting. | +| `isVoiceMuted()` | Whether they're muted, by `setVoiceMuted` or the mute button. | +| `setVoiceVolume(volume)` | Volume from 0 to 1, relative to the device's media volume, clamped to that range. Independent of mute. Throws for `NaN`. | + +On iOS the volume doesn't apply to instructions spoken by the system voice, which the SDK falls back to when the Mapbox voice is unavailable, e.g. offline. + +For your own text to speech, mute the SDK and subscribe to `addVoiceInstructionListener`, which is called whether or not the SDK speaks. + +## Keep screen on + +Active guidance, ``'s included, keeps the screen on. When guidance stops, the app's own idle timer (iOS) or keep screen on (Android) setting is restored. If the app manages that itself, turn it off: + +```tsx +Navigation.setKeepScreenOn(false); +``` + +See [`Navigation`](api/Core/Navigation.md) in the API reference. diff --git a/docs/navigation/map-matching.md b/docs/navigation/map-matching.md new file mode 100644 index 00000000..5fffdb8b --- /dev/null +++ b/docs/navigation/map-matching.md @@ -0,0 +1,51 @@ +--- +id: map-matching +sidebar_position: 5 +title: "Guide along your own path with Map Matching" +sidebar_label: Map Matching +description: "Turn a list of coordinates, such as a tour or a delivery run, into a navigable route with the Mapbox Map Matching API and @rnmapbox/navigation." +--- + +`Navigation.requestMatchedRoutes` snaps a path the app defines, such as a tour or a delivery run, to roads with the [Mapbox Map Matching API](https://docs.mapbox.com/api/navigation/map-matching/), and resolves with a route along it. Use it when the route has to follow your path, rather than the fastest route between the waypoints that `requestRoutes` returns. + +```tsx +import Navigation, { type Position } from '@rnmapbox/navigation'; + +const TOUR: Position[] = [ + [-122.39726, 37.79205], + [-122.39612, 37.79310], + [-122.39480, 37.79420], + [-122.39351, 37.79554], +]; + +const { main } = await Navigation.requestMatchedRoutes({ + coordinates: TOUR, + waypointIndices: [0, 2, TOUR.length - 1], +}); +await Navigation.startActiveGuidance(main, { simulate: true }); +``` + +The route works with `previewRoute` and `startActiveGuidance` like one from `requestRoutes`, and holds native memory the same way: `release()` it when you're done. `alternatives` is always empty. + +## Options + +| Option | Description | +| ----------------- | ----------------------------------------------------------------------------------------------- | +| `coordinates` | The path, 2 to 100 positions in order. Each is snapped to the nearest road within its radius, so a sparse trace works. | +| `profile` | `'driving-traffic'` (default), `'driving'`, `'walking'` or `'cycling'`. | +| `waypointIndices` | Indices of the `coordinates` that are waypoints: the route is split into legs there, and guidance announces the arrival. Ascending, including the first and last index. Defaults to the first and last coordinate. | +| `radiuses` | Meters each coordinate may be away from the road, one per coordinate, 0 to 50. The API defaults to 5. | +| `language` | Language of the instructions, e.g. `en` or `de-DE`. | +| `units` | `'metric'` or `'imperial'`, for the distances in voice instructions. | + +It rejects without sending a request if the options break these limits, and with `code: 'no_single_match'` if the API can only match the coordinates as several disconnected pieces, e.g. across a gap with no road. Add coordinates in the gap, or raise `radiuses` there. + +## Off route + +Neither SDK reroutes a matched route, whatever `setRerouteEnabled` says. When the user leaves it, `addOffRouteListener` reports `true` and guidance waits for them to return to the route. + +## Platform notes + +On Android this uses the SDK's `requestMapMatching`, which the Navigation SDK marks as experimental, so its behavior may change in minor releases. + +See [`MatchedRouteRequestOptions`](api/Routes/MatchedRouteRequestOptions.md) in the API reference. diff --git a/docs/navigation/navigation-view.md b/docs/navigation/navigation-view.md index 05d4a104..69f93347 100644 --- a/docs/navigation/navigation-view.md +++ b/docs/navigation/navigation-view.md @@ -1,6 +1,6 @@ --- id: navigation-view -sidebar_position: 4 +sidebar_position: 9 title: "NavigationView: drop-in turn-by-turn navigation UI" sidebar_label: NavigationView description: "The Mapbox Navigation SDK's full-screen turn-by-turn UI for React Native on iOS and Android, with maneuver banner, lanes, speed limit and trip progress." @@ -38,6 +38,8 @@ See [`NavigationViewProps`](api/Components/NavigationViewProps.md) in the API re | `waypoints` | `Waypoint[]` | Used without `route`: the view requests a route through them, origin first, and releases it on unmount. | | `simulate` | `boolean` | Drives the route with simulated locations. Read when guidance starts. Defaults to `false`. | | `mute` | `boolean` | Mutes voice instructions. The view's own mute button also toggles them. Defaults to `false`. | +| `styleURL` | `string` | Map style by day. Defaults to Mapbox Standard. See [Styling](styling.md#map-styles). | +| `nightStyleURL` | `string` | Map style while the system is in dark mode. Defaults to `styleURL`, or without it to Standard's night preset. | | `style` | `StyleProp` | | | `testID` | `string` | | | `onRouteProgress` | `(event: RouteProgressEvent) => void` | Same event as `addRouteProgressListener`. | @@ -54,4 +56,4 @@ See [`NavigationViewProps`](api/Components/NavigationViewProps.md) in the API re - `addRouteProgressListener` and `addCameraStateListener` also report its guidance. - Show one `NavigationView` at a time. On iOS it's embedded as a child of the nearest view controller. -Android `` has no end-of-route or feedback UI, no day/night styles, no road name label, and doesn't show reroutes as such. For the same UI on both platforms, see [``](rn-navigation-view.md). +Android `` has no end-of-route or feedback UI, no night colors for its UI (only the map switches), no road name label, and doesn't show reroutes as such. For the same UI on both platforms, see [``](rn-navigation-view.md). diff --git a/docs/navigation/overview.md b/docs/navigation/overview.md index 192b82b9..833bc294 100644 --- a/docs/navigation/overview.md +++ b/docs/navigation/overview.md @@ -84,7 +84,7 @@ There are three ways to build a navigation screen. They all share the app's sing | --- | --- | --- | --- | | What you get | A navigation map with the route line, puck and camera. All UI is yours. | A complete UI in React Native: maneuver banner, lanes, speed limit, trip progress, buttons. | The Navigation SDK's own UI: `NavigationViewController` on iOS, the SDK UI components on Android. | | Customizability | Full. Any `@rnmapbox/maps` layers, sources and annotations, any React Native UI. | High. Change the map style, add map children, or copy its source as a starting point. | Low. Props for the route, simulation and mute; the look is the SDK's. | -| Platform parity | Same on both platforms, since the UI is yours. | Same on both platforms. | Differs: Android has no end-of-route or feedback UI, no day/night styles and no road name label. | +| Platform parity | Same on both platforms, since the UI is yours. | Same on both platforms. | Differs: Android has no end-of-route or feedback UI, no night colors for its UI and no road name label. | | Effort | Most: you build banners, buttons and arrival handling from the [events](api/index.md). | Little: drop it in, pass `waypoints` or a `route`. | Least, for the full SDK experience. | Start with `` if you want a ready UI that matches the rest of your React Native app, `` if you want the SDK's UI as is, and the headless API when navigation is one part of a custom map screen. @@ -92,7 +92,7 @@ Start with `` if you want a ready UI that matches the rest of ## Known limitations - No explicit offline routing API. Requests use the SDK's default hybrid router, which only falls back to onboard routing with routing tiles already on the device. -- Android `` has no end-of-route or feedback UI, no day/night styles, no road name label, and doesn't show reroutes as such. +- Android `` has no end-of-route or feedback UI, no night colors for its UI (only the map switches), no road name label, and doesn't show reroutes as such. - `` draws road shields from the Directions API's shield images, not the Mapbox designed shields the native banners use. - The Expo config plugin is only tested with Expo SDK 58. - `@rnmapbox/maps` `LocationPuck` and `UserLocation` don't work on navigation maps: the navigation puck replaces the map's location provider. diff --git a/docs/navigation/rn-navigation-view.md b/docs/navigation/rn-navigation-view.md index ce535b5a..d9ba4f8e 100644 --- a/docs/navigation/rn-navigation-view.md +++ b/docs/navigation/rn-navigation-view.md @@ -1,6 +1,6 @@ --- id: rn-navigation-view -sidebar_position: 5 +sidebar_position: 10 title: "RNNavigationView: turn-by-turn navigation UI in React Native" sidebar_label: RNNavigationView description: "A customizable turn-by-turn navigation UI written in React Native on top of the @rnmapbox/navigation headless API, the same on iOS and Android." @@ -44,8 +44,10 @@ It takes the same props as [``](navigation-view.md#props) (`rout | Prop | Type | Description | | -------------- | ------------------------------- | ------------------------------------------------------------------------ | | `distanceUnit` | `'metric' \| 'imperial'` | Unit of the distances it shows, and of the voice instructions of a route it requests through `waypoints`. Defaults to `Navigation.getDefaultDistanceUnit()`, from the device's region and measurement settings. | -| `appearance` | `'auto' \| 'day' \| 'night'` | Colors of the UI and the Standard style's light preset. `'auto'` (default) switches at sunrise and sunset at the user's location. | +| `appearance` | `'auto' \| 'day' \| 'night'` | Colors of the UI and the Standard style's light preset. `'auto'` (default) is night while the system is in dark mode. | | `styleURL` | `string` | Map style. Defaults to Mapbox Standard. With another style, `appearance` only changes the UI. | +| `nightStyleURL` | `string` | Map style while `appearance` is night. Defaults to `styleURL`. | +| `routeLineStyle` | `RouteLineStyle` | Route line colors, see [Styling](styling.md#route-line-colors). | | `children` | `ReactNode` | Rendered inside its `MapView`, e.g. extra sources and layers. | See [`RNNavigationViewProps`](api/Components/RNNavigationViewProps.md) in the API reference. diff --git a/docs/navigation/route-options.md b/docs/navigation/route-options.md new file mode 100644 index 00000000..95458e44 --- /dev/null +++ b/docs/navigation/route-options.md @@ -0,0 +1,79 @@ +--- +id: route-options +sidebar_position: 4 +title: "Route options: waypoints, vehicle limits and departure time" +sidebar_label: Route options +description: "Request routes with @rnmapbox/navigation: profiles, silent and curbside waypoints, roads and points to avoid, vehicle height, width and weight, and departure or arrival time." +--- + +`Navigation.requestRoutes` takes the waypoints and the options below, and sends them to the Mapbox Directions API: + +```tsx +import Navigation from '@rnmapbox/navigation'; + +const { main, alternatives } = await Navigation.requestRoutes({ + waypoints: [ + [-122.39726, 37.79205], + { coordinate: [-122.40014, 37.79405], silent: true }, + { coordinate: [-122.39351, 37.79554], name: 'Ferry Building', approach: 'curb' }, + ], + profile: 'driving', + alternatives: true, + exclude: ['toll', 'ferry'], + maxHeight: 3.2, + departAt: '2026-11-02T08:30', + language: 'de-DE', + units: 'metric', +}); +``` + +Release the routes you don't keep, see [Routes](getting-started.md#routes). + +## Waypoints + +A waypoint is a `[longitude, latitude]` position, or an object with `coordinate` and these options: + +| Option | Description | +| ---------- | --------------------------------------------------------------------------------------------------- | +| `name` | Shown by the navigation UI, e.g. on arrival. | +| `silent` | Pass through without stopping. A silent waypoint doesn't end a leg, so there's no arrival at it. The origin and the destination can't be silent. | +| `approach` | `'curb'` arrives on the driving side of the road, e.g. the right in right-hand traffic. Defaults to `'unrestricted'`, either side. | + +Each non-silent waypoint after the origin ends a leg. See [Multi-stop routes](guidance-controls.md#multi-stop-routes) for what guidance does when it arrives. + +## Options + +| Option | Profiles | Description | +| --------------- | -------------------------------- | --------------------------------------------------------------------------- | +| `profile` | | `'driving-traffic'` (default), `'driving'`, `'walking'` or `'cycling'`. | +| `alternatives` | all | Also return alternative routes. Defaults to `false`. | +| `exclude` | all | Road classes to avoid, e.g. `['toll', 'ferry']`. | +| `excludePoints` | `driving`, `driving-traffic` | Up to 50 positions. Each is snapped to the nearest road, and the route avoids that road segment where it can. | +| `maxHeight` | `driving`, `driving-traffic` | Vehicle height in meters, 0 to 10. The API assumes 1.6 if unset. | +| `maxWidth` | `driving`, `driving-traffic` | Vehicle width in meters, 0 to 10. The API assumes 1.9 if unset. | +| `maxWeight` | `driving`, `driving-traffic` | Vehicle weight in metric tons, 0 to 100. The API assumes 2.5 if unset. | +| `departAt` | `driving`, `driving-traffic` | `YYYY-MM-DDThh:mm` in the local time at the origin. The route uses predicted traffic and timed restrictions at that time. | +| `arriveBy` | `driving` | `YYYY-MM-DDThh:mm` in the local time at the destination. | +| `language` | all | Language of the instructions, e.g. `en` or `de-DE`. | +| `units` | all | `'metric'` or `'imperial'`, for the distances in voice instructions. | + +`requestRoutes` checks the options before sending the request, because the Directions API or the SDK would ignore some of them silently. It rejects with `code: 'invalid_options'` for fewer than two waypoints, a silent origin or destination, an option the profile doesn't support, a value out of range, or a time that isn't `YYYY-MM-DDThh:mm`. + +```tsx +try { + await Navigation.requestRoutes({ waypoints, profile: 'walking', maxHeight: 3 }); +} catch (error) { + // error.code === 'invalid_options': maxHeight needs the driving or driving-traffic profile +} +``` + +## Rerouting + +When the user leaves a route from `requestRoutes`, guidance requests a new one, and `addRouteChangeListener` reports it with `reason: 'reroute'`. `Navigation.setRerouteEnabled(false)` turns that off. + +- Android applies it at once, also during guidance. +- iOS reads it only when the navigation session is created, on the first route request or guidance start. Call it before those, e.g. next to `setAccessToken`. A later call that changes the value has no effect on iOS and logs a warning. + +Routes from [Map Matching](map-matching.md) are never rerouted. + +See [`RouteRequestOptions`](api/Routes/RouteRequestOptions.md) and [`Waypoint`](api/Routes/Waypoint.md) in the API reference. diff --git a/docs/navigation/styling.md b/docs/navigation/styling.md new file mode 100644 index 00000000..2ada907d --- /dev/null +++ b/docs/navigation/styling.md @@ -0,0 +1,71 @@ +--- +id: styling +sidebar_position: 8 +title: "Styling navigation maps: route line colors and day/night styles" +sidebar_label: Styling +description: "Style @rnmapbox/navigation maps: route line, casing, alternative and traveled colors, day and night map styles for NavigationView, and RNNavigationView appearance." +--- + +## Route line colors + +`` sets the colors of the route lines the Navigation SDK draws on the map it's a child of. Unset colors keep the SDK's. + +```tsx +import { MapView } from '@rnmapbox/maps'; +import { NAVIGATION_MAP_VIEW_IMPL, NavigationViewport } from '@rnmapbox/navigation'; + + + + +``` + +| Color | Description | +| ------------------ | ------------------------------------------------------------------------------------------- | +| `color` | Main route where traffic is low or unknown. Moderate, heavy and severe traffic keep the SDK's colors. | +| `casingColor` | Border of the main route. | +| `alternativeColor` | Alternative routes where traffic is low or unknown. | +| `traveledColor` | Part of the main route already traveled during guidance. Defaults to transparent, so that part disappears. | + +The colors can change during guidance. On iOS the SDK then redraws the route, and the traveled part shows in the route color until the next location update. + +`` takes the same `routeLineStyle` prop. `` doesn't: on iOS its colors come from the SDK's own styles. + +## Map styles + +Navigation maps are `@rnmapbox/maps` maps, so set their style with `MapView`'s `styleURL` and add your own sources, layers and annotations as children. + +`` and `` take `styleURL` and `nightStyleURL`: + +```tsx + +``` + +| Prop | `` | `` | +| --------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------- | +| `styleURL` | Map style by day. Defaults to Mapbox Standard. | Map style. Defaults to Mapbox Standard. | +| `nightStyleURL` | Map style while the system is in dark mode. Defaults to `styleURL` if set, else to Standard with its night light preset. | Map style while `appearance` is night. Defaults to `styleURL`. | + +On Android, `` switches the map style at night, but its banner and bottom bar stay in day colors. + +## RNNavigationView appearance + +`appearance` sets the colors of ``'s UI and the light preset of the Standard style: + +- `'auto'` (default) is `'night'` while the system is in dark mode, like ``; +- `'day'` or `'night'` fixes it. + +With a `styleURL` other than Standard, `appearance` only changes the UI; pass a `nightStyleURL` to change the map too. + +See [`RouteLineStyle`](api/Components/RouteLineStyle.md), [`NavigationViewProps`](api/Components/NavigationViewProps.md) and [`RNNavigationViewProps`](api/Components/RNNavigationViewProps.md) in the API reference. diff --git a/docs/navigation/troubleshooting.md b/docs/navigation/troubleshooting.md index 616bac3f..8efde5d4 100644 --- a/docs/navigation/troubleshooting.md +++ b/docs/navigation/troubleshooting.md @@ -1,6 +1,6 @@ --- id: troubleshooting -sidebar_position: 6 +sidebar_position: 11 title: "Troubleshooting @rnmapbox/navigation" sidebar_label: Troubleshooting description: "Fixes for common @rnmapbox/navigation build problems on iOS and Android, such as duplicate MapboxMaps symbols and Maps SDK version conflicts."