@baeckerherz/expo-mapbox-navigation 0.1.0
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 +176 -0
- package/android/build.gradle +74 -0
- package/android/src/main/java/expo/modules/mapboxnavigation/ExpoMapboxNavigationModule.kt +66 -0
- package/android/src/main/java/expo/modules/mapboxnavigation/ExpoMapboxNavigationView.kt +164 -0
- package/expo-module.config.json +9 -0
- package/ios/ExpoMapboxNavigation.podspec +26 -0
- package/ios/ExpoMapboxNavigationModule.swift +66 -0
- package/ios/ExpoMapboxNavigationView.swift +377 -0
- package/package.json +52 -0
- package/plugin/build/index.d.ts +11 -0
- package/plugin/build/index.js +36 -0
- package/plugin/build/withMapboxNavGradle.d.ts +6 -0
- package/plugin/build/withMapboxNavGradle.js +31 -0
- package/plugin/build/withMapboxNavPodfile.d.ts +7 -0
- package/plugin/build/withMapboxNavPodfile.js +79 -0
- package/plugin/build/withMapboxNavSPM.d.ts +13 -0
- package/plugin/build/withMapboxNavSPM.js +80 -0
- package/plugin/src/index.ts +63 -0
- package/plugin/src/withMapboxNavGradle.ts +34 -0
- package/plugin/src/withMapboxNavPodfile.ts +56 -0
- package/plugin/src/withMapboxNavSPM.ts +124 -0
- package/src/ExpoMapboxNavigation.tsx +16 -0
- package/src/index.ts +8 -0
- package/src/types.ts +122 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import UIKit
|
|
3
|
+
import CoreLocation
|
|
4
|
+
import MapboxNavigationUIKit
|
|
5
|
+
import MapboxNavigationCore
|
|
6
|
+
import MapboxDirections
|
|
7
|
+
import MapboxMaps
|
|
8
|
+
|
|
9
|
+
// MARK: - Custom Style that applies React Native color overrides
|
|
10
|
+
|
|
11
|
+
class CustomDayStyle: StandardDayStyle {
|
|
12
|
+
var customMapStyleURL: URL?
|
|
13
|
+
var customAccentColor: UIColor?
|
|
14
|
+
var customRouteColor: UIColor?
|
|
15
|
+
var customBannerBackgroundColor: UIColor?
|
|
16
|
+
var customBannerTextColor: UIColor?
|
|
17
|
+
|
|
18
|
+
override func apply() {
|
|
19
|
+
super.apply()
|
|
20
|
+
|
|
21
|
+
if let url = customMapStyleURL {
|
|
22
|
+
mapStyleURL = url
|
|
23
|
+
previewMapStyleURL = url
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let tc = UITraitCollection(userInterfaceIdiom: .phone)
|
|
27
|
+
|
|
28
|
+
if let accent = customAccentColor {
|
|
29
|
+
tintColor = accent
|
|
30
|
+
FloatingButton.appearance(for: tc).tintColor = accent
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if let bannerBg = customBannerBackgroundColor {
|
|
34
|
+
TopBannerView.appearance(for: tc).backgroundColor = bannerBg
|
|
35
|
+
InstructionsBannerView.appearance(for: tc).backgroundColor = bannerBg
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if let bannerText = customBannerTextColor {
|
|
39
|
+
PrimaryLabel.appearance(for: tc, whenContainedInInstancesOf: [InstructionsBannerView.self]).normalTextColor = bannerText
|
|
40
|
+
SecondaryLabel.appearance(for: tc, whenContainedInInstancesOf: [InstructionsBannerView.self]).normalTextColor = bannerText
|
|
41
|
+
DistanceLabel.appearance(for: tc, whenContainedInInstancesOf: [InstructionsBannerView.self]).valueTextColor = bannerText
|
|
42
|
+
DistanceLabel.appearance(for: tc, whenContainedInInstancesOf: [InstructionsBannerView.self]).unitTextColor = bannerText.withAlphaComponent(0.7)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if let route = customRouteColor ?? customAccentColor {
|
|
46
|
+
Task { @MainActor in
|
|
47
|
+
NavigationMapView.appearance(for: tc).routeCasingColor = route.withAlphaComponent(0.8)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class CustomNightStyle: StandardNightStyle {
|
|
54
|
+
var customMapStyleURL: URL?
|
|
55
|
+
var customAccentColor: UIColor?
|
|
56
|
+
var customRouteColor: UIColor?
|
|
57
|
+
var customBannerBackgroundColor: UIColor?
|
|
58
|
+
var customBannerTextColor: UIColor?
|
|
59
|
+
|
|
60
|
+
override func apply() {
|
|
61
|
+
super.apply()
|
|
62
|
+
|
|
63
|
+
if let url = customMapStyleURL {
|
|
64
|
+
mapStyleURL = url
|
|
65
|
+
previewMapStyleURL = url
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let tc = UITraitCollection(userInterfaceIdiom: .phone)
|
|
69
|
+
|
|
70
|
+
if let accent = customAccentColor {
|
|
71
|
+
tintColor = accent
|
|
72
|
+
FloatingButton.appearance(for: tc).tintColor = accent
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if let bannerBg = customBannerBackgroundColor {
|
|
76
|
+
TopBannerView.appearance(for: tc).backgroundColor = bannerBg
|
|
77
|
+
InstructionsBannerView.appearance(for: tc).backgroundColor = bannerBg
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if let bannerText = customBannerTextColor {
|
|
81
|
+
PrimaryLabel.appearance(for: tc, whenContainedInInstancesOf: [InstructionsBannerView.self]).normalTextColor = bannerText
|
|
82
|
+
SecondaryLabel.appearance(for: tc, whenContainedInInstancesOf: [InstructionsBannerView.self]).normalTextColor = bannerText
|
|
83
|
+
DistanceLabel.appearance(for: tc, whenContainedInInstancesOf: [InstructionsBannerView.self]).valueTextColor = bannerText
|
|
84
|
+
DistanceLabel.appearance(for: tc, whenContainedInInstancesOf: [InstructionsBannerView.self]).unitTextColor = bannerText.withAlphaComponent(0.7)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if let route = customRouteColor ?? customAccentColor {
|
|
88
|
+
Task { @MainActor in
|
|
89
|
+
NavigationMapView.appearance(for: tc).routeCasingColor = route.withAlphaComponent(0.8)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// MARK: - Hex color parsing
|
|
96
|
+
|
|
97
|
+
extension UIColor {
|
|
98
|
+
convenience init?(hex: String) {
|
|
99
|
+
var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
100
|
+
hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
|
|
101
|
+
guard hexSanitized.count == 6, let rgb = UInt64(hexSanitized, radix: 16) else {
|
|
102
|
+
return nil
|
|
103
|
+
}
|
|
104
|
+
self.init(
|
|
105
|
+
red: CGFloat((rgb >> 16) & 0xFF) / 255.0,
|
|
106
|
+
green: CGFloat((rgb >> 8) & 0xFF) / 255.0,
|
|
107
|
+
blue: CGFloat(rgb & 0xFF) / 255.0,
|
|
108
|
+
alpha: 1.0
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// MARK: - ExpoMapboxNavigationView
|
|
114
|
+
|
|
115
|
+
class ExpoMapboxNavigationView: ExpoView {
|
|
116
|
+
|
|
117
|
+
// MARK: Event dispatchers
|
|
118
|
+
|
|
119
|
+
let onRouteProgressChanged = EventDispatcher()
|
|
120
|
+
let onCancelNavigation = EventDispatcher()
|
|
121
|
+
let onWaypointArrival = EventDispatcher()
|
|
122
|
+
let onFinalDestinationArrival = EventDispatcher()
|
|
123
|
+
let onRouteChanged = EventDispatcher()
|
|
124
|
+
let onUserOffRoute = EventDispatcher()
|
|
125
|
+
let onError = EventDispatcher()
|
|
126
|
+
|
|
127
|
+
// MARK: Route configuration
|
|
128
|
+
|
|
129
|
+
private var coordinates: [CLLocationCoordinate2D] = []
|
|
130
|
+
var waypointIndices: [Int]?
|
|
131
|
+
var navigationLocale: String?
|
|
132
|
+
var routeProfile: String?
|
|
133
|
+
var isMuted: Bool = false
|
|
134
|
+
|
|
135
|
+
// MARK: Appearance configuration
|
|
136
|
+
|
|
137
|
+
var mapStyleURL: String?
|
|
138
|
+
var themeMode: String?
|
|
139
|
+
var accentColorHex: String?
|
|
140
|
+
var routeColorHex: String?
|
|
141
|
+
var bannerBackgroundColorHex: String?
|
|
142
|
+
var bannerTextColorHex: String?
|
|
143
|
+
|
|
144
|
+
// MARK: Navigation state
|
|
145
|
+
|
|
146
|
+
private var navigationViewController: NavigationViewController?
|
|
147
|
+
private var hasStartedNavigation = false
|
|
148
|
+
|
|
149
|
+
// MARK: Init
|
|
150
|
+
|
|
151
|
+
required init(appContext: AppContext? = nil) {
|
|
152
|
+
super.init(appContext: appContext)
|
|
153
|
+
clipsToBounds = true
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// MARK: Props
|
|
157
|
+
|
|
158
|
+
func setCoordinates(_ raw: [[String: Double]]) {
|
|
159
|
+
coordinates = raw.compactMap { dict in
|
|
160
|
+
guard let lat = dict["latitude"], let lng = dict["longitude"] else {
|
|
161
|
+
return nil
|
|
162
|
+
}
|
|
163
|
+
return CLLocationCoordinate2D(latitude: lat, longitude: lng)
|
|
164
|
+
}
|
|
165
|
+
startNavigationIfReady()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// MARK: Layout
|
|
169
|
+
|
|
170
|
+
override func layoutSubviews() {
|
|
171
|
+
super.layoutSubviews()
|
|
172
|
+
navigationViewController?.view.frame = bounds
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// MARK: Style builder
|
|
176
|
+
|
|
177
|
+
private func buildStyles() -> [Style] {
|
|
178
|
+
let accentColor = accentColorHex.flatMap { UIColor(hex: $0) }
|
|
179
|
+
let routeColor = routeColorHex.flatMap { UIColor(hex: $0) }
|
|
180
|
+
let bannerBg = bannerBackgroundColorHex.flatMap { UIColor(hex: $0) }
|
|
181
|
+
let bannerText = bannerTextColorHex.flatMap { UIColor(hex: $0) }
|
|
182
|
+
let customURL = mapStyleURL.flatMap { URL(string: $0) }
|
|
183
|
+
|
|
184
|
+
let dayStyle = CustomDayStyle()
|
|
185
|
+
dayStyle.customMapStyleURL = customURL
|
|
186
|
+
dayStyle.customAccentColor = accentColor
|
|
187
|
+
dayStyle.customRouteColor = routeColor
|
|
188
|
+
dayStyle.customBannerBackgroundColor = bannerBg
|
|
189
|
+
dayStyle.customBannerTextColor = bannerText
|
|
190
|
+
|
|
191
|
+
let nightStyle = CustomNightStyle()
|
|
192
|
+
nightStyle.customMapStyleURL = customURL
|
|
193
|
+
nightStyle.customAccentColor = accentColor
|
|
194
|
+
nightStyle.customRouteColor = routeColor
|
|
195
|
+
nightStyle.customBannerBackgroundColor = bannerBg
|
|
196
|
+
nightStyle.customBannerTextColor = bannerText
|
|
197
|
+
|
|
198
|
+
switch themeMode {
|
|
199
|
+
case "night":
|
|
200
|
+
return [nightStyle]
|
|
201
|
+
case "day":
|
|
202
|
+
return [dayStyle]
|
|
203
|
+
default:
|
|
204
|
+
// "auto" or nil -- provide both for automatic day/night switching
|
|
205
|
+
return [dayStyle, nightStyle]
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// MARK: Navigation lifecycle
|
|
210
|
+
|
|
211
|
+
private func startNavigationIfReady() {
|
|
212
|
+
guard coordinates.count >= 2, !hasStartedNavigation else { return }
|
|
213
|
+
guard bounds.width > 0 else {
|
|
214
|
+
DispatchQueue.main.async { [weak self] in
|
|
215
|
+
self?.setNeedsLayout()
|
|
216
|
+
self?.layoutIfNeeded()
|
|
217
|
+
self?.startNavigationIfReady()
|
|
218
|
+
}
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
hasStartedNavigation = true
|
|
223
|
+
|
|
224
|
+
// Build waypoints
|
|
225
|
+
let waypoints = coordinates.enumerated().map { index, coord -> Waypoint in
|
|
226
|
+
var wp = Waypoint(coordinate: coord)
|
|
227
|
+
if let indices = waypointIndices {
|
|
228
|
+
wp.separatesLegs = indices.contains(index)
|
|
229
|
+
}
|
|
230
|
+
return wp
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Locale and measurement system
|
|
234
|
+
let resolvedLocale: Locale = {
|
|
235
|
+
if let code = navigationLocale {
|
|
236
|
+
return Locale(identifier: code)
|
|
237
|
+
}
|
|
238
|
+
return Locale.current
|
|
239
|
+
}()
|
|
240
|
+
let useMetric = !resolvedLocale.identifier.hasPrefix("en_US")
|
|
241
|
+
|
|
242
|
+
// Route options
|
|
243
|
+
var routeOptions = NavigationRouteOptions(waypoints: waypoints)
|
|
244
|
+
routeOptions.locale = resolvedLocale
|
|
245
|
+
routeOptions.distanceMeasurementSystem = useMetric ? .metric : .imperial
|
|
246
|
+
|
|
247
|
+
if let profile = routeProfile {
|
|
248
|
+
routeOptions.profileIdentifier = MapboxDirections.ProfileIdentifier(rawValue: profile)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Core config
|
|
252
|
+
var coreConfig = CoreConfig()
|
|
253
|
+
coreConfig.locale = resolvedLocale
|
|
254
|
+
coreConfig.unitOfMeasurement = useMetric ? .metric : .imperial
|
|
255
|
+
|
|
256
|
+
let provider = MapboxNavigationProvider(coreConfig: coreConfig)
|
|
257
|
+
let mapboxNavigation = provider.mapboxNavigation
|
|
258
|
+
|
|
259
|
+
Task { @MainActor in
|
|
260
|
+
do {
|
|
261
|
+
let result = try await mapboxNavigation.routingProvider().calculateRoutes(
|
|
262
|
+
options: routeOptions
|
|
263
|
+
).value
|
|
264
|
+
|
|
265
|
+
let navOptions = NavigationOptions(
|
|
266
|
+
mapboxNavigation: mapboxNavigation,
|
|
267
|
+
voiceController: provider.routeVoiceController,
|
|
268
|
+
eventsManager: provider.eventsManager(),
|
|
269
|
+
styles: self.buildStyles()
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
let navVC = NavigationViewController(
|
|
273
|
+
navigationRoutes: result,
|
|
274
|
+
navigationOptions: navOptions
|
|
275
|
+
)
|
|
276
|
+
navVC.delegate = self
|
|
277
|
+
|
|
278
|
+
// Apply mute setting
|
|
279
|
+
if self.isMuted {
|
|
280
|
+
provider.routeVoiceController.speechSynthesizer.muted = true
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Embed navigation view
|
|
284
|
+
navVC.view.frame = self.bounds
|
|
285
|
+
navVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
286
|
+
self.addSubview(navVC.view)
|
|
287
|
+
self.navigationViewController = navVC
|
|
288
|
+
|
|
289
|
+
if let parentVC = self.findViewController() {
|
|
290
|
+
parentVC.addChild(navVC)
|
|
291
|
+
navVC.didMove(toParent: parentVC)
|
|
292
|
+
}
|
|
293
|
+
} catch {
|
|
294
|
+
self.onError(["message": "Failed to calculate route: \(error.localizedDescription)"])
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private func findViewController() -> UIViewController? {
|
|
300
|
+
var responder: UIResponder? = self
|
|
301
|
+
while let next = responder?.next {
|
|
302
|
+
if let vc = next as? UIViewController { return vc }
|
|
303
|
+
responder = next
|
|
304
|
+
}
|
|
305
|
+
return nil
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// MARK: Cleanup
|
|
309
|
+
|
|
310
|
+
deinit {
|
|
311
|
+
navigationViewController?.willMove(toParent: nil)
|
|
312
|
+
navigationViewController?.view.removeFromSuperview()
|
|
313
|
+
navigationViewController?.removeFromParent()
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// MARK: - NavigationViewControllerDelegate
|
|
318
|
+
|
|
319
|
+
extension ExpoMapboxNavigationView: NavigationViewControllerDelegate {
|
|
320
|
+
|
|
321
|
+
func navigationViewController(
|
|
322
|
+
_ navigationViewController: NavigationViewController,
|
|
323
|
+
didUpdate progress: RouteProgress,
|
|
324
|
+
with location: CLLocation,
|
|
325
|
+
rawLocation: CLLocation
|
|
326
|
+
) {
|
|
327
|
+
onRouteProgressChanged([
|
|
328
|
+
"distanceRemaining": progress.distanceRemaining,
|
|
329
|
+
"durationRemaining": progress.durationRemaining,
|
|
330
|
+
"distanceTraveled": progress.distanceTraveled,
|
|
331
|
+
"fractionTraveled": progress.fractionTraveled,
|
|
332
|
+
])
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
func navigationViewControllerDidDismiss(
|
|
336
|
+
_ navigationViewController: NavigationViewController,
|
|
337
|
+
byCanceling canceled: Bool
|
|
338
|
+
) {
|
|
339
|
+
if canceled {
|
|
340
|
+
onCancelNavigation()
|
|
341
|
+
} else {
|
|
342
|
+
onFinalDestinationArrival()
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
func navigationViewController(
|
|
347
|
+
_ navigationViewController: NavigationViewController,
|
|
348
|
+
didArriveAt waypoint: Waypoint
|
|
349
|
+
) -> Bool {
|
|
350
|
+
let index = coordinates.firstIndex { coord in
|
|
351
|
+
coord.latitude == waypoint.coordinate.latitude
|
|
352
|
+
&& coord.longitude == waypoint.coordinate.longitude
|
|
353
|
+
} ?? -1
|
|
354
|
+
|
|
355
|
+
if index == coordinates.count - 1 {
|
|
356
|
+
onFinalDestinationArrival()
|
|
357
|
+
} else {
|
|
358
|
+
onWaypointArrival(["waypointIndex": index])
|
|
359
|
+
}
|
|
360
|
+
return true
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
func navigationViewController(
|
|
364
|
+
_ navigationViewController: NavigationViewController,
|
|
365
|
+
didRerouteAlong route: Route
|
|
366
|
+
) {
|
|
367
|
+
onRouteChanged()
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
func navigationViewController(
|
|
371
|
+
_ navigationViewController: NavigationViewController,
|
|
372
|
+
shouldRerouteFrom location: CLLocation
|
|
373
|
+
) -> Bool {
|
|
374
|
+
onUserOffRoute()
|
|
375
|
+
return true
|
|
376
|
+
}
|
|
377
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@baeckerherz/expo-mapbox-navigation",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Expo module wrapping Mapbox Navigation SDK v3 for iOS and Android",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"expo": {
|
|
8
|
+
"plugin": "./plugin/build/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build:plugin": "cd plugin && tsc",
|
|
12
|
+
"build": "tsc && yarn build:plugin",
|
|
13
|
+
"clean": "rm -rf build plugin/build",
|
|
14
|
+
"prepublishOnly": "yarn build:plugin",
|
|
15
|
+
"lint": "eslint src/"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"expo",
|
|
19
|
+
"react-native",
|
|
20
|
+
"mapbox",
|
|
21
|
+
"navigation",
|
|
22
|
+
"turn-by-turn",
|
|
23
|
+
"driving",
|
|
24
|
+
"mapbox-navigation-sdk",
|
|
25
|
+
"mapbox-navigation-v3"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/baeckerherz/expo-mapbox-navigation"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"src/",
|
|
34
|
+
"ios/",
|
|
35
|
+
"android/",
|
|
36
|
+
"plugin/build/",
|
|
37
|
+
"plugin/src/",
|
|
38
|
+
"expo-module.config.json",
|
|
39
|
+
"README.md"
|
|
40
|
+
],
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"expo": ">=51.0.0",
|
|
43
|
+
"react": ">=18.0.0",
|
|
44
|
+
"react-native": ">=0.74.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"expo-modules-core": "^2.5.0",
|
|
48
|
+
"expo-module-scripts": "^3.5.2",
|
|
49
|
+
"typescript": "^5.0.0",
|
|
50
|
+
"@expo/config-plugins": "^8.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ConfigPlugin } from "@expo/config-plugins";
|
|
2
|
+
interface PluginConfig {
|
|
3
|
+
/** Mapbox public access token (pk.xxx). Required for the native SDK. */
|
|
4
|
+
mapboxAccessToken: string;
|
|
5
|
+
/** Mapbox secret/download token (sk.xxx). Required for SPM and Maven auth. */
|
|
6
|
+
mapboxSecretToken?: string;
|
|
7
|
+
/** Mapbox Navigation SDK version. Default: "3.5.0" */
|
|
8
|
+
navigationSdkVersion?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const _default: ConfigPlugin<PluginConfig>;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
4
|
+
const withMapboxNavSPM_1 = require("./withMapboxNavSPM");
|
|
5
|
+
const withMapboxNavPodfile_1 = require("./withMapboxNavPodfile");
|
|
6
|
+
const withMapboxNavGradle_1 = require("./withMapboxNavGradle");
|
|
7
|
+
const withMapboxNavigation = (config, { mapboxAccessToken, mapboxSecretToken, navigationSdkVersion = "3.5.0", }) => {
|
|
8
|
+
if (!mapboxAccessToken) {
|
|
9
|
+
throw new Error("[@baeckerherz/expo-mapbox-navigation] mapboxAccessToken is required.");
|
|
10
|
+
}
|
|
11
|
+
// Inject MBXAccessToken and required background modes into Info.plist
|
|
12
|
+
if (!config.ios)
|
|
13
|
+
config.ios = {};
|
|
14
|
+
if (!config.ios.infoPlist)
|
|
15
|
+
config.ios.infoPlist = {};
|
|
16
|
+
config.ios.infoPlist.MBXAccessToken = mapboxAccessToken;
|
|
17
|
+
config.ios.infoPlist.UIBackgroundModes = [
|
|
18
|
+
...(config.ios.infoPlist.UIBackgroundModes || []),
|
|
19
|
+
"audio",
|
|
20
|
+
"location",
|
|
21
|
+
];
|
|
22
|
+
config.ios.infoPlist.NSLocationWhenInUseUsageDescription =
|
|
23
|
+
config.ios.infoPlist.NSLocationWhenInUseUsageDescription ||
|
|
24
|
+
"This app needs your location for turn-by-turn navigation.";
|
|
25
|
+
config.ios.infoPlist.NSLocationAlwaysAndWhenInUseUsageDescription =
|
|
26
|
+
config.ios.infoPlist.NSLocationAlwaysAndWhenInUseUsageDescription ||
|
|
27
|
+
"This app needs your location for turn-by-turn navigation, including in the background.";
|
|
28
|
+
// iOS: Inject SPM package references
|
|
29
|
+
config = (0, withMapboxNavSPM_1.withMapboxNavSPM)(config, { navigationSdkVersion });
|
|
30
|
+
// iOS: Patch Podfile for SPM framework visibility
|
|
31
|
+
config = (0, withMapboxNavPodfile_1.withMapboxNavPodfile)(config);
|
|
32
|
+
// Android: Add Mapbox Maven repository
|
|
33
|
+
config = (0, withMapboxNavGradle_1.withMapboxNavGradle)(config);
|
|
34
|
+
return config;
|
|
35
|
+
};
|
|
36
|
+
exports.default = (0, config_plugins_1.createRunOncePlugin)(withMapboxNavigation, "@baeckerherz/expo-mapbox-navigation", "0.1.0");
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withMapboxNavGradle = void 0;
|
|
4
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
5
|
+
/**
|
|
6
|
+
* Adds the authenticated Mapbox Maven repository to the Android
|
|
7
|
+
* project's root build.gradle for Navigation SDK v3 downloads.
|
|
8
|
+
*/
|
|
9
|
+
const withMapboxNavGradle = (config) => {
|
|
10
|
+
return (0, config_plugins_1.withProjectBuildGradle)(config, (config) => {
|
|
11
|
+
let contents = config.modResults.contents;
|
|
12
|
+
if (!contents.includes("api.mapbox.com/downloads/v2/releases/maven")) {
|
|
13
|
+
const mapboxMaven = `
|
|
14
|
+
// @baeckerherz/expo-mapbox-navigation: Mapbox Navigation SDK Maven repository
|
|
15
|
+
maven {
|
|
16
|
+
url = uri("https://api.mapbox.com/downloads/v2/releases/maven")
|
|
17
|
+
authentication {
|
|
18
|
+
create<BasicAuthentication>("basic")
|
|
19
|
+
}
|
|
20
|
+
credentials {
|
|
21
|
+
username = "mapbox"
|
|
22
|
+
password = providers.gradleProperty("MAPBOX_DOWNLOADS_TOKEN").getOrElse("")
|
|
23
|
+
}
|
|
24
|
+
}`;
|
|
25
|
+
contents = contents.replace(/allprojects\s*\{\s*repositories\s*\{/, `allprojects {\n repositories {${mapboxMaven}`);
|
|
26
|
+
}
|
|
27
|
+
config.modResults.contents = contents;
|
|
28
|
+
return config;
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
exports.withMapboxNavGradle = withMapboxNavGradle;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ConfigPlugin } from "@expo/config-plugins";
|
|
2
|
+
/**
|
|
3
|
+
* Patches the iOS Podfile to make SPM framework products visible
|
|
4
|
+
* to the ExpoMapboxNavigation pod target, and sets BUILD_LIBRARY_FOR_DISTRIBUTION
|
|
5
|
+
* for Mapbox-related pods.
|
|
6
|
+
*/
|
|
7
|
+
export declare const withMapboxNavPodfile: ConfigPlugin;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.withMapboxNavPodfile = void 0;
|
|
37
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
/**
|
|
41
|
+
* Patches the iOS Podfile to make SPM framework products visible
|
|
42
|
+
* to the ExpoMapboxNavigation pod target, and sets BUILD_LIBRARY_FOR_DISTRIBUTION
|
|
43
|
+
* for Mapbox-related pods.
|
|
44
|
+
*/
|
|
45
|
+
const withMapboxNavPodfile = (config) => {
|
|
46
|
+
return (0, config_plugins_1.withDangerousMod)(config, [
|
|
47
|
+
"ios",
|
|
48
|
+
async (config) => {
|
|
49
|
+
const podfilePath = path.join(config.modRequest.platformProjectRoot, "Podfile");
|
|
50
|
+
let contents = fs.readFileSync(podfilePath, "utf8");
|
|
51
|
+
const hook = `
|
|
52
|
+
# @baeckerherz/expo-mapbox-navigation: Make SPM frameworks visible to the pod target
|
|
53
|
+
installer.pods_project.targets.each do |target|
|
|
54
|
+
if target.name == 'ExpoMapboxNavigation'
|
|
55
|
+
target.build_configurations.each do |config|
|
|
56
|
+
shared_products = '$(BUILT_PRODUCTS_DIR)/..'
|
|
57
|
+
config.build_settings['FRAMEWORK_SEARCH_PATHS'] ||= ['$(inherited)']
|
|
58
|
+
config.build_settings['FRAMEWORK_SEARCH_PATHS'] << shared_products
|
|
59
|
+
config.build_settings['SWIFT_INCLUDE_PATHS'] ||= ['$(inherited)']
|
|
60
|
+
config.build_settings['SWIFT_INCLUDE_PATHS'] << shared_products
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
if target.name.start_with?('Mapbox') || target.name == 'Turf'
|
|
64
|
+
target.build_configurations.each do |config|
|
|
65
|
+
config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end`;
|
|
69
|
+
if (!contents.includes("@baeckerherz/expo-mapbox-navigation: Make SPM frameworks")) {
|
|
70
|
+
if (contents.includes("post_install do |installer|")) {
|
|
71
|
+
contents = contents.replace("post_install do |installer|", `post_install do |installer|${hook}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
fs.writeFileSync(podfilePath, contents);
|
|
75
|
+
return config;
|
|
76
|
+
},
|
|
77
|
+
]);
|
|
78
|
+
};
|
|
79
|
+
exports.withMapboxNavPodfile = withMapboxNavPodfile;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ConfigPlugin } from "@expo/config-plugins";
|
|
2
|
+
interface SPMConfig {
|
|
3
|
+
navigationSdkVersion: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Injects Mapbox Navigation SDK v3 as a Swift Package Manager dependency
|
|
7
|
+
* into the Xcode project's .pbxproj. This avoids vendoring .xcframework files.
|
|
8
|
+
*
|
|
9
|
+
* Adds XCRemoteSwiftPackageReference, XCSwiftPackageProductDependency,
|
|
10
|
+
* and links MapboxNavigationUIKit + MapboxNavigationCore to the main target.
|
|
11
|
+
*/
|
|
12
|
+
export declare const withMapboxNavSPM: ConfigPlugin<SPMConfig>;
|
|
13
|
+
export {};
|