Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ Covers web routing, turn-by-turn navigation, multi-stop routes, optimization, tr
**Key topics:**

- Directions API for web (basic routes, alternatives, multi-stop, optimization)
- Navigation SDK for iOS (NavigationViewController, voice guidance, custom UI)
- Navigation SDK for iOS (SwiftUI + wrapped NavigationViewController default; Core custom UI opt-in)
- Navigation SDK for Android (NavigationView, custom UI, route progress)
- Traffic-aware routing with congestion data
- Route caching and performance optimization
Expand All @@ -557,7 +557,7 @@ Covers web routing, turn-by-turn navigation, multi-stop routes, optimization, tr
**Covers all platforms:**

- Web (Directions API with Mapbox GL JS)
- iOS (Navigation SDK for iOS with Swift)
- iOS (Navigation SDK for iOS — SwiftUI + drop-in NavigationViewController by default)
- Android (Navigation SDK for Android with Kotlin)

**Common patterns:**
Expand Down
1 change: 1 addition & 0 deletions cspell.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
"mouseout",
"moveend",
"myapp",
"netrc",
"objc",
"podfile",
"podspec",
Expand Down
168 changes: 90 additions & 78 deletions skills/mapbox-navigation-patterns/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ Quick reference for implementing navigation and routing with Mapbox Directions A

## Product Decision

| Need | Solution |
| ----------------------------- | -------------------------- |
| **Show a route on a web map** | Directions API |
| **Turn-by-turn iOS** | Navigation SDK for iOS |
| **Turn-by-turn Android** | Navigation SDK for Android |
| **Voice guidance** | Navigation SDK only |
| **Multi-stop optimization** | Optimization API |
| Need | Solution |
| ----------------------------- | ------------------------------------------------------ |
| **Show a route on a web map** | Directions API |
| **Turn-by-turn iOS** | Navigation SDK for iOS (SwiftUI + drop-in NVC default) |
| **Turn-by-turn Android** | Navigation SDK for Android |
| **Voice guidance** | Navigation SDK only |
| **Multi-stop optimization** | Optimization API |

## Directions API (Web)

Expand Down Expand Up @@ -109,112 +109,124 @@ steps.forEach((step) => {

## Navigation SDK for iOS

### Basic Navigation
**Default:** SwiftUI app shell + wrap `NavigationViewController` with `UIViewControllerRepresentable` (official getting-started). Fully custom Core UI ([CoreSDKExample](https://github.com/mapbox/mapbox-navigation-ios/tree/main/Examples/CoreSDKExample)) only when explicitly requested.

**Setup first:** SPM (`MapboxNavigationCore` + `MapboxNavigationUIKit`), `.netrc` download token, `MBXAccessToken`, location permissions, background `audio`/`location` — see `references/ios-navigation-sdk.md` checklist and [install guide](https://docs.mapbox.com/ios/navigation/guides/install/).

For specialized topics (road cameras, history, e-horizon, CarPlay, offline, styled chrome, etc.), use the **Example patterns catalog** in `references/ios-navigation-sdk.md`. Load `references/ios-navigation-specialized.md` for multi-stop, route line, camera, road cameras, and route alerts. Do not fetch upstream sample source unless the user asks to open a specific example.

**Sample host ≠ API stack:** `AdditionalExamples` are often UIKit demos. APIs on `NavigationMapView` (waypoints, final-waypoint image, route line, camera, callouts, road cameras) are stack-independent — wrap `NavigationMapView` in `UIViewRepresentable`. Road cameras: `navigationMapView.mapView.mapboxMap` + `RoadCamerasManager(navigatorHandle: provider.navigatorHandle)`. True UIKit-only: NVC chrome (top/bottom bars, styled UI elements, embed NVC).

### Default: SwiftUI + drop-in NavigationViewController

```swift
import MapboxNavigationCore
import MapboxNavigationUIKit
import SwiftUI

// Initialize provider
let mapboxNavigationProvider = MapboxNavigationProvider(
coreConfig: CoreConfig(
locationSource: .live,
ttsConfig: .default // Voice guidance enabled
)
)

// Calculate routes with async/await
Task {
do {
let options = NavigationRouteOptions(
coordinates: [start, end]
)

let navigationRoutes = try await mapboxNavigationProvider
.mapboxNavigation
.routingProvider()
.calculateRoutes(options: options)
.value

// Show full navigation UI
let navigationOptions = NavigationOptions(
mapboxNavigation: mapboxNavigationProvider.mapboxNavigation,
voiceController: mapboxNavigationProvider.routeVoiceController,
eventsManager: mapboxNavigationProvider.eventsManager()
)
struct NavigationViewControllerWrapper: UIViewControllerRepresentable {
let navigationRoutes: NavigationRoutes
let navigationOptions: NavigationOptions

let navVC = NavigationViewController(
func makeUIViewController(context: Context) -> NavigationViewController {
NavigationViewController(
navigationRoutes: navigationRoutes,
navigationOptions: navigationOptions
)
present(navVC, animated: true)

} catch {
print("Error: \(error)")
}

func updateUIViewController(_ uiViewController: NavigationViewController, context: Context) {}
}

let provider = MapboxNavigationProvider(
coreConfig: CoreConfig(locationSource: .live, ttsConfig: .default)
)
let routes = try await provider.mapboxNavigation
.routingProvider()
.calculateRoutes(options: NavigationRouteOptions(coordinates: [start, end]))
.value
let options = NavigationOptions(
mapboxNavigation: provider.mapboxNavigation,
voiceController: provider.routeVoiceController,
eventsManager: provider.eventsManager()
)
// NavigationViewControllerWrapper(navigationRoutes: routes, navigationOptions: options)
```

### Custom Navigation UI
### UIKit: present drop-in UI

```swift
import MapboxNavigationCore
import MapboxNavigationUIKit

let provider = MapboxNavigationProvider(
coreConfig: CoreConfig(locationSource: .live, ttsConfig: .default)
)
let navigationRoutes = try await provider.mapboxNavigation
.routingProvider()
.calculateRoutes(options: NavigationRouteOptions(coordinates: [start, end]))
.value
let navVC = NavigationViewController(
navigationRoutes: navigationRoutes,
navigationOptions: NavigationOptions(
mapboxNavigation: provider.mapboxNavigation,
voiceController: provider.routeVoiceController,
eventsManager: provider.eventsManager()
)
)
present(navVC, animated: true)
```

### Opt-in: fully custom Core UI

```swift
import MapboxNavigationCore
import Combine

class CustomNavigation {
@MainActor
final class Navigation: ObservableObject {
@Published private(set) var visualInstruction: VisualInstructionBanner?
@Published private(set) var routeProgress: RouteProgress?
@Published private(set) var currentPreviewRoutes: NavigationRoutes?

// Keep a strong reference — do not create the provider only inside init and discard it.
private let provider: MapboxNavigationProvider
private var subscriptions = Set<AnyCancellable>()
private let core: MapboxNavigation
private let voiceController: RouteVoiceController

init() {
provider = MapboxNavigationProvider(coreConfig: CoreConfig())
setupSubscriptions()
}
let provider = MapboxNavigationProvider(
coreConfig: CoreConfig(locationSource: .live, ttsConfig: .default)
)
self.provider = provider
core = provider.mapboxNavigation
voiceController = provider.routeVoiceController

func setupSubscriptions() {
let navigation = provider.mapboxNavigation.navigation()

// Subscribe to route progress
navigation.routeProgress
.sink { [weak self] progressState in
guard let progress = progressState?.routeProgress else { return }
self?.updateUI(progress)
}
.store(in: &subscriptions)

// Subscribe to banner instructions
navigation.bannerInstructions
.removeDuplicates()
.sink { [weak self] state in
guard let instruction = state.visualInstruction else { return }
self?.showInstruction(instruction.primaryInstruction.text)
}
.store(in: &subscriptions)
}
core.navigation().bannerInstructions
.map(\.visualInstruction)
.assign(to: &$visualInstruction)

func updateUI(_ progress: RouteProgress) {
let distance = progress.currentLegProgress?.currentStepProgress.distanceRemaining
// Update your custom UI
core.navigation().routeProgress
.map { $0?.routeProgress }
.assign(to: &$routeProgress)
}

func showInstruction(_ text: String) {
// Display instruction in custom UI
func startActiveNavigation() {
guard let routes = currentPreviewRoutes else { return }
core.tripSession().startActiveGuidance(with: routes, startLegIndex: 0)
}
}
```

### Voice Guidance

```swift
// Configure voice via CoreConfig when creating provider
let provider = MapboxNavigationProvider(
coreConfig: CoreConfig(
ttsConfig: .default // or .localOnly, .custom(synthesizer)
)
MapboxNavigationProvider(
coreConfig: CoreConfig(ttsConfig: .default) // or .localOnly, .custom(synthesizer)
)

// Set language via route options
var options = NavigationRouteOptions(coordinates: [start, end])
options.locale = Locale(identifier: "es-ES") // Spanish
options.locale = Locale(identifier: "es-ES")
options.distanceMeasurementSystem = .metric
```

Expand Down
16 changes: 12 additions & 4 deletions skills/mapbox-navigation-patterns/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,18 @@ User says things like:

**Best for:** Native iOS apps with turn-by-turn navigation

**Defaults:** SwiftUI app shell + wrap drop-in `NavigationViewController` via `UIViewControllerRepresentable` (official getting-started path). Use a fully custom Core UI ([CoreSDKExample](https://github.com/mapbox/mapbox-navigation-ios/tree/main/Examples/CoreSDKExample)) only when the user explicitly wants to build their own nav chrome.

**Before coding:** SPM (`MapboxNavigationCore` + `MapboxNavigationUIKit`), `.netrc` download token, `MBXAccessToken`, location permissions, background `audio`/`location` — see [install guide](https://docs.mapbox.com/ios/navigation/guides/install/) and the iOS reference checklist.

**Features:**

- Complete turn-by-turn navigation UI
- Drop-in turn-by-turn UI (`NavigationViewController`) — default
- Optional fully custom Core UI (route progress / banner publishers)
- Voice guidance (30+ languages)
- Real-time rerouting
- Traffic-aware routing
- Offline maps and routing
- Custom UI components
- Route progress tracking
- Speed limit display

Expand Down Expand Up @@ -95,7 +99,8 @@ User says things like:
## Implementation Patterns

- **[references/web-directions-api.md](references/web-directions-api.md)** - Directions API patterns for the web: basic route display, turn-by-turn instructions, alternative routes, multi-stop routing, route optimization, and congestion-based route coloring
- **[references/ios-navigation-sdk.md](references/ios-navigation-sdk.md)** - Navigation SDK for iOS: basic turn-by-turn navigation, custom navigation UI, voice guidance configuration
- **[references/ios-navigation-sdk.md](references/ios-navigation-sdk.md)** - When: iOS turn-by-turn, setup, default SwiftUI + wrapped `NavigationViewController`, Core opt-in, example catalog
- **[references/ios-navigation-specialized.md](references/ios-navigation-specialized.md)** - When: multi-stop, route line, camera, road cameras, route alerts, or `NavigationMapView` customization
- **[references/android-navigation-sdk.md](references/android-navigation-sdk.md)** - Navigation SDK for Android: basic turn-by-turn navigation, custom navigation UI, route line rendering, maneuver arrows, navigation camera, voice guidance
- **[references/android-performance-antipatterns.md](references/android-performance-antipatterns.md)** - Android Navigation SDK performance and correctness antipatterns: Native Route Object traversal costs, threading, memory/lifecycle leaks, route management correctness, frequent-callback rendering efficiency, and Coordination API lifecycle
- **[references/best-practices.md](references/best-practices.md)** - Route caching, error handling, performance optimization, user experience, and common use cases (delivery routing, ride-sharing ETAs, walking/cycling directions)
Expand Down Expand Up @@ -126,7 +131,7 @@ User says things like:

**User says: "I need turn-by-turn navigation"**

- iOS → Navigation SDK for iOS
- iOS → Navigation SDK for iOS (SwiftUI + wrapped `NavigationViewController` by default; Core custom UI only if requested)
- Android → Navigation SDK for Android
- Web → Use Directions API + custom UI (no voice guidance)

Expand All @@ -138,3 +143,6 @@ User says things like:

**User says: "I need voice guidance"**
→ Must use Navigation SDK (iOS/Android only)

**User says: "Directions API or Navigation SDK?"**
→ Native turn-by-turn / voice → Navigation SDK (**MAU** pricing). Web / route display only → Directions API (**pay-per-request**).
34 changes: 33 additions & 1 deletion skills/mapbox-navigation-patterns/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"expectations": [
"Recommends the Navigation SDK for iOS, not the Directions API",
"Explains that voice guidance is only available through the Navigation SDK, not the Directions API",
"Mentions the Navigation SDK provides a complete turn-by-turn UI (NavigationViewController) and real-time rerouting",
"Defaults to drop-in NavigationViewController (wrapped with UIViewControllerRepresentable in a SwiftUI app), not a fully custom CoreSDKExample UI",
"May mention fully custom Core UI only as an opt-in when the user wants to build their own nav chrome",
"Notes Navigation SDK pricing is Monthly Active Users (MAU) based, vs. the Directions API's pay-per-request model"
]
},
Expand Down Expand Up @@ -147,6 +148,37 @@
"States every `register*Observer` call needs a matching `unregister*Observer` during teardown, and that observers registered as inline lambdas with no stored reference cannot be unregistered",
"Recommends the lifecycle-aware `requireMapboxNavigation`/`MapboxNavigationApp` pattern instead of a manually held singleton reference"
]
},
{
"id": 16,
"prompt": "I'm building a SwiftUI iOS app and want turn-by-turn navigation. What's the recommended way to add it?",
"expectations": [
"Recommends wrapping NavigationViewController with UIViewControllerRepresentable rather than building a fully custom Core UI by default",
"Shows calculating routes via MapboxNavigationProvider / routing provider, then presenting NavigationViewController with NavigationOptions",
"Keeps a strong reference to MapboxNavigationProvider",
"Mentions fully custom Core / CoreSDKExample only as an alternative when the user wants to replace the drop-in nav UI"
]
},
{
"id": 17,
"prompt": "I want to show road cameras during navigation in my iOS app. How should I find the right Mapbox sample to follow?",
"expectations": [
"Uses the skill's inline example-patterns catalog (or equivalent guidance) rather than requiring a live GitHub API fetch",
"Identifies the Road Cameras example as the matching pattern",
"Notes that Road Cameras is stack-independent: wrap `NavigationMapView` in `UIViewRepresentable`, take `mapboxMap` from `navigationMapView.mapView.mapboxMap`, and create `RoadCamerasManager` with Core `provider.navigatorHandle`",
"May load references/ios-navigation-specialized.md for the inline Road Cameras section",
"Only suggests opening upstream example source if the user wants the full sample implementation"
]
},
{
"id": 18,
"prompt": "I'm building a SwiftUI iOS navigation app and want a custom final waypoint image and custom waypoint styling. The Mapbox AdditionalExamples look UIKit-only — do I have to switch to UIKit?",
"expectations": [
"Says no — these are NavigationMapView APIs, so they are stack-independent even though AdditionalExamples hosts them in UIKit",
"Recommends wrapping NavigationMapView in UIViewRepresentable and configuring waypoint / final-waypoint styling on that view",
"Does not tell the user to abandon SwiftUI or rebuild the whole app in UIKit",
"May contrast true NVC chrome (top/bottom bars, styled UI elements, embed NavigationViewController) as the things that are drop-in UIKit UI"
]
}
]
}
Loading
Loading