Skip to content

Commit d5f5716

Browse files
authored
Merge pull request #7 from mapbox/cw/add-ios-nav
add source code for the ios-navigation tutorial
2 parents a9a781f + d110755 commit d5f5716

File tree

18 files changed

+1165
-0
lines changed

18 files changed

+1165
-0
lines changed

ios-navigation/.gitignore

Whitespace-only changes.

ios-navigation/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
## Add turn-by-turn navigation to an iOS app
2+
3+
This repo is a demo application to help developers understand how to use [Mapbox Navigation SDK for iOS](https://docs.mapbox.com/ios/navigation) in an iOS application using SwiftUI.
4+
5+
The corresponding walk-through tutorial is available on [docs.mapbox.com](https://docs.mapbox.com/help/tutorials/ios-navigation/).
6+
7+
The app demonstrates the use of [`NavigationViewController`](https://docs.mapbox.com/ios/navigation/api/3.8.1/navigation/documentation/mapboxnavigationuikit/navigationviewcontroller) as a "drop-in" navigation experience for iOS. The user can choose from a list of predefined destinations. Tapping one passes the current device location coordinates and the destination coordinates to `NavigationLoader`, which calculates routes. The routes are then used to render `NavigationViewController` which presents turn-by-turn navigation with voice prompts in a dismissable fullscreen view.
8+
9+
10+
### Requirements
11+
- A [Mapbox Account](https://console.mapbox.com) and Access Token
12+
- xCode
13+
14+
#### Add your Access Token
15+
As outlined in the [Install Guide](https://docs.mapbox.com/ios/maps/guides/install/) you **must** configure your public token to make requests to Mapbox services. Add your access token to the `Marker-App-Info.plist` file and replace `YOUR_MAPBOX_ACCESS_TOKEN` with your public token. Find your token in the [Mapbox Console](https://console.mapbox.com).
16+

ios-navigation/ios-navigation.xcodeproj/project.pbxproj

Lines changed: 644 additions & 0 deletions
Large diffs are not rendered by default.

ios-navigation/ios-navigation.xcodeproj/project.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>

ios-navigation/ios-navigation.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 104 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"colors" : [
3+
{
4+
"idiom" : "universal"
5+
}
6+
],
7+
"info" : {
8+
"author" : "xcode",
9+
"version" : 1
10+
}
11+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"images" : [
3+
{
4+
"idiom" : "universal",
5+
"platform" : "ios",
6+
"size" : "1024x1024"
7+
}
8+
],
9+
"info" : {
10+
"author" : "xcode",
11+
"version" : 1
12+
}
13+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import SwiftUI
2+
import CoreLocation
3+
import Combine
4+
import MapboxNavigationCore
5+
6+
// LocationManager handles the device's location updates
7+
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
8+
private let manager = CLLocationManager()
9+
10+
// Publishes the current location to subscribers
11+
@Published var currentLocation: CLLocationCoordinate2D?
12+
13+
// Initializes the CLLocationManager and starts updating location
14+
override init() {
15+
super.init()
16+
manager.delegate = self
17+
manager.desiredAccuracy = kCLLocationAccuracyBest
18+
manager.requestWhenInUseAuthorization() // Request location access permission from the user
19+
manager.startUpdatingLocation() // Start receiving location updates
20+
}
21+
22+
// Delegate method called when location is updated
23+
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
24+
currentLocation = locations.last?.coordinate // Store the most recent location
25+
}
26+
27+
// Delegate method called when location updates fail
28+
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
29+
print("Failed to get location: \(error)") // Print error if location fails
30+
}
31+
}
32+
33+
// Represents a destination point with name and coordinates
34+
struct Destination: Identifiable {
35+
let id = UUID()
36+
let name: String
37+
let coordinates: CLLocationCoordinate2D
38+
}
39+
40+
//
41+
struct PreparedNavigation: Identifiable {
42+
let id = UUID()
43+
let routes: NavigationRoutes
44+
let navigationProvider: MapboxNavigationProvider
45+
}
46+
47+
// List of predefined destination points
48+
let destinations = [
49+
Destination(name: "Columbus Circle", coordinates: CLLocationCoordinate2D(latitude: 40.76804, longitude: -73.98190)),
50+
Destination(name: "Empire State Building", coordinates: CLLocationCoordinate2D(latitude: 40.74843, longitude: -73.98568)),
51+
Destination(name: "Brooklyn Bridge Park", coordinates: CLLocationCoordinate2D(latitude: 40.70223, longitude: -73.99656)),
52+
]
53+
54+
// Main view that displays a list of destinations to choose from
55+
struct DestinationListView: View {
56+
@State private var preparedNavigation: PreparedNavigation? = nil
57+
@StateObject private var locationManager = LocationManager() // Observes the user's current location
58+
private let navigationLoader = NavigationLoader()
59+
60+
var body: some View {
61+
NavigationStack {
62+
List(destinations) { destination in
63+
Button(action: {
64+
// Check if the current location is available before proceeding
65+
guard let origin = locationManager.currentLocation else {
66+
print("Current location not available yet.")
67+
return
68+
}
69+
70+
Task {
71+
let destinationCoord = destination.coordinates
72+
if let result = try? await navigationLoader.loadNavigation(from: origin, to: destinationCoord) {
73+
preparedNavigation = result
74+
let distance = result.routes.mainRoute.route.distance
75+
print("✅ Navigation to \(destination.name) ready: route distance: \(distance) meters.")
76+
}
77+
}
78+
79+
}) {
80+
// Layout for each destination button
81+
HStack {
82+
Image(systemName: "location.fill").foregroundColor(.blue)
83+
Text(destination.name)
84+
Spacer()
85+
Image(systemName: "arrow.turn.up.right").foregroundColor(.gray)
86+
}
87+
.padding(.vertical, 8)
88+
}
89+
}
90+
.navigationTitle("Choose Destination") // Title for the navigation stack
91+
// Present the navigation view full screen when OD pair is set
92+
.fullScreenCover(item: $preparedNavigation) { preparedNavigation in
93+
NavigationViewWrapper(
94+
preparedNavigation: preparedNavigation,
95+
onCancel: {
96+
self.preparedNavigation = nil // Reset when user cancels
97+
}
98+
)
99+
.edgesIgnoringSafeArea(.all) // Make the navigation view full screen
100+
}
101+
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)