Skip to content

Resupply fork: Mapbox Navigation SDK v3 (vendored binaries) + customization & truck-routing props#50

Open
stefanpavlovic-tech wants to merge 36 commits into
pawan-pk:mainfrom
stefanpavlovic-tech:feat/nav-v3-spm
Open

Resupply fork: Mapbox Navigation SDK v3 (vendored binaries) + customization & truck-routing props#50
stefanpavlovic-tech wants to merge 36 commits into
pawan-pk:mainfrom
stefanpavlovic-tech:feat/nav-v3-spm

Conversation

@stefanpavlovic-tech

@stefanpavlovic-tech stefanpavlovic-tech commented Jun 10, 2026

Copy link
Copy Markdown

Summary

Re-platforms this module onto Mapbox Navigation SDK v3 (3.20.1) on both iOS and Android, fixes the New-Architecture (Fabric) integration, and adds the customization and truck-routing props the Resupply driver app needs. Upstream targets Nav SDK v2
(iOS CocoaPods MapboxNavigation ~> 2.18.1) and v3.0.2 on Android; this branch brings both platforms to a single, interlocked v3.20.1 that coexists with @rnmapbox/maps.

Base: fork main (b43c134). Branch: feat/nav-v3-spm.


iOS — Nav SDK v3 via vendored binary xcframeworks

v3 has no CocoaPods pod, and consuming the SPM package from a CocoaPods host hits duplicate-symbol walls. Mapbox publishes prebuilt binary .xcframeworks (mapbox-navigation-ios-build-artifacts), so the podspec vendors them instead:

  • Ports the native module from Nav SDK v2 → v3 (MapboxNavigationProvider, async calculateRoutes(options:), NavigationOptions with mapboxNavigation:/voiceController:/eventsManager:, CoreConfig.locationSource for simulation, per-VC
    StyleManager — v2's Directions.shared / NavigationSettings.shared are gone).
  • Podspec vendors ios/Frameworks/*.xcframework (static_framework, non-recursive source_files so CocoaPods doesn't strip the frameworks), depends on the shared MapboxMaps 11.20.2 pod (same one @rnmapbox/maps consumes → deduped by name, no
    use_frameworks!).
  • Binaries are not committed (Mapbox terms forbid redistribution; ~80 MB; download-token-gated). The consuming app fetches them into ios/Frameworks/ on demand before pod install (checksum-pinned scripts/fetch-mapbox-nav-binaries.sh, auth via a
    Mapbox DOWNLOADS:READ token).

Version interlock: nav 3.20.1 / MapboxNavigationNative 324.20.2 / MapboxCommon 24.20.2 / MapboxMaps 11.20.2 (aligned with @rnmapbox/maps@10.3.1). When bumping, lift versions + checksums from the matching build-artifacts tag.

Android — com.mapbox.navigationcore 3.20.1

  • Upgrades navigation / ui-components from 3.0.23.20.1 to match iOS (shared transitive maps/common dedupe with @rnmapbox/maps).
  • Selects -ndk27 artifacts when targetSdk >= 35 (mirrors rnmapbox's selection for the 16 KB page-size requirement; mixing ndk27 / non-ndk27 Mapbox artifacts duplicates classes).

New Architecture (Fabric) fixes

  • Events declared inside the codegen spec. Upstream declared event props only via an as HostComponent<… & NativeEventsProps> cast, so Fabric registered zero events and every callback (onArrive, onCancelNavigation, onError,
    onLocationChange, onRouteProgressChange) was silently dropped on New-Arch apps. They now live in NativeProps.
  • Metro resolves to TS source ("react-native": "./src/index.tsx") so codegen can parse the native-component spec.
  • Android starts navigation after the full prop transaction (onAfterUpdateTransaction) instead of from a prop setter racing alphabetical prop order, with guarded teardown.

New props

Prop Type Platform Purpose
theme 'day' | 'night' | 'auto' iOS + Android Pin day/night style; follows live changes (iOS StyleManager, Android NAVIGATION_DAY/NIGHT_STYLE)
styleUrl string iOS + Android App Mapbox style URI so the nav map matches the app's other maps
fontFamily string iOS Nav-UI label font (PostScript family). No-op on Android (build-time text appearance)
bottomInset number iOS + Android Extra bottom camera inset so route/puck clear an app overlay (e.g. bottom sheet)
showsReportFeedback boolean iOS Toggle the SDK report-issue floating button
vehicleMaxHeight number (meters) iOS + Android Truck routing — see below
vehicleMaxWidth number (meters) iOS + Android Truck routing
vehicleMaxWeight number (metric tons) iOS + Android Truck routing

Truck routing (vehicleMaxHeight / vehicleMaxWidth / vehicleMaxWeight)

Wires Mapbox's vehicle-dimension routing constraints end-to-end so callers can request truck-aware routes that avoid low bridges, narrow roads, and weight-restricted roads (where Mapbox has the restriction data). Mapbox has no dedicated truck
profile — these are dimension parameters on the driving / driving-traffic profiles.

Previously vehicleMaxHeight / vehicleMaxWidth were declared on iOS but dead (not registered in the ViewManager, never applied to RouteOptions), and Android had no support at all.

  • iOS — applies the typed RouteOptions.maximumHeight / maximumWidth / maximumWeight (Measurement, .meters / .metricTons), only when provided; registers all three props in the ObjC ViewManager.
  • Android — chains RouteOptions.builder().maxHeight()/.maxWidth()/.maxWeight() (applied only when > 0); adds @ReactProp overrides.
  • JS — props added to the codegen spec (Fabric registration) and the public MapboxNavigationProps.

Units match the Directions API: height/width in meters, weight in metric tons (1000 kg). Omitted / 0 = the API's car-sized defaults (1.6 m / 1.9 m / 2.5 t), so existing callers are unaffected. Best-effort: coverage of road restriction data
varies by region.

Build & packaging

  • Commits a prebuilt lib/ (CommonJS / ESM / TypeScript via react-native-builder-bob) for git-dependency consumption; renames preparebuild (no auto-build on install of a git dep), drops the exports map and adds the react-native source
    field.
  • README banner documenting the fork's v3 vendored-binary integration and consumer requirements; .gitignore for ios/Frameworks/; version 0.5.20.5.3.

Consumer requirements

  • Mapbox DOWNLOADS:READ token (~/.netrc or MAPBOX_DOWNLOADS_TOKEN) — for iOS binary fetch and the Android maven repo.
  • App must run fetch-mapbox-nav-binaries.sh (checksum-pinned) before pod install.
  • Native rebuild required to adopt — not OTA-shippable.

Testing

  • iOS + Android device-verified: turn-by-turn nav, day/night theme, customization props, and the New-Arch event callbacks firing.
  • Truck-routing props verified against the vendored v3.20.1 MapboxDirections interface (maximumHeight/Width/Weight: Measurement<UnitLength/Mass>?) and the Android RouteOptions builder methods; inert until a caller passes values.

stefanpavlovic-tech and others added 12 commits June 10, 2026 10:40
Migrates the iOS module from Nav SDK v2 → v3 so it can coexist with
@rnmapbox/maps' MapboxMaps v11 (v2 pinned MapboxMaps v10 → CocoaPods conflict).

ios/MapboxNavigationView.swift — v2 → v3 API:
- import MapboxNavigationCore + MapboxNavigationUIKit (was MapboxNavigation/MapboxCoreNavigation)
- MapboxNavigationProvider(coreConfig:) replaces Directions.shared + NavigationSettings.shared
- routingProvider().calculateRoutes(options:) (async, await .result) replaces the v2 closure
- NavigationViewController(navigationRoutes:navigationOptions:); NavigationOptions now requires
  mapboxNavigation:/voiceController:/eventsManager:
- mute via provider.routeVoiceController.speechSynthesizer.muted (NavigationSettings removed)
- simulation via CoreConfig.locationSource (.simulation/.live)
- didArriveAt delegate returns Void (v3 breaking change); fixed lng/lat swap in onArrive
- travelMode → ProfileIdentifier; heading now from location.course

react-native-mapbox-navigation.podspec — SPM injection (mirrors @rnmapbox/maps $RNMapboxMaps):
- drop `s.dependency 'MapboxNavigation' ~> 2.18.1` (no v3 CocoaPods pod exists)
- add $RNMapboxNavigation pre_install/post_install hooks for the host Podfile to call;
  default SPM version exactVersion 3.20.1 (pins MapboxMaps 11.20.2 — matches @rnmapbox/maps 10.3.1)
- post_install adds the SPM package + MapboxNavigationCore/UIKit products to BOTH the pod
  target (so the pod's Swift compiles) AND the user app target (so they link)

Paper RCTViewManager structure kept (works under New Arch via interop). Based on the
v3 migration verified against mapbox/navigation-ios; SPM-injection pattern adapted from
upstream's unreleased v0.6 branch (whose iOS Swift was never completed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… consumption

Consuming this fork as a git dependency requires the built lib/ (the package
`main`/`module` point at lib/commonjs + lib/module). pnpm's git-dep prepare
sandbox didn't reliably run bob build, so commit the prebuilt output and rename
the `prepare` script to `build` — installs now use the committed lib/
deterministically instead of a clean-and-rebuild that can fail in CI/sandboxes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v3's MapboxDirections.Waypoint is a value type (was a class in v2), so a 'let'
binding can't have separatesLegs mutated. Verified by headless compile against
Nav SDK 3.20.1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…(Path C)

Replaces the SPM-injection podspec with direct vendoring of Mapbox's prebuilt
binary xcframeworks (mapbox-navigation-ios-build-artifacts @ 3.20.1). The SPM
"duplicate symbol" wall was a misdiagnosis: MapboxNavigationCore/UIKit/Helpers
are distributed as binaries, not source. Vendoring them + sharing the CocoaPods
MapboxMaps pod (deduped by name with @rnmapbox/maps) dissolves the trilemma,
needs no use_frameworks!, and removes the headless-SPM-resolution hang.

- podspec: vendored_frameworks ios/Frameworks/*.xcframework, static_framework,
  s.dependency MapboxMaps 11.20.2, non-recursive source_files (an exclude_files
  pattern would also strip the vendored frameworks → "no such module").
- Binaries are NOT committed (Mapbox ToS / size / token-gated) — the host app
  fetches them on demand into ios/Frameworks/ (gitignored).

Verified: headless xcodebuild link BUILD SUCCEEDED, zero duplicate symbols, all
10 Mapbox/nav frameworks embedded exactly once.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…5 codegen

- build.gradle: bump com.mapbox.navigationcore 3.0.2 → 3.20.1 and mirror
  @rnmapbox/maps' NDK 27 variant selection (targetSdk >= 35 → -ndk27
  artifacts). 3.0.2 pulled maps 11.3.1 / common 24.3.1 (non-ndk27), which
  duplicated classes against rnmapbox's android-ndk27 11.20.2 world; 3.20.1
  ndk27 resolves to the same maps 11.20.2 / common-ndk27 24.20.2 artifacts —
  and matches the iOS vendored binaries' versions.
- ViewManager: New Arch codegen requires set<PropName> method names from the
  TS spec — rename setDirectionUnit→setDistanceUnit, setLocal→setLanguage, and
  add the missing overrides (setSeparateLegs, setShouldSimulateRoute,
  setShowsEndOfRouteFeedback, setHideStatusView) as documented no-ops
  (per-waypoint separatesLegs covers legs; the rest are iOS-only).
- oldarch spec: mirror the renamed/new abstract methods so both architectures
  compile against the same concrete manager.

Verified: app assembleDebug BUILD SUCCESSFUL; dependencyInsight shows a single
com.mapbox.common:common-ndk27:24.20.2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…onent spec

The exports map sent Metro to the prebuilt lib/module .mjs, where
babel-plugin-codegen intercepts *NativeComponent.mjs but the type-stripped
build has no parsable spec → "Could not find component config for native
component" at bundle time. Drop the exports map and add the classic
react-native main field pointing at src/ (Metro resolverMainFields:
['react-native', 'browser', 'main']) — Metro bundles the typed TS source
(codegen succeeds); Node tooling keeps main → lib/commonjs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Events (onCancelNavigation, onArrive, onError, onLocationChange,
onRouteProgressChange) were declared only via an
`as HostComponent<NativeProps & NativeEventsProps>` cast — under the New
Architecture the Fabric view config is generated from the codegen spec, so
ZERO events were registered and every callback was silently dropped (the
cancel ✕ did nothing). Declare them as DirectEventHandler fields inside
NativeProps.

theme prop ('day' | 'night' | 'auto', default auto):
- iOS: explicit themes pin StandardDayStyle/StandardNightStyle via
  NavigationOptions(styles:) and re-apply live on prop change through the
  VC's StyleManager; auto keeps the SDK's time-of-day switching.
- Android: loads NAVIGATION_NIGHT_STYLE for 'night' (day otherwise), live
  reload on prop change; manager + oldarch spec updated.
- lib/ rebuilt (bob) so the committed typescript defs include the new prop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…down

Navigation init was triggered from the distanceUnit prop setter — props apply
in alphabetical order, so `distanceUnit` landed before `startOrigin` and
initNavigation() bailed with "origin and destination are required" on every
mount; JS reacted to the error by unmounting, and onDestroy() then crashed on
the never-initialized lateinit nav APIs
(kotlin.UninitializedPropertyAccessException: maneuverApi).

- Manager: onAfterUpdateTransaction → view.initIfReady() — runs after ALL
  props of the batch are applied; starts navigation exactly once when both
  endpoints are present.
- setDirectionUnit: stores the unit only, no init.
- onDestroy: tear down only what was initialized (::isInitialized guards for
  the lateinit APIs, navigationInitialized for the session-bound ones).
- setTheme live-reload now keys off navigationInitialized (mapboxNavigation is
  created in onCreate, so the old null-check was always true).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… report button)

Add four opt-in props so a host app can match the nav UI to its own design
without baking anything app-specific into the fork. All default to current SDK
behavior when unset.

- styleUrl: load a custom Mapbox style URI. iOS uses ResupplyDay/NightStyle
  (subclass DayStyle/NightStyle, which inherit Style.applyMapStyle so mapStyleURL
  is honored — Standard styles force the SDK style). Android styleForTheme()
  returns it. Falls back to the SDK navigation style.
- fontFamily: nav-label font family. iOS Style.fontFamily; Android no-op
  (maneuver banner uses a build-time textAppearance, no runtime font-by-name).
- bottomInset: extra bottom camera padding so the route/puck clear an app
  overlay. iOS overrides only the bottom edge of viewportPadding; Android adds
  to the base overview/following padding.
- showsReportFeedback: toggle the iOS report-issue floating button (default
  true = SDK default); keeps overview/recenter/mute.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The published package resolves types from the prebuilt lib/typescript; rebuild
it (bob build) so styleUrl / fontFamily / bottomInset / showsReportFeedback are
present for consumers. Pairs with the previous source commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire Mapbox Directions vehicle-dimension constraints end-to-end so callers
can request truck-aware routes (avoid low bridges / narrow / weight-restricted
roads). Previously vehicleMaxHeight/Width were declared on iOS but dead
(unregistered in the ViewManager, never applied to RouteOptions) and Android
had no support at all.

- iOS: declare vehicleMaxWeight; apply all three to NavigationRouteOptions via
  the typed maximumHeight/Width/Weight (Measurement, .meters/.metricTons),
  only when provided; register the three props in the ObjC ViewManager.
- Android: store the three as Double fields + setters; chain
  maxHeight()/maxWidth()/maxWeight() onto RouteOptions.builder() only when > 0;
  add @ReactProp overrides.
- JS: add props to the codegen spec (New-Arch Fabric registration) and the
  public MapboxNavigationProps; rebuild lib/.

Units match the Directions API: height/width in meters, weight in metric tons.
Omitted / 0 = the API's car-sized defaults (1.6 m / 1.9 m / 2.5 t).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@stefanpavlovic-tech stefanpavlovic-tech changed the title Mapbox Navigation SDK v3 — vendored binary xcframeworks (iOS) + matching Android, New Arch fixes, theme support Resupply fork: Mapbox Navigation SDK v3 (vendored binaries) + customization & truck-routing props Jun 22, 2026
stefanpavlovic-tech and others added 17 commits June 22, 2026 14:02
The README props section was upstream's and listed none of the props this
fork added. Add a "Resupply fork additions" subsection covering theme,
styleUrl, fontFamily, bottomInset, showsReportFeedback, and the truck-routing
trio (vehicleMaxHeight/Width/Weight — meters / metric tons, with their
car-sized defaults when unset).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
showCancelButton was declared + exported to JS but never applied — the
setup wired showsReportFeedback/showsEndOfRouteFeedback/hideStatusView/
theme and silently dropped it, so the SDK's bottom-banner cancel (X)
always showed. Apply it: when showCancelButton is false (the default),
pass a BottomBannerViewController subclass that hides its cancelButton in
viewDidLoad, keeping the ETA / distance / arrival banner. Host apps that
provide their own exit control no longer get a duplicate cancel button.

Subclassing is the timing-safe way to hide it — cancelButton is an IUO
that only exists post-viewDidLoad — and the SDK still wires the subclass
for progress (onRouteProgressUpdated) the same as the default banner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…anner

Hiding only the cancel button left its empty layout slot plus the
vertical divider line, so the arrival-time label sat mid-banner with dead
space on the right. Also hide the vertical divider and zero both views'
self-width constants so the arrival-time label (chained to divider →
cancel) slides to the trailing edge. Constraint-safe — no constraints
added/removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapsing widths didn't move the arrival-time label — the cancel button
has no width constraint (intrinsic size) and the label isn't chained to
it. Instead, deactivate the arrival-time label's horizontal constraints
and re-pin its trailing edge to the banner trailing (centerY kept), so it
fills the space the hidden cancel button left.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a `vehicleType` prop ('van'|'truck'|'box_truck'|'cargo_van') that
swaps the location puck for a bundled per-vehicle icon (silhouette as the
bearing image so it rotates to the travel course); empty/unknown keeps the
SDK default puck. Also replaces the SDK's default destination marker with
the app's donor pin via the didAdd finalDestinationAnnotation delegate, so
the embedded nav matches the donation pins on the app's other maps.

Assets are placeholder PNGs (designer-replaceable — same filenames in
ios/Assets/ = drop-in swap) bundled via a new resource_bundle; the donor
pin is rasterized from the app's en-route donation pin. SVG sources kept
under ios/Assets/src/. iOS only — Android Drop-In UI puck/marker is a
follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move icon ownership out of the SDK and into the host app. Drop the
bundled placeholder PNGs + resource_bundle + the hardcoded vehicleType→
image switch. Replace with two host-supplied image-source props,
`puckImage` and `destinationImage` (RCTConvert→UIImage): puckImage is
applied as the location puck's bearing image; destinationImage replaces
the SDK's destination marker via the didAdd finalDestinationAnnotation
delegate. Either omitted = the SDK default.

The app now owns the {vehicleType: image} catalog and the donor pin, so
designers iterate on art in the app without a fork rebuild. ImageSource is
imported from a local module so RN codegen name-matches it to the reserved
image primitive (a local alias would resolve to a plain object) while
staying typecheck-safe against the pinned RN. iOS only — Android follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The puckImage/destinationImage UIImage props arrived nil in dev: RCTConvert
can't load Metro's http asset URIs into a UIImage, so the puck/destination
fell back to SDK defaults (only a release build would have worked). Switch
to puckImageUri/destinationImageUri string props and load them natively —
URLSession for http (dev), filesystem for file:// (release), bundle for a
bare name — caching the UIImage and (re)applying the puck + destination
annotation whenever the async load lands. Drops the codegen ImageSource
type (and its local shim) entirely.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Android parity for the puckImageUri / destinationImageUri props. Load each
URI to a Bitmap via Fresco (RN's pipeline — handles Metro-dev http and
release res:// / file://), then: swap the location puck's bearing image
(LocationPuck2D + ImageHolder.from(bitmap)), and drop a PointAnnotation at
the destination with the donor pin (created in the loadStyle callback so
the style is ready). Default puck/marker kept when no URI / load fails.
Setters re-apply live once nav is up. oldarch spec left as-is (stale; app
is New Arch — manager overrides the codegen interface).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two fix attempts (UIImage prop, then URI loader) still show default pins;
codegen is confirmed current (props are in the generated Fabric spec), so
the break is downstream. Log each step (prop arrival, image load result,
puck apply, destination delegate fire + apply) under the 🧭RSPLNav tag to
localize it from one run. To be reverted once diagnosed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Force an obvious red-car system image onto the puck (incl. a delayed
re-apply to rule out the SDK overriding it on start) and the destination
annotation, bypassing host-image loading — so we can tell ON-SCREEN
whether the apply path works without needing native logs. Red car =
apply works (bug is image loading); still-default = the apply API/timing
is the problem.

Bump the package version so CocoaPods detects the change and recompiles
the pod on `expo run:ios` — the commit SHA alone doesn't change the pod
version string, so incremental builds were silently keeping stale fork
binaries. Temporary diagnostic; to be reverted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…agnostic

Replace the 2D puck-image approach with a 3D model puck. New puckModelUri
prop (a .glb/.gltf URI, local or remote) renders via Puck3DConfiguration
(iOS) / LocationPuck3D (Android); the SDK fetches the model directly so
there's no host-side image loading. puckBearing=.course turns it with
travel. Empty URI keeps the SDK default 2D puck. Model scale is a tunable
constant (80) — model-dependent.

The diagnostic red-car probe confirmed the puck APPLY path works (it
changed the puck), so the prior failure was the 2D image never loading;
the 3D model URI sidesteps that. Diagnostic logging/code removed. The
donor destination pin (destinationImageUri, 2D) is unchanged. Bump 0.5.5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The custom location puck / donor destination pin never rendered reliably
across five approaches (2D UIImage prop, 2D URI loader, Android Fresco, and
3D model) — each hit a different native loading/rendering wall, and it's
polish nobody requested. Revert the icon source files to the banner-fix
baseline (e5b9a69): the embedded nav uses the SDK's default puck + marker.
Keeps the cancel-button + arrival-time banner work. Bump 0.5.6 so the pod
recompiles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Day-mode maneuver banner: white bg + dark text + dark turn-arrow (theme-aware;
  night keeps the SDK dark banner). Full-width banner; fill top corners white.
- Lane guidance: active lane renders dark (matches iOS), inactive stay faded.
- Puck above the route line: place route below "road-label-simple" — the actual
  road-label layer id in the app's light-v11/dark-v11 styles ("road-label" and
  the nav-only "road-label-navigation" don't exist there, so the route fell to
  the top of the stack over the puck).
- Route arrow above the route-line top layer (withAboveLayerId).
- Start in follow mode instead of route overview, like iOS.
- Hide the compass ornament during guidance.
- Keep Mapbox logo + attribution visible (ToS): logo bottom-left, info
  bottom-right, lifted above the bottom sheet.
- Add explicit maps-logo / maps-attribution plugin deps (not reliably on the
  module compile classpath otherwise).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add MapboxSpeedInfoView (top-left, mirrors iOS placement) driven by
  MapboxSpeedInfoApi from the existing LocationObserver; MUTCD sign style;
  visibility wired to route start/teardown.
- Codegen: numeric props (vehicleMax*/bottomInset) Double -> WithDefault<Double,0>
  so the generated delegate param matches the non-null `value: Double` manager
  overrides (a bare optional Double generated a nullable param that would not
  compile against the overrides — never built because node_modules was stale).
- iOS: treat 0 as unset on truck dims (> 0 guard) so a WithDefault 0 can't set
  maximumHeight = 0 m and reject every road.
- Rebuild lib/ (bob build).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…imental options)

Caught by a local Android build of the previous commit:
- updatePostedAndCurrentSpeed() returns SpeedInfoValue? (nullable) — null-guard
  before render() (which takes non-null).
- Drop setSpeedInfoOptions(...) — the symbol is unresolved on MapboxSpeedInfoView
  and MapboxSpeedInfoOptions.Builder needs @ExperimentalPreviewMapboxNavigationAPI
  opt-in. The view auto-selects the sign convention from road data (MUTCD for the
  US fleet), so the default is correct without forcing it.
- Remove now-unused SpeedLimitSign / MapboxSpeedInfoOptions imports.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
iOS shows the built-in SpeedLimitView (posted limit + current-speed overspeed
warning, showsSpeedLimits defaults true) but HIDES the sign when the posted
limit is unknown — so on sparse/simulated routes it appears blank/absent.

Add an `alwaysShowSpeedLimit` prop: iOS sets speedLimitView.shouldShowUnknownSpeedLimit
(and showsSpeedLimits = true explicitly). The app passes true outside production
(visible during testing) and false in production (no blank signs on rural roads).
Android no-op — its speed-info badge is already shown and data-driven.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NavigationViewController has no `speedLimitView`; it's on the root
NavigationView (NavigationViewController.navigationView.speedLimitView).
Caught by an iOS build of the previous commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
stefanpavlovic-tech and others added 7 commits June 26, 2026 21:09
T2 of the offline-nav milestone — the offline-reroute foundation.

iOS: state the CoreConfig routing/tilestore config explicitly in embed()
(routingProviderSource .hybrid + detectsReroute + tilestoreConfig .default
+ predictiveCacheConfig). These are the v3 defaults, made explicit so the
offline intent is pinned across SDK bumps. .default shares TileStore.default
with @rnmapbox/maps automatically.

Android: default NavigationOptions uses a private nav-tiles path, so set
RoutingTilesOptions.tileStore(TileStore.create()) on the first provider
create() to share the same default store @rnmapbox/maps uses.

With this, once a corridor region is downloaded into the default store (T4),
the on-board router reroutes locally with no connectivity.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
T3 of the offline-nav milestone — the codegen + TS half (native impls follow).

- codegenConfig.type "components" -> "all" so codegen emits a TurboModule spec
  alongside the existing MapboxNavigationView component spec.
- src/NativeMapboxNavigationOffline.ts: TurboModule spec (downloadRegion /
  listRegions / removeRegion / clearAllRegions + addListener/removeListeners).
  Codegen-legal types only (Object across the bridge).
- src/types.ts: OfflineRegionOptions / OfflineRegion / OfflineRegionDownloadProgressEvent
  + OfflineRegionStatus — the typed shapes the facade exposes.
- src/index.tsx: MapboxOffline typed facade over the Object-based spec + a lazy
  NativeEventEmitter for onRegionDownloadProgress. View default export unchanged.

tsc --noEmit clean. lib/ rebuild + native modules land in the follow-up commits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… wiring

T4/T5 (Android) of the offline-nav milestone.

JS: revert codegenConfig to "components" and make MapboxNavigationOffline a
classic NativeModules accessor (linking-error proxy) instead of a codegen
TurboModule. The fork's view manager already relies on the host app's New-Arch
legacy interop, so a classic module resolves identically at runtime while
avoiding the iOS generated-protocol conformance (the fiddliest blind-build risk).
The MapboxOffline facade + NativeEventEmitter in index.tsx are unchanged.

Android: MapboxNavigationOfflineModule (ReactContextBaseJavaModule, registered in
MapboxNavigationPackage). downloadRegion does a Directions request, decodes the
route line, buffers it into a corridor (MultiPolygon of Turf circles sampled
along the route — robust vs self-intersection), then downloads maps + nav
tileset descriptors + a style pack via loadStylePack -> loadTileRegion into the
shared default TileStore, streaming onRegionDownloadProgress. Plus listRegions /
removeRegion / clearAllRegions and the NativeEventEmitter stubs.

API signatures verified via javap against the resolved 3.20.1 / 11.20.2 AARs.
iOS module + lib rebuild + version bump follow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
T4/T5 (iOS) of the offline-nav milestone — parity with the Android module.

MapboxNavigationOffline.swift (@objc RCTEventEmitter) + .m (RCT_EXTERN_MODULE),
resolved via the host app's New-Arch legacy interop like the fork's view manager.

downloadRegion computes a route via NavigationRouteOptions(coordinates:) ->
routingProvider().calculateRoutes, reads routes.mainRoute.route.shape.coordinates,
buffers it into a corridor (MultiPolygon of circles via Turf coordinate(at:facing:)),
then downloads maps + nav tileset descriptors + a style pack via loadStylePack ->
loadTileRegion into the TileStore obtained through the provider's
tilestoreConfig.navigatorLocation (so nav + maps share one store). Streams
onRegionDownloadProgress; plus listRegions / removeRegion / clearAllRegions.

All Swift signatures pinned against mapbox-navigation-ios v3.20.1 /
mapbox-maps-ios v11.20.2 / turf-swift v4.0.0 source, mirroring the official
Offline-Regions.swift example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
T7 — regenerate the committed lib/ build output (bob) so the offline JS surface
(NativeMapboxNavigationOffline accessor + MapboxOffline facade + types) is in the
package the app consumes, and bump the version (per fork-iteration rule: bump
version, not just the SHA, so podspec/Android caches invalidate).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
iOS Nav SDK allows only ONE active MapboxNavigationProvider — the offline module
allocated its own while the embedded view had one, tripping the SDK's
"Two simultaneous active navigation cores" [BUG] abort the moment the en-route
screen fired the corridor pre-download (crash on entering en-route).

Add SharedNavigationProvider (a single app-wide provider, created on first use)
and route BOTH the view's embed() and the offline module through it — mirroring
Android's MapboxNavigationProvider process singleton (which is why Android never
hit this). The view still holds/niles its ref per session; the singleton retains
the core. Offline-reroute config (.hybrid + tilestoreConfig .default) lives in
the singleton.

Native-only (Swift) — no lib change. Bump 0.6.0 -> 0.6.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-dim corridors

Audit vs official Nav SDK v3.20.1 offline patterns found two functional
gaps and two hardening issues:

1. styleUrls: string[] — the style pack + maps tiles must match the style
   the nav view actually renders (the app passes light/dark-v11 to the
   view, but downloads defaulted to streets/Standard → blank basemap
   offline). Multiple styles per region: one maps descriptor per style,
   sequential style packs; light+dark share sources so tiles dedupe.
   Android default aligned to Streets (was Standard) to match iOS.

2. vehicleMaxHeight/Width/Weight on downloadRegion — the corridor route
   is now computed with the same truck dimensions the nav view applies
   (same >0 guard, meters/metric tons), so a dimension-forced detour
   stays inside the downloaded corridor.

3. Android emitProgress now guards hasActiveReactInstance() — downloads
   outlive React instances (foreground OTA force-reload) and emitting
   into a dead instance throws. iOS already guarded via hasListeners.

4. Android removeRegion no longer evicts the style pack — packs are
   style-keyed, shared across every corridor, and small; evicting forced
   a pointless re-download each head-out (and targeted the wrong style
   after a custom styleUrl download). Matches iOS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant