@jacques_gordon/expo-mapbox-navigation 2.2.5 → 2.2.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/ExpoMapboxNavigation.podspec +58 -25
- package/android/src/main/java/expo/modules/mapboxnavigation/ExpoMapboxNavigationView.kt +47 -5
- package/app.plugin.js +137 -221
- package/ios/ExpoMapboxNavigationModule.swift +49 -27
- package/ios/ExpoMapboxNavigationView.swift +122 -116
- package/ios/Package.swift +69 -0
- package/ios/build-xcframeworks.sh +118 -0
- package/package.json +1 -1
- package/plugin/src/index.js +137 -221
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
3
|
+
# build-xcframeworks.sh
|
|
4
|
+
# Builds xcframeworks for the ExpoMapboxNavigation module.
|
|
5
|
+
#
|
|
6
|
+
# PREREQUISITES:
|
|
7
|
+
# 1. macOS + Xcode 16+
|
|
8
|
+
# 2. ~/.netrc configured with your Mapbox Downloads token:
|
|
9
|
+
# machine api.mapbox.com
|
|
10
|
+
# login mapbox
|
|
11
|
+
# password sk.your_downloads_token
|
|
12
|
+
# 3. Swift Package Manager available (comes with Xcode)
|
|
13
|
+
# 4. Scipio installed (https://github.com/giginet/Scipio):
|
|
14
|
+
# swift package --disable-sandbox experimental-publish-xcbundles
|
|
15
|
+
# OR install via Homebrew: brew install giginet/scipio/scipio
|
|
16
|
+
#
|
|
17
|
+
# This script builds MapboxNavigationCore and MapboxNavigationUIKit
|
|
18
|
+
# (and their dependencies) as xcframeworks and copies them into the
|
|
19
|
+
# ios/Frameworks/ directory, which is referenced by ExpoMapboxNavigation.podspec.
|
|
20
|
+
#
|
|
21
|
+
# Based on the approach by youssefhenna/expo-mapbox-navigation:
|
|
22
|
+
# https://github.com/uju777/expo-mapbox-navigation#getting-the-xcframework-files
|
|
23
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
set -e
|
|
26
|
+
|
|
27
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
28
|
+
MODULE_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
29
|
+
FRAMEWORKS_DIR="$SCRIPT_DIR/Frameworks"
|
|
30
|
+
|
|
31
|
+
# ── Config ────────────────────────────────────────────────────────────────────
|
|
32
|
+
MAPBOX_NAV_VERSION="3.25.0"
|
|
33
|
+
MAPBOX_MAPS_VERSION="11.25.0"
|
|
34
|
+
MAPBOX_COMMON_VERSION="24.25.0"
|
|
35
|
+
MAPBOX_NAV_NATIVE_VERSION="324.25.0"
|
|
36
|
+
|
|
37
|
+
echo "🔧 Building xcframeworks for Mapbox Navigation SDK v$MAPBOX_NAV_VERSION"
|
|
38
|
+
echo " Output: $FRAMEWORKS_DIR"
|
|
39
|
+
echo ""
|
|
40
|
+
|
|
41
|
+
# ── Step 1: Clone mapbox-navigation-ios ──────────────────────────────────────
|
|
42
|
+
TMPDIR=$(mktemp -d)
|
|
43
|
+
echo "📦 Cloning mapbox-navigation-ios v$MAPBOX_NAV_VERSION..."
|
|
44
|
+
git clone --branch "v$MAPBOX_NAV_VERSION" --depth 1 \
|
|
45
|
+
https://github.com/mapbox/mapbox-navigation-ios.git \
|
|
46
|
+
"$TMPDIR/mapbox-navigation-ios"
|
|
47
|
+
|
|
48
|
+
cd "$TMPDIR/mapbox-navigation-ios"
|
|
49
|
+
|
|
50
|
+
# ── Step 2: Replace Package.swift with the modified version ──────────────────
|
|
51
|
+
echo "📝 Patching Package.swift..."
|
|
52
|
+
cp "$SCRIPT_DIR/Package.swift" Package.swift
|
|
53
|
+
|
|
54
|
+
# ── Step 3: Get the correct navNative checksum ───────────────────────────────
|
|
55
|
+
echo "🔍 Resolving navNative checksum (this may take a moment)..."
|
|
56
|
+
# Run swift build once to get the correct checksum — it will fail and print it
|
|
57
|
+
CHECKSUM=$(swift build -c release 2>&1 | grep -oE '"[a-f0-9]{64}"' | head -1 | tr -d '"' || true)
|
|
58
|
+
|
|
59
|
+
if [ -z "$CHECKSUM" ]; then
|
|
60
|
+
echo "⚠️ Could not auto-detect checksum. Please run manually and update Package.swift."
|
|
61
|
+
else
|
|
62
|
+
echo " Checksum: $CHECKSUM"
|
|
63
|
+
sed -i '' "s/placeholder_run_swift_build_to_get_real_checksum/$CHECKSUM/g" Package.swift
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# ── Step 4: Build xcframeworks with Scipio ────────────────────────────────────
|
|
67
|
+
echo ""
|
|
68
|
+
echo "🏗️ Building xcframeworks with Scipio..."
|
|
69
|
+
echo " This will take 10-30 minutes on first run."
|
|
70
|
+
echo ""
|
|
71
|
+
|
|
72
|
+
# Clone Scipio inside the navigation-ios repo
|
|
73
|
+
git clone --depth 1 https://github.com/giginet/Scipio.git Scipio
|
|
74
|
+
cd Scipio
|
|
75
|
+
swift build -c release
|
|
76
|
+
|
|
77
|
+
# Build the xcframeworks
|
|
78
|
+
cd "$TMPDIR/mapbox-navigation-ios"
|
|
79
|
+
Scipio/.build/release/scipio create ./ -f \
|
|
80
|
+
--platforms iOS \
|
|
81
|
+
--only-use-versions-from-resolved-file \
|
|
82
|
+
--enable-library-evolution \
|
|
83
|
+
--support-simulators \
|
|
84
|
+
--embed-debug-symbols \
|
|
85
|
+
--verbose
|
|
86
|
+
|
|
87
|
+
# ── Step 5: Copy to module ────────────────────────────────────────────────────
|
|
88
|
+
echo ""
|
|
89
|
+
echo "📋 Copying xcframeworks to $FRAMEWORKS_DIR..."
|
|
90
|
+
mkdir -p "$FRAMEWORKS_DIR"
|
|
91
|
+
|
|
92
|
+
BUILT_FRAMEWORKS="$TMPDIR/mapbox-navigation-ios/XCFrameworks"
|
|
93
|
+
|
|
94
|
+
# Copy the frameworks we need (others like MapboxMaps come from @rnmapbox/maps)
|
|
95
|
+
NEEDED_FRAMEWORKS=(
|
|
96
|
+
"MapboxNavigationCore"
|
|
97
|
+
"MapboxNavigationUIKit"
|
|
98
|
+
"MapboxNavigationNative"
|
|
99
|
+
"MapboxDirections"
|
|
100
|
+
"_MapboxNavigationHelpers"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
for fw in "${NEEDED_FRAMEWORKS[@]}"; do
|
|
104
|
+
if [ -d "$BUILT_FRAMEWORKS/$fw.xcframework" ]; then
|
|
105
|
+
echo " ✅ $fw.xcframework"
|
|
106
|
+
cp -R "$BUILT_FRAMEWORKS/$fw.xcframework" "$FRAMEWORKS_DIR/"
|
|
107
|
+
else
|
|
108
|
+
echo " ❌ $fw.xcframework not found in $BUILT_FRAMEWORKS"
|
|
109
|
+
fi
|
|
110
|
+
done
|
|
111
|
+
|
|
112
|
+
# ── Cleanup ────────────────────────────────────────────────────────────────────
|
|
113
|
+
cd /
|
|
114
|
+
rm -rf "$TMPDIR"
|
|
115
|
+
|
|
116
|
+
echo ""
|
|
117
|
+
echo "✅ Done! xcframeworks are in $FRAMEWORKS_DIR"
|
|
118
|
+
echo " Commit the Frameworks/ directory and publish the package."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jacques_gordon/expo-mapbox-navigation",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.7",
|
|
4
4
|
"description": "Expo module for Mapbox Navigation SDK with 16KB page size support, NDK27, and Mapbox Maps v11.11.0+",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
package/plugin/src/index.js
CHANGED
|
@@ -1,201 +1,54 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
withAppBuildGradle,
|
|
3
|
+
withAndroidManifest,
|
|
4
|
+
withInfoPlist,
|
|
5
|
+
withDangerousMod,
|
|
6
|
+
} = require('@expo/config-plugins');
|
|
2
7
|
const fs = require('fs');
|
|
3
8
|
const path = require('path');
|
|
4
9
|
|
|
5
|
-
const NDK_VERSION = '27.0.12077973';
|
|
6
|
-
const MAPBOX_MAPS_MIN_VERSION = '11.11.0';
|
|
7
|
-
|
|
8
10
|
const withMapboxNavigation = (config, options = {}) => {
|
|
9
11
|
const {
|
|
10
12
|
accessToken,
|
|
11
13
|
downloadsToken,
|
|
12
|
-
mapboxMapsVersion =
|
|
14
|
+
mapboxMapsVersion = '11.11.0',
|
|
13
15
|
androidColorOverrides = {},
|
|
14
16
|
} = options;
|
|
15
17
|
|
|
16
18
|
if (!accessToken) {
|
|
17
19
|
throw new Error(
|
|
18
|
-
'[@jacques_gordon/expo-mapbox-navigation] `accessToken` is required
|
|
19
|
-
'
|
|
20
|
-
' ["@jacques_gordon/expo-mapbox-navigation", { "accessToken": "pk.your_token" }]'
|
|
20
|
+
'[@jacques_gordon/expo-mapbox-navigation] `accessToken` is required.\n' +
|
|
21
|
+
' ["@jacques_gordon/expo-mapbox-navigation", { "accessToken": "pk.xxx" }]'
|
|
21
22
|
);
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
// downloadsToken (a secret sk.* token with the Downloads:Read scope) is
|
|
25
|
-
// required on iOS to authenticate Swift Package Manager's fetch of the
|
|
26
|
-
// Mapbox Navigation SDK package — without it, `pod install`/`expo
|
|
27
|
-
// prebuild` will fail to resolve the SPM dependency declared in
|
|
28
|
-
// ExpoMapboxNavigation.podspec.
|
|
29
25
|
if (!downloadsToken) {
|
|
30
26
|
throw new Error(
|
|
31
|
-
'[@jacques_gordon/expo-mapbox-navigation] `downloadsToken` is required for iOS
|
|
32
|
-
'This must be a SECRET Mapbox token (starts with "sk.") with the "Downloads:Read" scope,\n' +
|
|
33
|
-
'used to authenticate Swift Package Manager when fetching the Mapbox Navigation SDK.\n' +
|
|
34
|
-
'Add it to your app.json plugins array:\n' +
|
|
27
|
+
'[@jacques_gordon/expo-mapbox-navigation] `downloadsToken` (secret sk.* token) is required for iOS.\n' +
|
|
35
28
|
' ["@jacques_gordon/expo-mapbox-navigation", { "accessToken": "pk.xxx", "downloadsToken": "sk.xxx" }]'
|
|
36
29
|
);
|
|
37
30
|
}
|
|
38
31
|
|
|
39
|
-
|
|
40
|
-
if (major < 11 || (major === 11 && minor < 11)) {
|
|
41
|
-
throw new Error(
|
|
42
|
-
`[@jacques_gordon/expo-mapbox-navigation] mapboxMapsVersion must be >= 11.11.0.\n` +
|
|
43
|
-
`Provided: ${mapboxMapsVersion}`
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// ── Android: project-level build.gradle ─────────────────────────────────
|
|
48
|
-
config = withProjectBuildGradle(config, (mod) => {
|
|
49
|
-
let contents = mod.modResults.contents;
|
|
50
|
-
|
|
51
|
-
if (!contents.includes('api.mapbox.com/downloads/v2/releases/maven')) {
|
|
52
|
-
const mavenBlock = `
|
|
53
|
-
maven {
|
|
54
|
-
url 'https://api.mapbox.com/downloads/v2/releases/maven'
|
|
55
|
-
authentication { basic(BasicAuthentication) }
|
|
56
|
-
credentials {
|
|
57
|
-
username = 'mapbox'
|
|
58
|
-
password = project.hasProperty('MAPBOX_DOWNLOADS_TOKEN')
|
|
59
|
-
? project.property('MAPBOX_DOWNLOADS_TOKEN')
|
|
60
|
-
: System.getenv('MAPBOX_DOWNLOADS_TOKEN') ?: ""
|
|
61
|
-
}
|
|
62
|
-
}`;
|
|
63
|
-
if (contents.includes('allprojects') && contents.includes('repositories')) {
|
|
64
|
-
contents = contents.replace(
|
|
65
|
-
/allprojects\s*\{[\s\S]*?repositories\s*\{/,
|
|
66
|
-
(match) => match + mavenBlock
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
mod.modResults.contents = contents;
|
|
72
|
-
return mod;
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// ── Android: app-level build.gradle ─────────────────────────────────────
|
|
32
|
+
// ── Android ───────────────────────────────────────────────────────────────
|
|
76
33
|
config = withAppBuildGradle(config, (mod) => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (!contents.includes('ndkVersion')) {
|
|
80
|
-
contents = contents.replace(
|
|
81
|
-
/android\s*\{/,
|
|
82
|
-
`android {\n ndkVersion "${NDK_VERSION}"`
|
|
83
|
-
);
|
|
84
|
-
} else if (!contents.includes(NDK_VERSION)) {
|
|
85
|
-
contents = contents.replace(
|
|
86
|
-
/ndkVersion\s+["'][^"']*["']/,
|
|
87
|
-
`ndkVersion "${NDK_VERSION}"`
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (!contents.includes('useLegacyPackaging')) {
|
|
92
|
-
contents = contents.replace(
|
|
93
|
-
/android\s*\{/,
|
|
94
|
-
`android {\n packagingOptions {\n jniLibs {\n useLegacyPackaging = false\n }\n }`
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const resolutionBlock = `
|
|
99
|
-
configurations.all {
|
|
100
|
-
resolutionStrategy {
|
|
101
|
-
force "com.mapbox.maps:android:${mapboxMapsVersion}"
|
|
102
|
-
force "com.mapbox.maps:android-ndk27:${mapboxMapsVersion}"
|
|
103
|
-
}
|
|
104
|
-
}`;
|
|
105
|
-
if (!contents.includes('com.mapbox.maps:android:')) {
|
|
106
|
-
contents = contents + '\n' + resolutionBlock;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
mod.modResults.contents = contents;
|
|
34
|
+
addAndroidConfig(mod, mapboxMapsVersion, androidColorOverrides);
|
|
110
35
|
return mod;
|
|
111
36
|
});
|
|
112
37
|
|
|
113
|
-
// ── Android: AndroidManifest ─────────────────────────────────────────────
|
|
114
38
|
config = withAndroidManifest(config, (mod) => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// ── Permissions required by Mapbox Navigation SDK (targetSdk 35) ────────
|
|
118
|
-
//
|
|
119
|
-
// SecurityException: Starting FGS with type location requires:
|
|
120
|
-
// - android.permission.FOREGROUND_SERVICE_LOCATION (mandatory API 34+)
|
|
121
|
-
// - android.permission.FOREGROUND_SERVICE (mandatory)
|
|
122
|
-
// - android.permission.ACCESS_FINE_LOCATION (at least one of coarse/fine)
|
|
123
|
-
// - android.permission.ACCESS_COARSE_LOCATION
|
|
124
|
-
//
|
|
125
|
-
const requiredPermissions = [
|
|
126
|
-
'android.permission.ACCESS_FINE_LOCATION',
|
|
127
|
-
'android.permission.ACCESS_COARSE_LOCATION',
|
|
128
|
-
'android.permission.FOREGROUND_SERVICE',
|
|
129
|
-
'android.permission.FOREGROUND_SERVICE_LOCATION', // ← fixes the crash
|
|
130
|
-
'android.permission.POST_NOTIFICATIONS', // needed for nav notification (API 33+)
|
|
131
|
-
];
|
|
132
|
-
|
|
133
|
-
if (!manifest['uses-permission']) {
|
|
134
|
-
manifest['uses-permission'] = [];
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
for (const permission of requiredPermissions) {
|
|
138
|
-
const already = manifest['uses-permission'].some(
|
|
139
|
-
(p) => p.$?.['android:name'] === permission
|
|
140
|
-
);
|
|
141
|
-
if (!already) {
|
|
142
|
-
manifest['uses-permission'].push({ $: { 'android:name': permission } });
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// ── Mapbox access token meta-data ────────────────────────────────────────
|
|
147
|
-
const mainApp = manifest.application?.[0];
|
|
148
|
-
if (mainApp) {
|
|
149
|
-
if (!mainApp['meta-data']) mainApp['meta-data'] = [];
|
|
150
|
-
const tokenMeta = mainApp['meta-data'].find(
|
|
151
|
-
(m) => m.$?.['android:name'] === 'com.mapbox.token'
|
|
152
|
-
);
|
|
153
|
-
if (!tokenMeta) {
|
|
154
|
-
mainApp['meta-data'].push({
|
|
155
|
-
$: { 'android:name': 'com.mapbox.token', 'android:value': accessToken },
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
39
|
+
addAndroidPermissions(mod, accessToken);
|
|
160
40
|
return mod;
|
|
161
41
|
});
|
|
162
42
|
|
|
163
|
-
// ──
|
|
164
|
-
if (Object.keys(androidColorOverrides).length > 0) {
|
|
165
|
-
config = withDangerousMod(config, [
|
|
166
|
-
'android',
|
|
167
|
-
async (mod) => {
|
|
168
|
-
const colorsDir = path.join(
|
|
169
|
-
mod.modRequest.platformProjectRoot,
|
|
170
|
-
'app/src/main/res/values'
|
|
171
|
-
);
|
|
172
|
-
if (!fs.existsSync(colorsDir)) fs.mkdirSync(colorsDir, { recursive: true });
|
|
173
|
-
|
|
174
|
-
const colorEntries = Object.entries(androidColorOverrides)
|
|
175
|
-
.map(([name, value]) => ` <color name="${name}">${value}</color>`)
|
|
176
|
-
.join('\n');
|
|
177
|
-
|
|
178
|
-
fs.writeFileSync(
|
|
179
|
-
path.join(colorsDir, 'mapbox_navigation_colors.xml'),
|
|
180
|
-
`<?xml version="1.0" encoding="utf-8"?>\n<resources>\n${colorEntries}\n</resources>\n`
|
|
181
|
-
);
|
|
182
|
-
return mod;
|
|
183
|
-
},
|
|
184
|
-
]);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// ── iOS: Info.plist ──────────────────────────────────────────────────────
|
|
43
|
+
// ── iOS: Info.plist — MBXAccessToken + permissions ────────────────────────
|
|
188
44
|
config = withInfoPlist(config, (mod) => {
|
|
189
45
|
mod.modResults.MBXAccessToken = accessToken;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
mod.modResults.NSLocationAlwaysAndWhenInUseUsageDescription =
|
|
197
|
-
'Your location is used for turn-by-turn navigation even when the app is in the background.';
|
|
198
|
-
}
|
|
46
|
+
mod.modResults.NSLocationWhenInUseUsageDescription =
|
|
47
|
+
mod.modResults.NSLocationWhenInUseUsageDescription ||
|
|
48
|
+
'Your location is used for navigation.';
|
|
49
|
+
mod.modResults.NSLocationAlwaysAndWhenInUseUsageDescription =
|
|
50
|
+
mod.modResults.NSLocationAlwaysAndWhenInUseUsageDescription ||
|
|
51
|
+
'Your location is used for navigation.';
|
|
199
52
|
if (!mod.modResults.UIBackgroundModes) mod.modResults.UIBackgroundModes = [];
|
|
200
53
|
for (const mode of ['audio', 'location']) {
|
|
201
54
|
if (!mod.modResults.UIBackgroundModes.includes(mode)) {
|
|
@@ -205,82 +58,145 @@ configurations.all {
|
|
|
205
58
|
return mod;
|
|
206
59
|
});
|
|
207
60
|
|
|
208
|
-
// ── iOS: .netrc for SPM authentication
|
|
209
|
-
//
|
|
210
|
-
// spm_dependency() to
|
|
211
|
-
// against api.mapbox.com using
|
|
212
|
-
//
|
|
213
|
-
// access, identical in spirit to the MAPBOX_DOWNLOADS_TOKEN gradle
|
|
214
|
-
// property used on Android.
|
|
61
|
+
// ── iOS: .netrc for SPM authentication ────────────────────────────────────
|
|
62
|
+
// The Mapbox Navigation SDK v3 is distributed as source code via SPM only.
|
|
63
|
+
// Our podspec uses spm_dependency() to declare the dependency, and SPM
|
|
64
|
+
// authenticates against api.mapbox.com using ~/.netrc credentials.
|
|
65
|
+
// This is the official Mapbox-documented authentication mechanism.
|
|
215
66
|
config = withDangerousMod(config, [
|
|
216
67
|
'ios',
|
|
217
68
|
(mod) => {
|
|
218
69
|
const homeDir = require('os').homedir();
|
|
219
70
|
const netrcPath = path.join(homeDir, '.netrc');
|
|
220
71
|
const netrcEntry = `machine api.mapbox.com\nlogin mapbox\npassword ${downloadsToken}\n`;
|
|
221
|
-
|
|
222
72
|
let existingContent = '';
|
|
223
73
|
if (fs.existsSync(netrcPath)) {
|
|
224
74
|
existingContent = fs.readFileSync(netrcPath, 'utf8');
|
|
225
75
|
}
|
|
226
|
-
|
|
227
76
|
if (!existingContent.includes('machine api.mapbox.com')) {
|
|
228
77
|
fs.writeFileSync(netrcPath, existingContent + netrcEntry, { mode: 0o600 });
|
|
78
|
+
console.log('[@jacques_gordon/expo-mapbox-navigation] ✅ Wrote Mapbox credentials to ~/.netrc');
|
|
229
79
|
}
|
|
230
|
-
|
|
231
80
|
return mod;
|
|
232
81
|
},
|
|
233
82
|
]);
|
|
234
83
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// "43029 duplicate symbols" linker errors (React Native GitHub #47344,
|
|
240
|
-
// Expo GitHub #37813). The framework ends up linked in both the Pod target
|
|
241
|
-
// AND the main app target, so the linker sees every symbol twice.
|
|
242
|
-
//
|
|
243
|
-
// THE CORRECT APPROACH:
|
|
244
|
-
// Add MapboxNavigation as a Swift Package directly to the Xcode project's
|
|
245
|
-
// main target via withXcodeProject. This ensures only ONE copy of each
|
|
246
|
-
// framework is linked in the final binary — exactly the same technique
|
|
247
|
-
// used by rnmapbox/maps itself for MapboxMaps.
|
|
248
|
-
config = withXcodeProject(config, (mod) => {
|
|
249
|
-
const xcodeProject = mod.modResults;
|
|
250
|
-
const MAPBOX_NAV_REPO = 'https://github.com/mapbox/mapbox-navigation-ios.git';
|
|
251
|
-
const MAPBOX_NAV_VERSION = '3.25.0';
|
|
84
|
+
return config;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// ── Android helpers ───────────────────────────────────────────────────────────
|
|
252
88
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
(pkg) => pkg && pkg.repositoryURL && pkg.repositoryURL.includes('mapbox-navigation-ios')
|
|
89
|
+
function addAndroidConfig(mod, mapboxMapsVersion, androidColorOverrides) {
|
|
90
|
+
if (!mod.modResults.contents.includes('abiFilters')) {
|
|
91
|
+
mod.modResults.contents = mod.modResults.contents.replace(
|
|
92
|
+
/defaultConfig {([\s\S]*?)}/,
|
|
93
|
+
`defaultConfig {\n ndk {\n abiFilters "arm64-v8a", "x86_64"\n }\n $1\n }`
|
|
259
94
|
);
|
|
95
|
+
}
|
|
260
96
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
},
|
|
272
|
-
[
|
|
273
|
-
// Products from MapboxNavigation SPM package that our Swift files import
|
|
274
|
-
{ product: 'MapboxNavigationCore' },
|
|
275
|
-
{ product: 'MapboxNavigationUIKit' },
|
|
276
|
-
]
|
|
277
|
-
);
|
|
97
|
+
const workVersion = '2.8.0';
|
|
98
|
+
mod.modResults.contents = mod.modResults.contents.replace(
|
|
99
|
+
/implementation ['"]androidx.work:work-runtime-ktx:[\d.]+['"]/,
|
|
100
|
+
''
|
|
101
|
+
);
|
|
102
|
+
if (!mod.modResults.contents.includes('work-runtime')) {
|
|
103
|
+
mod.modResults.contents += `
|
|
104
|
+
dependencies {
|
|
105
|
+
implementation 'androidx.work:work-runtime:${workVersion}'
|
|
106
|
+
implementation 'androidx.work:work-runtime-ktx:${workVersion}'
|
|
278
107
|
}
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
279
110
|
|
|
280
|
-
|
|
281
|
-
|
|
111
|
+
if (!mod.modResults.contents.includes('dependencySubstitution')) {
|
|
112
|
+
const MAPS_VER = mapboxMapsVersion || '11.11.0';
|
|
113
|
+
const COMMON_VER = '24.11.3';
|
|
114
|
+
mod.modResults.contents += `
|
|
115
|
+
configurations.all {
|
|
116
|
+
resolutionStrategy {
|
|
117
|
+
dependencySubstitution {
|
|
118
|
+
substitute module('com.mapbox.maps:android') using module('com.mapbox.maps:android-ndk27:${MAPS_VER}')
|
|
119
|
+
substitute module('com.mapbox.maps:android-core') using module('com.mapbox.maps:android-core-ndk27:${MAPS_VER}')
|
|
120
|
+
substitute module('com.mapbox.maps:base') using module('com.mapbox.maps:base-ndk27:${MAPS_VER}')
|
|
121
|
+
substitute module('com.mapbox.common:common') using module('com.mapbox.common:common-ndk27:${COMMON_VER}')
|
|
122
|
+
substitute module('com.mapbox.module:maps-telemetry') using module('com.mapbox.module:maps-telemetry-ndk27:${MAPS_VER}')
|
|
123
|
+
substitute module('com.mapbox.plugin:maps-attribution') using module('com.mapbox.plugin:maps-attribution-ndk27:${MAPS_VER}')
|
|
124
|
+
substitute module('com.mapbox.plugin:maps-scalebar') using module('com.mapbox.plugin:maps-scalebar-ndk27:${MAPS_VER}')
|
|
125
|
+
substitute module('com.mapbox.plugin:maps-gestures') using module('com.mapbox.plugin:maps-gestures-ndk27:${MAPS_VER}')
|
|
126
|
+
substitute module('com.mapbox.plugin:maps-logo') using module('com.mapbox.plugin:maps-logo-ndk27:${MAPS_VER}')
|
|
127
|
+
substitute module('com.mapbox.plugin:maps-compass') using module('com.mapbox.plugin:maps-compass-ndk27:${MAPS_VER}')
|
|
128
|
+
substitute module('com.mapbox.plugin:maps-lifecycle') using module('com.mapbox.plugin:maps-lifecycle-ndk27:${MAPS_VER}')
|
|
129
|
+
substitute module('com.mapbox.plugin:maps-animation') using module('com.mapbox.plugin:maps-animation-ndk27:${MAPS_VER}')
|
|
130
|
+
substitute module('com.mapbox.plugin:maps-overlay') using module('com.mapbox.plugin:maps-overlay-ndk27:${MAPS_VER}')
|
|
131
|
+
substitute module('com.mapbox.plugin:maps-annotation') using module('com.mapbox.plugin:maps-annotation-ndk27:${MAPS_VER}')
|
|
132
|
+
substitute module('com.mapbox.plugin:maps-locationcomponent') using module('com.mapbox.plugin:maps-locationcomponent-ndk27:${MAPS_VER}')
|
|
133
|
+
substitute module('com.mapbox.plugin:maps-viewport') using module('com.mapbox.plugin:maps-viewport-ndk27:${MAPS_VER}')
|
|
134
|
+
substitute module('com.mapbox.extension:maps-localization') using module('com.mapbox.extension:maps-localization-ndk27:${MAPS_VER}')
|
|
135
|
+
substitute module('com.mapbox.extension:maps-style') using module('com.mapbox.extension:maps-style-ndk27:${MAPS_VER}')
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
282
141
|
|
|
283
|
-
|
|
284
|
-
|
|
142
|
+
// Android color overrides for Mapbox resource colors (route line, banner, etc.)
|
|
143
|
+
if (Object.keys(androidColorOverrides).length > 0) {
|
|
144
|
+
const resDir = path.join(
|
|
145
|
+
mod.modRequest?.platformProjectRoot || '',
|
|
146
|
+
'app', 'src', 'main', 'res', 'values'
|
|
147
|
+
);
|
|
148
|
+
try {
|
|
149
|
+
fs.mkdirSync(resDir, { recursive: true });
|
|
150
|
+
const colorEntries = Object.entries(androidColorOverrides)
|
|
151
|
+
.map(([name, value]) => ` <color name="${name}">${value}</color>`)
|
|
152
|
+
.join('\n');
|
|
153
|
+
const xmlContent = `<?xml version="1.0" encoding="utf-8"?>\n<resources>\n${colorEntries}\n</resources>\n`;
|
|
154
|
+
fs.writeFileSync(path.join(resDir, 'mapbox_color_overrides.xml'), xmlContent);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
// Ignore — resDir may not exist at plugin resolution time
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function addAndroidPermissions(mod, accessToken) {
|
|
162
|
+
const manifest = mod.modResults.manifest;
|
|
163
|
+
const application = manifest.application[0];
|
|
164
|
+
|
|
165
|
+
if (!application['meta-data']) application['meta-data'] = [];
|
|
166
|
+
const existingToken = application['meta-data'].find(
|
|
167
|
+
(item) => item['$']['android:name'] === 'com.mapbox.token'
|
|
168
|
+
);
|
|
169
|
+
if (!existingToken) {
|
|
170
|
+
application['meta-data'].push({
|
|
171
|
+
$: { 'android:name': 'com.mapbox.token', 'android:value': accessToken },
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const requiredPermissions = [
|
|
176
|
+
'android.permission.ACCESS_FINE_LOCATION',
|
|
177
|
+
'android.permission.ACCESS_COARSE_LOCATION',
|
|
178
|
+
'android.permission.FOREGROUND_SERVICE',
|
|
179
|
+
'android.permission.FOREGROUND_SERVICE_LOCATION',
|
|
180
|
+
'android.permission.POST_NOTIFICATIONS',
|
|
181
|
+
];
|
|
182
|
+
if (!manifest['uses-permission']) manifest['uses-permission'] = [];
|
|
183
|
+
for (const perm of requiredPermissions) {
|
|
184
|
+
if (!manifest['uses-permission'].find((p) => p['$']['android:name'] === perm)) {
|
|
185
|
+
manifest['uses-permission'].push({ $: { 'android:name': perm } });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!manifest.service) manifest.service = [];
|
|
190
|
+
const svcName = 'com.mapbox.navigation.core.trip.service.NavigationNotificationService';
|
|
191
|
+
if (!manifest.service.find((s) => s['$']['android:name'] === svcName)) {
|
|
192
|
+
manifest.service.push({
|
|
193
|
+
$: {
|
|
194
|
+
'android:name': svcName,
|
|
195
|
+
'android:foregroundServiceType': 'location',
|
|
196
|
+
'android:exported': 'false',
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
285
201
|
|
|
286
202
|
module.exports = withMapboxNavigation;
|