@jacques_gordon/expo-mapbox-navigation 2.2.6 → 2.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,40 +10,40 @@ Pod::Spec.new do |s|
10
10
  s.license = package['license']
11
11
  s.author = package['author']
12
12
  s.homepage = package['homepage']
13
+
14
+ # Mapbox Navigation SDK v3 requires iOS 14+
13
15
  s.platforms = { :ios => '14.0' }
14
16
  s.swift_version = '5.9'
15
17
  s.source = { git: package['repository']['url'], tag: "v#{s.version}" }
18
+ s.static_framework = true
16
19
 
17
20
  s.dependency 'ExpoModulesCore'
18
21
 
19
- # ── iOS: Mapbox Navigation SDK v3 ───────────────────────────────────────────
20
- #
21
- # IMPORTANT — do NOT use spm_dependency() here.
22
+ # ── iOS: Mapbox Navigation SDK v3 via SPM ─────────────────────────────────
22
23
  #
23
- # spm_dependency() causes "43029 duplicate symbols" linker errors when used
24
- # alongside @rnmapbox/maps in an Expo project (confirmed: React Native
25
- # GitHub issue #47344, Expo GitHub issue #37813). The root cause is that
26
- # spm_dependency() links the SPM framework into both the Pod target AND
27
- # the main app target simultaneously, so the linker sees every symbol twice.
24
+ # Mapbox Navigation SDK v3 is SPM-only (CocoaPods "coming soon" per Mapbox).
25
+ # We do NOT use spm_dependency() here that causes 43029 duplicate symbol
26
+ # linker errors when used alongside @rnmapbox/maps.
28
27
  #
29
- # The correct approach for Expo modules that need SPM-only packages:
30
- # add the SPM dependency directly to the Xcode project's main target via
31
- # the config plugin (withXcodeProject + withDangerousMod), which is exactly
32
- # what our plugin/src/index.js does via the addMapboxNavigationSPM function.
33
- # That way there is only ONE copy of each framework in the final binary.
28
+ # Instead, our config plugin (app.plugin.js) injects a post_install hook
29
+ # into the Podfile. The hook uses the Xcodeproj Ruby API the same technique
30
+ # as @rnmapbox/maps itself to add mapbox-navigation-ios as a SPM
31
+ # dependency to the ExpoMapboxNavigation pod target and the app target.
32
+ # find-or-create semantics prevent any duplication.
34
33
  #
35
- # MapboxNavigationCore and MapboxNavigationUIKit are therefore listed as
36
- # weak framework references here so the module compiles against them, while
37
- # the actual linking happens at the app level from the SPM dependency added
38
- # by the config plugin.
39
- s.pod_target_xcconfig = {
40
- 'DEFINES_MODULE' => 'YES',
41
- 'SWIFT_COMPILATION_MODE' => 'wholemodule',
42
- 'OTHER_SWIFT_FLAGS' => '$(inherited) -Xfrontend -disable-reflection-metadata',
43
- 'FRAMEWORK_SEARCH_PATHS' => '$(inherited) $(PLATFORM_DIR)/Developer/Library/Frameworks',
44
- 'OTHER_LDFLAGS' => '$(inherited)',
45
- 'IPHONEOS_DEPLOYMENT_TARGET' => '14.0',
46
- }
34
+ # The post_install hook runs during `pod install`, with full access to
35
+ # installer.pods_project and installer.aggregate_targets, which is the only
36
+ # correct way to add SPM packages alongside CocoaPods in Expo projects.
47
37
 
48
38
  s.source_files = 'ios/**/*.{swift,h,m,mm}'
39
+ s.exclude_files = [
40
+ 'ios/Package.swift',
41
+ 'ios/build-xcframeworks.sh',
42
+ ]
43
+
44
+ s.pod_target_xcconfig = {
45
+ 'DEFINES_MODULE' => 'YES',
46
+ 'SWIFT_COMPILATION_MODE' => 'wholemodule',
47
+ 'IPHONEOS_DEPLOYMENT_TARGET' => '14.0',
48
+ }
49
49
  end
package/README.md CHANGED
@@ -2,17 +2,16 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/@jacques_gordon%2Fexpo-mapbox-navigation.svg)](https://www.npmjs.com/package/@jacques_gordon/expo-mapbox-navigation)
4
4
 
5
- Expo module for Mapbox Navigation SDK — forked from [`@badatgil/expo-mapbox-navigation`](https://github.com/uju777/expo-mapbox-navigation) with the following fixes and improvements:
6
-
7
- | Change | Details |
8
- |--------|---------|
9
- | 🐛 **Fix #43** | Android crash: `NoSuchMethodError` for `CameraAnimationsUtils.calculateCameraAnimationHint` — caused by Mapbox Maps/Navigation SDK version mismatch. Fixed by pinning `mapbox-maps-android ≥ 11.11.0` and Navigation SDK `3.7.0`. |
10
- | 🐛 **Fix #31** | Voice instructions always defaulted to imperial units. New `voiceUnits` prop (`"metric"` \| `"imperial"`) added. |
11
- | **NDK 27** | Forced NDK `27.0.12077973` for full 16 KB page size compatibility (Android 15+ requirement). |
12
- | **16 KB page size** | `jniLibs.useLegacyPackaging = false` + `ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON` required for Google Play compliance from 2025 onwards. |
13
- | **Expo SDK 53+** | Compatible with Expo SDK ≥ 53 and React Native 0.79+. |
14
- | **Maps v11.11.0** | Minimum Mapbox Maps Android SDK enforced to 11.11.0 (config plugin validates this). |
15
- | ✅ **iOS support** | Full native iOS implementation (`NavigationViewController` drop-in UI) via Swift Package Manager — feature parity with Android (lane guidance, speed limit, voice instructions, day/night, steps list). |
5
+ Full-featured Expo module for Mapbox Navigation SDK v3 Android and iOS.
6
+
7
+ ---
8
+
9
+ ## Features
10
+
11
+ - **Android** Waze-style navigation UI built from scratch: maneuver banner, lane guidance, speed limit, ETA bar, voice instructions, mute/overview/recenter buttons, day/night auto-switch
12
+ - **iOS** Drop-in `NavigationViewController` from Mapbox Navigation SDK v3 (lane guidance, speed limit, voice, day/night all built-in)
13
+ - **Both platforms** 7 events, 19 props, full feature and API parity
14
+ - NDK 27 + 16 KB page size compliant (Android 15+)
16
15
 
17
16
  ---
18
17
 
@@ -22,54 +21,108 @@ Expo module for Mapbox Navigation SDK — forked from [`@badatgil/expo-mapbox-na
22
21
  npx expo install @jacques_gordon/expo-mapbox-navigation @rnmapbox/maps
23
22
  ```
24
23
 
25
- ### Setup @rnmapbox/maps first
24
+ ### 1. Setup @rnmapbox/maps first
25
+
26
+ ```json
27
+ ["@rnmapbox/maps", {
28
+ "RNMapboxMapsImpl": "mapbox",
29
+ "RNMapboxMapsVersion": "11.11.0",
30
+ "RNMapboxMapsDownloadToken": "sk.your_secret_token"
31
+ }]
32
+ ```
26
33
 
27
- Follow the [full @rnmapbox/maps installation guide](https://rnmapbox.github.io/docs/install). Set `RNMapboxMapsVersion` to `11.11.0` or higher.
34
+ ### 2. Add this plugin
28
35
 
29
36
  ```json
30
- "plugins": [
31
- [
32
- "@rnmapbox/maps",
33
- {
34
- "RNMapboxMapsImpl": "mapbox",
35
- "RNMapboxMapsVersion": "11.11.0",
36
- "RNMapboxMapsDownloadToken": "sk.your_secret_token"
37
- }
38
- ]
39
- ]
37
+ ["@jacques_gordon/expo-mapbox-navigation", {
38
+ "accessToken": "pk.your_public_token",
39
+ "downloadsToken": "sk.your_secret_token",
40
+ "mapboxMapsVersion": "11.11.0"
41
+ }]
40
42
  ```
41
43
 
42
- ### Add this plugin
44
+ ### 3. iOS only — enable static frameworks
43
45
 
44
46
  ```json
45
- "plugins": [
46
- [
47
- "@jacques_gordon/expo-mapbox-navigation",
48
- {
49
- "accessToken": "pk.your_public_token",
50
- "downloadsToken": "sk.your_secret_token",
51
- "mapboxMapsVersion": "11.11.0"
52
- }
53
- ]
54
- ]
47
+ ["expo-build-properties", { "ios": { "useFrameworks": "static" } }]
55
48
  ```
56
49
 
57
- > ⚠️ `mapboxMapsVersion` must match the version set in `@rnmapbox/maps`. Minimum: `11.11.0`.
50
+ ---
51
+
52
+ ## Plugin Options
53
+
54
+ | Option | Required | Default | Description |
55
+ |--------|----------|---------|-------------|
56
+ | `accessToken` | ✅ | — | Public Mapbox token (`pk.*`). Used for map tiles and routing. |
57
+ | `downloadsToken` | ✅ | — | Secret Mapbox token (`sk.*`) with **Downloads:Read** scope. Same token as `RNMapboxMapsDownloadToken`. Used on iOS to authenticate SPM when fetching the Navigation SDK from `api.mapbox.com` via `~/.netrc`. |
58
+ | `mapboxMapsVersion` | ✅ | `"11.11.0"` | Must exactly match `RNMapboxMapsVersion` in `@rnmapbox/maps`. |
59
+ | `mapboxNavigationVersion` | — | auto-calculated | iOS only. See [iOS Version Strategy](#ios-version-strategy) below. |
60
+ | `androidColorOverrides` | — | `{}` | Override Mapbox native resource colors on Android. |
61
+
62
+ ---
63
+
64
+ ## iOS Architecture
65
+
66
+ ### How it works
67
+
68
+ iOS uses `NavigationViewController` — the official Mapbox Navigation SDK v3 drop-in UI — installed via **Swift Package Manager** (SPM). CocoaPods does not host the Navigation SDK v3 (Mapbox confirmed CocoaPods support is "coming soon").
69
+
70
+ This package bridges SPM into your CocoaPods/Expo project using a **`post_install` Ruby hook** injected into your `Podfile` — the same technique used by `@rnmapbox/maps` itself. The hook uses the Xcodeproj Ruby API (`XCRemoteSwiftPackageReference`, `XCSwiftPackageProductDependency`) to add the package properly, with find-or-create semantics to prevent duplicate symbols.
71
+
72
+ ### iOS Version Strategy
58
73
 
59
- > ⚠️ `downloadsToken` is **required** it's a secret Mapbox token (starts with `sk.`) with the **Downloads:Read** scope. On iOS it's used to authenticate Swift Package Manager when it fetches the Mapbox Navigation SDK from Mapbox's private package registry (via a generated `.netrc` entry). This is the same token already used for `RNMapboxMapsDownloadToken` above — you can reuse it.
74
+ This is the most important part. Understanding it prevents build failures.
60
75
 
61
- ### iOS architecture
76
+ **The problem:** SPM requires all packages in the dependency graph to agree on a single version of shared libraries (`MapboxCommon`, `MapboxMaps`, `Turf`). Both `@rnmapbox/maps` and `mapbox-navigation-ios` depend on these shared libraries. If they request incompatible versions, SPM fails.
62
77
 
63
- iOS uses the **official Mapbox Navigation SDK v3 drop-in UI** (`NavigationViewController`), installed via **Swift Package Manager** — not prebuilt `.xcframework` binaries. This is the only officially supported distribution method for the Navigation SDK v3 on iOS; CocoaPods doesn't host it directly, so this package bridges it in automatically through CocoaPods' `spm_dependency()` mechanism when you run `pod install` / `expo prebuild`.
78
+ **The Mapbox versioning pattern** (confirmed from official GitHub releases):
64
79
 
65
- Because `NavigationViewController` is a complete drop-in experience, lane guidance, speed limit display, voice instructions, and the recenter/overview camera button all come built-in from Mapbox — no extra wiring needed, unlike the more manual Android implementation.
80
+ The `.0` release of each Navigation minor always pairs with the matching Maps minor:
66
81
 
67
- ### iOS: enable static frameworks
82
+ | Maps version | Navigation `.0` | Compatible? |
83
+ |---|---|---|
84
+ | `11.11.0` | `3.11.0` | ✅ |
85
+ | `11.12.0` | `3.12.0` | ✅ |
86
+ | `11.21.5` | `3.21.5` (same minor+patch) | ✅ |
87
+
88
+ **⚠️ Patch versions drift.** Navigation patch releases (`3.11.x` where x > 0) often update to a newer Maps version — for example `3.11.4` requires Maps `11.14.7`, not `11.11.x`. Using `upToNextMinorVersion` would therefore be unsafe.
89
+
90
+ **Our solution:** We use the **exact** `.0` version that matches your Maps minor:
91
+
92
+ ```
93
+ mapboxMapsVersion = "11.11.0"
94
+ → Navigation exact version = "3.11.0"
95
+ → requires MapboxMaps 11.11.x ✅ compatible
96
+ ```
97
+
98
+ This is **fully automatic** — when you upgrade Maps from `11.11.0` to `11.12.0`, the Navigation version is recalculated to exact `3.12.0`.
99
+
100
+ **Manual override (escape hatch):** To pin any specific Navigation version:
68
101
 
69
102
  ```json
70
- "plugins": [
71
- ["expo-build-properties", { "ios": { "useFrameworks": "static" } }]
72
- ]
103
+ ["@jacques_gordon/expo-mapbox-navigation", {
104
+ "accessToken": "pk.xxx",
105
+ "downloadsToken": "sk.xxx",
106
+ "mapboxMapsVersion": "11.11.0",
107
+ "mapboxNavigationVersion": "3.11.2"
108
+ }]
109
+ ```
110
+
111
+ During `expo prebuild`, you will see in the logs:
112
+ ```
113
+ [@jacques_gordon/expo-mapbox-navigation] Maps 11.11.0 → auto-calculated Navigation 3.11.0..<3.12.0
114
+ [@jacques_gordon/expo-mapbox-navigation] ✅ Wrote Mapbox credentials to ~/.netrc
115
+ [@jacques_gordon/expo-mapbox-navigation] ✅ Injected mapbox-navigation-ios SPM hook into Podfile
116
+ ```
117
+
118
+ And during `pod install`:
119
+ ```
120
+ [ExpoMapboxNavigation] Added mapbox-navigation-ios to pods_project
121
+ [ExpoMapboxNavigation] Found target: ExpoMapboxNavigation
122
+ [ExpoMapboxNavigation] Linked MapboxNavigationCore -> ExpoMapboxNavigation
123
+ [ExpoMapboxNavigation] Linked MapboxNavigationUIKit -> ExpoMapboxNavigation
124
+ [ExpoMapboxNavigation] Linked MapboxNavigationCore -> Navio
125
+ [ExpoMapboxNavigation] Linked MapboxNavigationUIKit -> Navio
73
126
  ```
74
127
 
75
128
  ---
@@ -84,50 +137,89 @@ export default function Navigation() {
84
137
  <MapboxNavigationView
85
138
  style={{ flex: 1 }}
86
139
  coordinates={[
87
- { latitude: 48.8566, longitude: 2.3522 }, // Paris
88
- { latitude: 51.5074, longitude: -0.1278 }, // London
140
+ { latitude: 50.8503, longitude: 4.3517 }, // Brussels
141
+ { latitude: 51.2194, longitude: 4.4025 }, // Antwerp
89
142
  ]}
90
- voiceUnits="metric" // Fix for issue #31
143
+ voiceUnits="metric"
91
144
  language="fr"
92
145
  navigationProfile="driving-traffic"
146
+ onRoutesReady={({ nativeEvent }) =>
147
+ console.log('Route ready:', nativeEvent.distanceMeters, 'm')
148
+ }
149
+ onRouteProgressChanged={({ nativeEvent }) =>
150
+ console.log('Remaining:', nativeEvent.distanceRemaining, 'm')
151
+ }
152
+ onManeuverBannerPressed={({ nativeEvent }) => {
153
+ // Open a bottom sheet showing all upcoming steps
154
+ console.log('Steps:', nativeEvent.steps);
155
+ }}
93
156
  onArrival={() => console.log('Arrived!')}
157
+ onNavigationCancelled={() => console.log('Cancelled')}
94
158
  onRoutesFailed={({ nativeEvent }) =>
95
- console.error('Routes failed:', nativeEvent.message)
159
+ console.error('Failed:', nativeEvent.message)
96
160
  }
97
161
  />
98
162
  );
99
163
  }
100
164
  ```
101
165
 
102
- ### Color customization (Android)
166
+ ---
103
167
 
104
- All color props are optional — if not provided, the defaults below are applied automatically.
168
+ ## Props
105
169
 
106
- ```tsx
107
- <MapboxNavigationView
108
- // Maneuver banner (turn-by-turn instruction banner)
109
- maneuverBackgroundColorDay="#1E2433" // default: Mapbox native style color
110
- maneuverTurnIconColor="#1A73E8" // default: Mapbox native style color
170
+ ### Navigation
171
+
172
+ | Prop | Type | Default | Description |
173
+ |------|------|---------|-------------|
174
+ | `coordinates` | `{ latitude: number; longitude: number }[]` | **required** | Waypoints. Minimum 2. |
175
+ | `waypointIndices` | `number[]` | all | Which coordinates are true waypoints (vs. route shape points). |
176
+ | `navigationProfile` | `string` | `"driving-traffic"` | `"driving-traffic"`, `"driving"`, `"walking"`, `"cycling"`. **Android**: omit the `"mapbox/"` prefix. |
177
+ | `language` | `string` | device locale | BCP-47 tag (e.g. `"fr"`, `"nl"`, `"en-US"`). |
178
+ | `voiceUnits` | `"metric" \| "imperial"` | auto by locale | Overrides automatic unit detection. |
179
+ | `excludeTypes` | `string[]` | — | Road types to avoid (e.g. `["toll", "ferry"]`). |
180
+ | `mapStyle` | `string` | Mapbox Navigation Day | Map style URL. |
181
+ | `mute` | `boolean` | `false` | Silence voice instructions. |
182
+ | `maxHeight` | `number` | — | Max vehicle height in metres. |
183
+ | `maxWidth` | `number` | — | Max vehicle width in metres. |
184
+ | `useMapMatching` | `boolean` | `false` | Use Map Matching API instead of routing. |
185
+ | `customRasterTileUrl` | `string` | — | Custom raster tile URL with `{x}/{y}/{z}`. |
186
+ | `customRasterAboveLayerId` | `string` | — | Layer ID to insert custom raster tiles above. |
111
187
 
112
- // Bottom ETA / duration / distance bar
113
- etaBarBackgroundColor="#1E2433" // default: #1E2433 (dark navy)
114
- etaTextColor="#FFFFFF" // default: #FFFFFF (white)
188
+ ### Color Customization (Android)
115
189
 
116
- // Control buttons (mute, overview, recenter)
117
- iconButtonColor="#1A73E8" // default: #1A73E8 (Google Blue)
118
- iconButtonMutedColor="#EA4335" // default: #EA4335 (Google Red)
190
+ All color props are optional — defaults are applied when omitted.
119
191
 
120
- // Route line color set via plugin config, not a view prop
121
- // (uses androidColorOverrides in app.json)
122
- {...otherProps}
192
+ | Prop | Default | Description |
193
+ |------|---------|-------------|
194
+ | `maneuverBackgroundColorDay` | Mapbox default | Background of the turn-by-turn instruction banner. Uses `ManeuverViewOptions.maneuverBackgroundColor` (official Mapbox SDK API). |
195
+ | `maneuverTurnIconColor` | Mapbox default | Color of the turn arrow icon. Uses `ManeuverViewOptions.turnIconManeuver`. |
196
+ | `etaBarBackgroundColor` | `"#1E2433"` | Background of the bottom ETA/duration/distance bar. |
197
+ | `etaTextColor` | `"#FFFFFF"` | Text color for ETA time and duration. |
198
+ | `iconButtonColor` | `"#1A73E8"` | Color of the mute/overview/recenter buttons (default state). |
199
+ | `iconButtonMutedColor` | `"#EA4335"` | Color of the mute button when voice is muted. |
200
+
201
+ ```tsx
202
+ <MapboxNavigationView
203
+ maneuverBackgroundColorDay="#1E2433"
204
+ maneuverTurnIconColor="#1A73E8"
205
+ etaBarBackgroundColor="#1E2433"
206
+ etaTextColor="#FFFFFF"
207
+ iconButtonColor="#1A73E8"
208
+ iconButtonMutedColor="#EA4335"
123
209
  />
124
210
  ```
125
211
 
212
+ > **Note:** On iOS, `NavigationViewController` applies its own theme. Color props are stored and can be applied via the SDK's `StyleManager` in a future release.
213
+
214
+ ### Mapbox Native Colors (Android, via plugin)
215
+
216
+ Override Mapbox's built-in resource colors (route line, etc.) via `androidColorOverrides` in `app.json`:
217
+
126
218
  ```json
127
- // app.json — route line and other Mapbox native resource colors
128
219
  ["@jacques_gordon/expo-mapbox-navigation", {
129
220
  "accessToken": "pk.xxx",
130
221
  "downloadsToken": "sk.xxx",
222
+ "mapboxMapsVersion": "11.11.0",
131
223
  "androidColorOverrides": {
132
224
  "mapbox_primary_route_color": "#0055FF",
133
225
  "mapbox_main_maneuver_background_color": "#FF5500"
@@ -137,83 +229,77 @@ All color props are optional — if not provided, the defaults below are applied
137
229
 
138
230
  ---
139
231
 
140
- ## Props
141
-
142
- | Prop | Type | Default | Description |
143
- |------|------|---------|-------------|
144
- | `coordinates` | `Coordinate[]` | required | Route waypoints. Min 2 items. |
145
- | `waypointIndices` | `number[]` | all points | Which coordinates are waypoints. |
146
- | `language` | `string` | device locale | BCP-47 locale (e.g. `"fr"`, `"en-US"`). |
147
- | `voiceUnits` | `"metric" \| "imperial"` | auto | **Fix #31** — Voice/distance units. |
148
- | `navigationProfile` | `string` | `"driving-traffic"` | Mapbox routing profile. |
149
- | `excludeTypes` | `string[]` | — | Road types to avoid. |
150
- | `mapStyle` | `string` | Mapbox Navigation Day | Map style URL. |
151
- | `mute` | `boolean` | `false` | Silence voice instructions. |
152
- | `maxHeight` | `number` | — | Max vehicle height (m). |
153
- | `maxWidth` | `number` | — | Max vehicle width (m). |
154
- | `useMapMatching` | `boolean` | `false` | Use Map Matching API. |
155
- | `customRasterTileUrl` | `string` | — | Custom tile URL with `{x}/{y}/{z}`. |
156
- | `customRasterAboveLayerId` | `string` | — | Layer ID to place custom raster above. |
157
-
158
- ---
159
-
160
232
  ## Events
161
233
 
162
234
  | Event | Payload | Description |
163
235
  |-------|---------|-------------|
164
- | `onRoutesReady` | `{ routeCount, distanceMeters, durationSeconds }` | Routes calculated. |
165
- | `onRouteProgressChanged` | `{ distanceRemaining, durationRemaining, ... }` | Progress update. |
166
- | `onArrival` | `{}` | User reached destination. |
167
- | `onNavigationCancelled` | `{}` | User cancelled navigation. |
168
- | `onNavigationFinished` | `{}` | Session ended normally. |
169
- | `onRoutesFailed` | `{ message }` | Route calculation failed. |
170
-
171
- ---
172
-
173
- ## Android Color Overrides
174
-
175
- Customize the Navigation UI colors by overriding Mapbox resource values:
176
-
177
- ```json
178
- ["@jacques_gordon/expo-mapbox-navigation", {
179
- "accessToken": "pk.your_token",
180
- "mapboxMapsVersion": "11.11.0",
181
- "androidColorOverrides": {
182
- "mapbox_main_maneuver_background_color": "#FF5500",
183
- "mapbox_primary_route_color": "#0055FF"
184
- }
185
- }]
236
+ | `onRoutesReady` | `{ routeCount, distanceMeters, durationSeconds }` | Fired when routes are calculated and navigation starts. |
237
+ | `onRouteProgressChanged` | `{ distanceRemaining, durationRemaining, distanceTraveled, fractionTraveled, currentStepDistanceRemaining }` | Fired on every GPS update during navigation. |
238
+ | `onArrival` | `{}` | User reached the destination. |
239
+ | `onNavigationCancelled` | `{}` | User tapped the cancel (✕) button. |
240
+ | `onNavigationFinished` | `{}` | Navigation session ended normally. |
241
+ | `onRoutesFailed` | `{ message: string }` | Route calculation failed. |
242
+ | `onManeuverBannerPressed` | `{ steps: RouteStep[] }` | Fired when user taps the instruction banner. Use to open a bottom sheet with the full steps list. |
243
+
244
+ ### RouteStep type
245
+
246
+ ```ts
247
+ interface RouteStep {
248
+ instruction: string; // "Turn left onto Main St"
249
+ distanceMeters: number;
250
+ durationSeconds: number;
251
+ maneuverType: string; // "turn", "merge", "roundabout", etc.
252
+ maneuverModifier: string; // "left", "right", "straight", etc.
253
+ roadName: string;
254
+ laneInstructions: {
255
+ active: boolean; // true = recommended lane
256
+ directions: string[]; // ["straight"], ["left", "straight"]
257
+ }[];
258
+ }
186
259
  ```
187
260
 
188
261
  ---
189
262
 
190
- ## 16 KB Page Size Compatibility
263
+ ## 16 KB Page Size (Android 15+)
191
264
 
192
- Android 15 (API 35) requires all `.so` native libraries to be aligned to 16 KB boundaries for devices using 16 KB memory page sizes.
265
+ This package enforces full compliance with Android's 16 KB memory page size requirement:
193
266
 
194
- This package enforces:
195
- - **NDK 27** (`27.0.12077973`)the first NDK version with full 16 KB support
196
- - **`jniLibs.useLegacyPackaging = false`**prevents `.so` compression, enabling proper alignment
197
- - **64-bit-only ABI filters** (`arm64-v8a`, `x86_64`) 16 KB requirement applies to 64-bit only
267
+ - **NDK 27** (`27.0.12077973`) — first NDK version with full 16 KB support
268
+ - **`jniLibs.useLegacyPackaging = false`**prevents `.so` compression, enables proper alignment
269
+ - **64-bit ABI filters** (`arm64-v8a`, `x86_64`) requirement applies to 64-bit only
270
+ - **NDK27 variant substitution** `dependencySubstitution` replaces all Mapbox Maven artifacts with their `-ndk27` equivalents across the entire dependency graph (including transitive deps from other packages)
198
271
 
199
- More info: [Android 16 KB page size guide](https://developer.android.com/guide/practices/page-sizes)
272
+ See [Android 16 KB page size guide](https://developer.android.com/guide/practices/page-sizes).
200
273
 
201
274
  ---
202
275
 
203
276
  ## Changelog
204
277
 
278
+ ### 2.2.8
279
+ - **iOS version strategy redesigned** — dynamic `mapboxNavigationVersion` calculation from `mapboxMapsVersion` minor. Prevents `MapboxCommon` version conflicts with `@rnmapbox/maps`. Pattern confirmed from real Mapbox releases: Navigation `3.N.x` always compatible with Maps `11.N.x`.
280
+ - **iOS: `mapboxNavigationVersion` optional param** — escape hatch to pin exact Navigation version.
281
+ - **iOS: post_install hook strengthened** — fallback `include?` target search + debug log of all available targets if `ExpoMapboxNavigation` not found.
282
+ - **Android color props fixed** — setters now apply immediately to views (were only stored previously, causing icon colors to remain default).
283
+
205
284
  ### 2.2.0
206
- - **iOS support added.** Full native implementation using Mapbox Navigation SDK v3 (`NavigationViewController` drop-in UI) via Swift Package Manager.
207
- - Fixed: previous versions referenced non-existent `.xcframework` files in the podspec, causing `Unimplemented component: ViewManagerAdapter_ExpoMapboxNavigation` crashes on iOS — no native module was ever actually registered.
208
- - New required `downloadsToken` config plugin option (secret Mapbox token, used to authenticate Swift Package Manager).
209
- - iOS feature parity with Android: lane guidance, speed limit, voice instructions (with working mute), day/night auto-switching, and the `onManeuverBannerPressed` full-steps-list event.
285
+ - **iOS support added** full native implementation using `NavigationViewController` drop-in UI.
286
+ - iOS SPM integration via `post_install` Podfile hook (Xcodeproj Ruby API, same technique as `@rnmapbox/maps`).
287
+ - `downloadsToken` required for iOS SPM authentication.
288
+ - Fixed previous phantom `.xcframework` references that caused `Unimplemented component` crashes.
289
+
290
+ ### 2.1.x
291
+ - Waze-style Android UI: maneuver banner, speed limit, ETA bar, action buttons (mute/overview/recenter).
292
+ - Voice instructions with TTS fallback.
293
+ - Puck jitter fix (GitHub issue #4140) — `keyPoints = emptyList()`.
294
+ - Lane guidance fix — explicit `bannerInstructions(true)`, `steps(true)`, `roundaboutExits(true)`.
295
+ - `onManeuverBannerPressed` event with full route steps list.
296
+ - Color customization props (Android).
210
297
 
211
298
  ### 2.0.1
212
- - Fix #43: `CameraAnimationsUtils.calculateCameraAnimationHint` NoSuchMethodError on Android
213
- - Fix #31: Add `voiceUnits` prop for metric/imperial voice instructions
214
- - Force NDK 27 for 16 KB page size support
215
- - Enforce Mapbox Maps Android ≥ 11.11.0
216
- - Expo SDK 53 compatibility
299
+ - Fix #43: `CameraAnimationsUtils.calculateCameraAnimationHint` crash on Android.
300
+ - Fix #31: `voiceUnits` prop for metric/imperial.
301
+ - NDK 27 + 16 KB page size enforcement.
302
+ - Expo SDK 53 compatibility.
217
303
 
218
304
  ---
219
305