@baeckerherz/expo-mapbox-navigation 0.1.6 → 0.1.7

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.
package/README.md CHANGED
@@ -1,35 +1,66 @@
1
1
  # @baeckerherz/expo-mapbox-navigation
2
2
 
3
- > **WARNING: This is a prototype and actively under development.** APIs may change without notice. Not recommended for production use yet. Contributions and feedback are welcome.
3
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
4
4
 
5
- Expo module wrapping [Mapbox Navigation SDK v3](https://docs.mapbox.com/ios/navigation/guides/) for iOS and Android. A clean, maintainable alternative to existing community wrappers.
5
+ Expo module wrapping [Mapbox Navigation SDK v3](https://docs.mapbox.com/ios/navigation/guides/) (iOS) / [Android](https://docs.mapbox.com/android/navigation/guides/) for turn-by-turn navigation on iOS and Android. A minimal, maintainable alternative to existing community wrappers.
6
6
 
7
- ## Why this exists
7
+ > **Warning:** This is a prototype under active development. APIs may change. Not recommended for production yet. Contributions and feedback are welcome.
8
8
 
9
- Existing React Native / Expo wrappers for Mapbox Navigation have significant issues:
9
+ ## Table of contents
10
10
 
11
- - **@badatgil/expo-mapbox-navigation**: Bundles vendored `.xcframework` files for iOS (fragile, requires manual rebuild for every Mapbox SDK update). Android assembles the navigation UI from ~30 individual components in ~1100 lines of Kotlin instead of using Mapbox's drop-in view.
12
- - **@homee/react-native-mapbox-navigation**: Abandoned (no activity since 2022), locked to Nav SDK v2.1.1, no Expo support, crashes on Android 13+.
11
+ - [Prerequisites](#prerequisites)
12
+ - [Installation](#installation)
13
+ - [Usage](#usage)
14
+ - [API](#api)
15
+ - [Architecture](#architecture)
16
+ - [Why this exists](#why-this-exists)
17
+ - [Comparison](#comparison)
18
+ - [Status](#status)
19
+ - [Contributing](#contributing)
20
+ - [License](#license)
21
+ - [Sponsors](#sponsors)
13
22
 
14
- ## Architecture
23
+ ## Prerequisites
15
24
 
16
- ### iOS: SPM via Config Plugin (not vendored xcframeworks)
25
+ - [Mapbox account](https://account.mapbox.com/) with Navigation SDK access
26
+ - **Public token** (`pk.xxx`) and **secret/download token** (`sk.xxx`)
27
+ - **iOS:** Add Mapbox credentials to `~/.netrc` for SPM:
17
28
 
18
- Mapbox Navigation SDK v3 for iOS dropped CocoaPods and only supports Swift Package Manager. Since Expo uses CocoaPods, we use an **Expo config plugin** that injects SPM package references into the Xcode project's `.pbxproj` at prebuild time. Version bumps are a single string change -- no manual xcframework rebuilding.
29
+ ```plaintext
30
+ machine api.mapbox.com
31
+ login mapbox
32
+ password YOUR_SECRET_TOKEN
33
+ ```
19
34
 
20
- ### Android: Drop-in NavigationView
35
+ - **Android:** The plugin writes `mapboxSecretToken` to `android/gradle.properties` as `MAPBOX_DOWNLOADS_TOKEN` so Maven can download the SDK. Use the same plugin config as for iOS.
21
36
 
22
- Android Nav SDK v3 provides a `NavigationView` drop-in component. We wrap it directly instead of rebuilding the UI from scratch.
37
+ ## Installation
23
38
 
24
- ### Both platforms: Expo Module API
39
+ ```bash
40
+ npx expo install @baeckerherz/expo-mapbox-navigation
41
+ ```
25
42
 
26
- Uses `expo-modules-core` for native bridging, giving us:
27
- - Fabric / New Architecture compatibility
28
- - Type-safe props and events in Swift/Kotlin
29
- - Clean `EventDispatcher` pattern
30
- - Works in Expo and bare RN projects
43
+ Add the plugin in `app.config.ts` (or `app.json`). Prefer environment variables for tokens so you don’t commit secrets:
31
44
 
32
- ## API
45
+ ```ts
46
+ plugins: [
47
+ ["@baeckerherz/expo-mapbox-navigation/plugin", {
48
+ mapboxAccessToken: process.env.MAPBOX_ACCESS_TOKEN,
49
+ mapboxSecretToken: process.env.MAPBOX_SECRET_TOKEN,
50
+ navigationSdkVersion: "3.5.0",
51
+ }],
52
+ ]
53
+ ```
54
+
55
+ Rebuild native projects:
56
+
57
+ ```bash
58
+ npx expo prebuild --clean
59
+ npx expo run:ios
60
+ npx expo run:android
61
+ ```
62
+
63
+ ## Usage
33
64
 
34
65
  ```tsx
35
66
  import { MapboxNavigation } from '@baeckerherz/expo-mapbox-navigation';
@@ -49,172 +80,118 @@ import { MapboxNavigation } from '@baeckerherz/expo-mapbox-navigation';
49
80
  />
50
81
  ```
51
82
 
83
+ ## API
84
+
52
85
  ### Props
53
86
 
54
- #### Route
87
+ **Route**
55
88
 
56
89
  | Prop | Type | Description |
57
90
  |------|------|-------------|
58
91
  | `coordinates` | `Array<{ latitude, longitude }>` | Route waypoints (min 2). First = origin, last = destination. |
59
- | `waypointIndices` | `number[]` | Indices into `coordinates` treated as full waypoints (with arrival notification). Others are silent via-points. Must include first and last index. |
60
- | `routeProfile` | `string` | Mapbox routing profile. iOS: `"mapbox/driving-traffic"` (default), `"mapbox/driving"`, `"mapbox/walking"`, `"mapbox/cycling"`. Android: omit the `"mapbox/"` prefix. |
92
+ | `waypointIndices` | `number[]` | Indices in `coordinates` that are full waypoints (with arrival notification). Others are via-points. Must include first and last. |
93
+ | `routeProfile` | `string` | Routing profile. iOS: `"mapbox/driving-traffic"` (default), `"mapbox/driving"`, `"mapbox/walking"`, `"mapbox/cycling"`. Android: omit `"mapbox/"` prefix. |
61
94
 
62
- #### Localization
95
+ **Localization**
63
96
 
64
97
  | Prop | Type | Description |
65
98
  |------|------|-------------|
66
- | `locale` | `string` | BCP 47 language tag for voice guidance, maneuver instructions, and UI labels (e.g. `"de"`, `"en-US"`). Default: device locale. |
99
+ | `locale` | `string` | BCP 47 language for voice and UI (e.g. `"de"`, `"en-US"`). Default: device locale. |
67
100
  | `mute` | `boolean` | Mute voice guidance. Default: `false`. |
68
101
 
69
- #### Appearance
102
+ **Appearance**
70
103
 
71
104
  | Prop | Type | Description |
72
105
  |------|------|-------------|
73
- | `mapStyle` | `string` | Custom Mapbox map style URL. Overrides the default navigation style. Example: `"mapbox://styles/mapbox/navigation-night-v1"`. |
74
- | `themeMode` | `"day" \| "night" \| "auto"` | Controls day/night map appearance. `"day"` (default): light map. `"night"`: dark 3D map. `"auto"`: switches based on time of day. |
75
- | `accentColor` | `string` | Primary accent color (hex). Applied to route line, floating buttons, and interactive elements. Example: `"#007AFF"`. |
76
- | `routeColor` | `string` | Route line color (hex). Overrides `accentColor` for the route line only. Example: `"#4264fb"`. |
77
- | `bannerBackgroundColor` | `string` | Instruction banner background color (hex). Example: `"#FFFFFF"` for light, `"#1A1A2E"` for dark. |
78
- | `bannerTextColor` | `string` | Instruction banner text color (hex). Example: `"#000000"`. |
106
+ | `mapStyle` | `string` | Mapbox style URL. Example: `"mapbox://styles/mapbox/navigation-night-v1"`. |
107
+ | `themeMode` | `"day" \| "night" \| "auto"` | Day (default), night, or auto by time. |
108
+ | `accentColor` | `string` | Primary accent (hex). Example: `"#007AFF"`. |
109
+ | `routeColor` | `string` | Route line color (hex). Overrides `accentColor` for the line. |
110
+ | `bannerBackgroundColor` | `string` | Instruction banner background (hex). |
111
+ | `bannerTextColor` | `string` | Instruction banner text (hex). |
79
112
 
80
113
  ### Events
81
114
 
82
115
  | Event | Payload | Description |
83
116
  |-------|---------|-------------|
84
- | `onRouteProgressChanged` | `{ distanceRemaining, durationRemaining, distanceTraveled, fractionTraveled }` | Fires as the user progresses along the route. |
85
- | `onCancelNavigation` | — | User tapped cancel / back. |
86
- | `onWaypointArrival` | `{ waypointIndex }` | Arrived at an intermediate waypoint. |
87
- | `onFinalDestinationArrival` | — | Arrived at the final destination. |
88
- | `onRouteChanged` | — | Route was recalculated (reroute). |
89
- | `onUserOffRoute` | — | User went off the planned route. |
90
- | `onError` | `{ message }` | Navigation error occurred. |
91
-
92
- ## Prerequisites
93
-
94
- 1. A [Mapbox account](https://account.mapbox.com/) with Navigation SDK access
95
- 2. A public access token (`pk.xxx`) and a secret/download token (`sk.xxx`)
96
- 3. For iOS: `~/.netrc` must contain Mapbox credentials for SPM package resolution:
97
-
98
- ```
99
- machine api.mapbox.com
100
- login mapbox
101
- password sk.eyJ1...YOUR_SECRET_TOKEN
102
- ```
103
-
104
- ## Installation
105
-
106
- ```bash
107
- npx expo install @baeckerherz/expo-mapbox-navigation
108
- ```
109
-
110
- Add the plugin to `app.config.ts`:
111
-
112
- ```ts
113
- plugins: [
114
- ["@baeckerherz/expo-mapbox-navigation/plugin", {
115
- mapboxAccessToken: "pk.eyJ1...",
116
- mapboxSecretToken: "sk.eyJ1...", // for SPM download auth
117
- navigationSdkVersion: "3.5.0",
118
- }],
119
- ]
120
- ```
121
-
122
- Rebuild native:
123
-
124
- ```bash
125
- npx expo prebuild --clean
126
- npx expo run:ios
127
- ```
128
-
129
- ## Contributing
117
+ | `onRouteProgressChanged` | `{ distanceRemaining, durationRemaining, distanceTraveled, fractionTraveled }` | Progress along the route. |
118
+ | `onCancelNavigation` | — | User cancelled. |
119
+ | `onWaypointArrival` | `{ waypointIndex }` | Reached an intermediate waypoint. |
120
+ | `onFinalDestinationArrival` | — | Reached final destination. |
121
+ | `onRouteChanged` | — | Route recalculated (reroute). |
122
+ | `onUserOffRoute` | — | User left the route. |
123
+ | `onError` | `{ message }` | Navigation error. |
130
124
 
131
- ### Project Structure
125
+ ## Architecture
132
126
 
133
- ```
134
- src/ TypeScript API (component, types, exports)
135
- ios/ Swift native module + podspec
136
- android/ Kotlin native module + build.gradle
137
- plugin/ Expo config plugins (SPM injection, Gradle setup)
138
- example/ Test app
139
- ```
127
+ **iOS — SPM via config plugin**
128
+ Mapbox Navigation SDK v3 is SPM-only. The Expo config plugin injects SPM package references into the Xcode project at prebuild. Version bumps are a single string change; no vendored xcframeworks.
140
129
 
141
- ### Local Development
130
+ **Android Drop-in NavigationView**
131
+ We wrap the Nav SDK v3 `NavigationView` directly instead of rebuilding the UI.
142
132
 
143
- ```bash
144
- git clone https://github.com/baeckerherz/expo-mapbox-navigation.git
145
- cd expo-mapbox-navigation
146
- yarn install
147
- ```
133
+ **Both — Expo Module API**
134
+ `expo-modules-core` for native bridging: Fabric/New Architecture, type-safe props and events, and compatibility with Expo and bare React Native.
148
135
 
149
- ### Running the Example App
136
+ ## Why this exists
150
137
 
151
- ```bash
152
- cd example
153
- yarn install
154
- npx expo prebuild --clean
155
- npx expo run:ios --device
156
- ```
138
+ Existing wrappers have major drawbacks:
157
139
 
158
- The example app navigates from your current location to Innsbruck Hauptbahnhof with German voice guidance.
140
+ - **@badatgil/expo-mapbox-navigation:** Vendored `.xcframework` on iOS (fragile; manual rebuild per SDK update). Android uses ~30 custom Kotlin components (~1100 LOC) instead of Mapbox’s drop-in view.
141
+ - **@homee/react-native-mapbox-navigation:** Unmaintained (no activity since 2022), Nav SDK v2.1.1, no Expo, crashes on Android 13+.
159
142
 
160
- ## Key differences from existing wrappers
143
+ ## Comparison
161
144
 
162
145
  | | This module | @badatgil/expo-mapbox-navigation | @homee/react-native-mapbox-navigation |
163
146
  |---|---|---|---|
164
- | iOS SDK integration | SPM via config plugin (clean version bumps) | Vendored .xcframeworks (manual rebuild per update) | CocoaPods pinned to Nav SDK v2 |
165
- | Android approach | Drop-in NavigationView | Custom UI from ~30 components (~1100 LOC) | Custom UI (~500 LOC) |
166
- | Nav SDK version | v3 (current) | v3 | v2 (legacy) |
167
- | Expo Module API | Yes (Fabric-ready) | Yes | No (legacy bridge) |
168
- | Multi-waypoint | Yes | Yes | No (origin + destination only) |
147
+ | iOS | SPM via config plugin | Vendored .xcframeworks | CocoaPods, Nav SDK v2 |
148
+ | Android | Drop-in NavigationView | Custom UI (~1100 LOC) | Custom UI (~500 LOC) |
149
+ | Nav SDK | v3 | v3 | v2 (legacy) |
150
+ | Expo Module API | Yes (Fabric-ready) | Yes | No |
151
+ | Multi-waypoint | Yes | Yes | No |
169
152
  | Maintenance | Active | Semi-active | Abandoned |
170
153
 
171
154
  ## Status
172
155
 
173
- **Prototype** — not production-ready. This is a proof of concept to validate:
156
+ **Prototype** — not production-ready. Goals:
174
157
 
175
- 1. SPM injection via config plugin works reliably with Xcode + CocoaPods
176
- 2. Drop-in NavigationView/NavigationViewController integration is sufficient
177
- 3. Event bridging covers the required use cases
158
+ 1. Reliable SPM injection with Xcode + CocoaPods
159
+ 2. Sufficient drop-in NavigationView/NavigationViewController integration
160
+ 3. Event bridging for required use cases
178
161
 
179
- ### Known risks
162
+ **Known risks**
180
163
 
181
- - **Android NavigationView completeness**: The drop-in `NavigationView` in Android Nav SDK v3 may require additional configuration for full feature parity with iOS.
182
- - **Mapbox licensing**: The Navigation SDK requires a commercial Mapbox license for production use. This wrapper does not change that requirement.
164
+ - **Android:** Drop-in `NavigationView` may need more config for full parity with iOS.
165
+ - **Licensing:** Mapbox Navigation SDK requires a commercial Mapbox license; this wrapper does not change that.
183
166
 
184
- ---
167
+ ## Contributing
185
168
 
186
- ## Contributors
169
+ We welcome contributors and maintainers. If you work on Expo native modules, Mapbox SDKs, or React Native tooling, we’d love your help.
187
170
 
188
- We're looking for **contributors and maintainers** to help shape this project. If you're interested in working on Expo native modules, Mapbox SDKs, or React Native tooling — we'd love to have you on board.
171
+ **Project layout:** `src/` (TypeScript API), `ios/` (Swift + podspec), `android/` (Kotlin + build.gradle), `plugin/` (Expo config plugins), `example/` (test app).
189
172
 
190
- Check out the [issues](https://github.com/baeckerherz/expo-mapbox-navigation/issues) or open a PR to get started.
173
+ **Run the example:**
191
174
 
192
- ---
175
+ ```bash
176
+ git clone https://github.com/baeckerherz/expo-mapbox-navigation.git
177
+ cd expo-mapbox-navigation && yarn install
178
+ cd example && yarn install
179
+ npx expo prebuild --clean
180
+ npx expo run:ios --device # or: npx expo run:android
181
+ ```
193
182
 
194
- ## Sponsors
183
+ The example navigates from your location to Innsbruck Hauptbahnhof with German voice guidance.
195
184
 
196
- This project is sponsored and maintained by the teams that use it in production.
185
+ Open an [issue](https://github.com/baeckerherz/expo-mapbox-navigation/issues) or submit a PR to get started.
197
186
 
198
- <table>
199
- <tr>
200
- <td align="center">
201
- <a href="https://github.com/baeckerherz">
202
- <img src="https://avatars.githubusercontent.com/u/261656164?s=200&v=4" width="100" alt="Bäckerherz" />
203
- <br />
204
- <strong>Bäckerherz</strong>
205
- </a>
206
- <br />
207
- <em>Founding Sponsor</em>
208
- </td>
209
- </tr>
210
- </table>
187
+ ## License
211
188
 
212
- Bäckerherz builds and uses this module in their own apps. This project exists because of their investment in open-source tooling for the Expo ecosystem.
189
+ [MIT](./LICENSE)
213
190
 
214
- **Become a sponsor** — help us keep this project maintained and growing. Reach out at [partner@baeckerherz.at](mailto:partner@baeckerherz.at).
191
+ ## Sponsors
215
192
 
216
- ---
193
+ Sponsored and maintained by teams that use it in production.
217
194
 
218
- ## Need help building something?
195
+ <a href="https://github.com/baeckerherz"><img src="https://avatars.githubusercontent.com/u/261656164?s=80&v=4" width="48" alt="Bäckerherz" /></a> **[Bäckerherz](https://github.com/baeckerherz)** — Founding sponsor. They build and use this module; the project exists thanks to their investment in open-source Expo tooling.
219
196
 
220
- We build mobile apps, backend systems, and custom integrations using Expo, React Native, and more. If you need a team that ships — get in touch at [partner@baeckerherz.at](mailto:partner@baeckerherz.at).
197
+ To support the project or work with us: [partner@baeckerherz.at](mailto:partner@baeckerherz.at).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baeckerherz/expo-mapbox-navigation",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Expo module wrapping Mapbox Navigation SDK v3 for iOS and Android",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -4,6 +4,7 @@ const config_plugins_1 = require("@expo/config-plugins");
4
4
  const withMapboxNavSPM_1 = require("./withMapboxNavSPM");
5
5
  const withMapboxNavPodfile_1 = require("./withMapboxNavPodfile");
6
6
  const withMapboxNavGradle_1 = require("./withMapboxNavGradle");
7
+ const withMapboxNavGradleProperties_1 = require("./withMapboxNavGradleProperties");
7
8
  const withMapboxNavigation = (config, { mapboxAccessToken, mapboxSecretToken, navigationSdkVersion = "3.5.0", }) => {
8
9
  if (!mapboxAccessToken) {
9
10
  throw new Error("[@baeckerherz/expo-mapbox-navigation] mapboxAccessToken is required.");
@@ -29,8 +30,9 @@ const withMapboxNavigation = (config, { mapboxAccessToken, mapboxSecretToken, na
29
30
  config = (0, withMapboxNavSPM_1.withMapboxNavSPM)(config, { navigationSdkVersion });
30
31
  // iOS: Patch Podfile for SPM framework visibility
31
32
  config = (0, withMapboxNavPodfile_1.withMapboxNavPodfile)(config);
32
- // Android: Add Mapbox Maven repository
33
+ // Android: Mapbox Maven repository and optional token for SDK download
33
34
  config = (0, withMapboxNavGradle_1.withMapboxNavGradle)(config);
35
+ config = (0, withMapboxNavGradleProperties_1.withMapboxNavGradleProperties)(config, { mapboxSecretToken });
34
36
  return config;
35
37
  };
36
38
  exports.default = (0, config_plugins_1.createRunOncePlugin)(withMapboxNavigation, "@baeckerherz/expo-mapbox-navigation", "0.1.0");
@@ -0,0 +1,8 @@
1
+ import { ConfigPlugin } from "@expo/config-plugins";
2
+ /**
3
+ * Injects MAPBOX_DOWNLOADS_TOKEN into android/gradle.properties so the
4
+ * Mapbox Maven repository can authenticate when resolving Navigation SDK.
5
+ */
6
+ export declare const withMapboxNavGradleProperties: ConfigPlugin<{
7
+ mapboxSecretToken?: string;
8
+ }>;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withMapboxNavGradleProperties = void 0;
4
+ const config_plugins_1 = require("@expo/config-plugins");
5
+ /**
6
+ * Injects MAPBOX_DOWNLOADS_TOKEN into android/gradle.properties so the
7
+ * Mapbox Maven repository can authenticate when resolving Navigation SDK.
8
+ */
9
+ const withMapboxNavGradleProperties = (config, { mapboxSecretToken }) => {
10
+ if (!mapboxSecretToken)
11
+ return config;
12
+ return (0, config_plugins_1.withGradleProperties)(config, (config) => {
13
+ const key = "MAPBOX_DOWNLOADS_TOKEN";
14
+ const existing = config.modResults.find((item) => item.type === "property" && item.key === key);
15
+ if (existing && existing.type === "property") {
16
+ existing.value = mapboxSecretToken;
17
+ }
18
+ else {
19
+ config.modResults.push({
20
+ type: "property",
21
+ key,
22
+ value: mapboxSecretToken,
23
+ });
24
+ }
25
+ return config;
26
+ });
27
+ };
28
+ exports.withMapboxNavGradleProperties = withMapboxNavGradleProperties;
@@ -2,6 +2,7 @@ import { ConfigPlugin, createRunOncePlugin } from "@expo/config-plugins";
2
2
  import { withMapboxNavSPM } from "./withMapboxNavSPM";
3
3
  import { withMapboxNavPodfile } from "./withMapboxNavPodfile";
4
4
  import { withMapboxNavGradle } from "./withMapboxNavGradle";
5
+ import { withMapboxNavGradleProperties } from "./withMapboxNavGradleProperties";
5
6
 
6
7
  interface PluginConfig {
7
8
  /** Mapbox public access token (pk.xxx). Required for the native SDK. */
@@ -50,8 +51,9 @@ const withMapboxNavigation: ConfigPlugin<PluginConfig> = (
50
51
  // iOS: Patch Podfile for SPM framework visibility
51
52
  config = withMapboxNavPodfile(config);
52
53
 
53
- // Android: Add Mapbox Maven repository
54
+ // Android: Mapbox Maven repository and optional token for SDK download
54
55
  config = withMapboxNavGradle(config);
56
+ config = withMapboxNavGradleProperties(config, { mapboxSecretToken });
55
57
 
56
58
  return config;
57
59
  };
@@ -0,0 +1,28 @@
1
+ import { withGradleProperties, ConfigPlugin } from "@expo/config-plugins";
2
+
3
+ /**
4
+ * Injects MAPBOX_DOWNLOADS_TOKEN into android/gradle.properties so the
5
+ * Mapbox Maven repository can authenticate when resolving Navigation SDK.
6
+ */
7
+ export const withMapboxNavGradleProperties: ConfigPlugin<{
8
+ mapboxSecretToken?: string;
9
+ }> = (config, { mapboxSecretToken }) => {
10
+ if (!mapboxSecretToken) return config;
11
+
12
+ return withGradleProperties(config, (config) => {
13
+ const key = "MAPBOX_DOWNLOADS_TOKEN";
14
+ const existing = config.modResults.find(
15
+ (item) => item.type === "property" && item.key === key
16
+ );
17
+ if (existing && existing.type === "property") {
18
+ existing.value = mapboxSecretToken;
19
+ } else {
20
+ config.modResults.push({
21
+ type: "property",
22
+ key,
23
+ value: mapboxSecretToken,
24
+ });
25
+ }
26
+ return config;
27
+ });
28
+ };