> ## Documentation Index
> Fetch the complete documentation index at: https://docs.baato.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Turn-by-turn navigation

> Add turn-by-turn navigation to an iOS app with Swift.

Follow these instructions for integrating navigation in iOS app with mapbox navigation.

Your podfile should look like the following. Follow cocoapods documentation for installing pods.

```ruby theme={null}
source 'https://github.com/baato/BaatoPodSpec.git'
source 'https://github.com/CocoaPods/Specs.git'
target '${YourApp}' do
  use_frameworks!

  # Pods for ${YourApp}
  pod 'BaatoSwift', '~> ${LatestVersion}'

  # You can use latest mapbox-ios-sdk and mapboxnavigation
  pod 'Mapbox-iOS-SDK', '~> 6.2.1'
  pod 'MapboxNavigation', '~> 1.1'

end
```

### Usage examples

Go to project target -> Signing & Capabilities, add the following Background Modes from Capability

1. Audio, Airplay, and Picture in Picture
2. Location updates

## Map and navigation delegates

Add a delegate method MGLMapViewDelegate, NavigationMapViewDelegate and NavigationViewControllerDelegate. And initialize router, service and mapstyle.

```swift theme={null}
class ViewController: UIViewController, MGLMapViewDelegate, NavigationViewControllerDelegate, NavigationMapViewDelegate  {
    // Initializing parameter for navigation
    let mytStyle = DayStyle()
    var navigationViewController: NavigationViewController?
    var navigationService: NavigationService!
    var route: Route!
    var router: LegacyRouteController!
    var startPoint = MGLPointAnnotation()
    var endPoint = MGLPointAnnotation()
}
```

## Requesting a baato route

We have to parse directionRoute for mapbox navigation with the help of ResType and RouteResDecoder class. Create a copy of the following classes

### ResType.swift

```swift theme={null}
import Foundation
// MARK: - Welcome
public class ResType: NSObject, NSCoding, Codable {
    let timestamp: String
    let status: Int
    let message: String
    var data: RouteResDecoder

    // coding keys for Decodable
    enum CodingKeys: String, CodingKey {
        case timestamp
        case status
        case message
        case data
    }

    public func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)
            try container.encode(timestamp, forKey: .timestamp)
            try container.encode(status, forKey: .status)
            try container.encode(message, forKey: .message)
        }

        // MARK: Decodable

    required public init(from decoder: Decoder) throws {
            // get container
            let container = try decoder.container(keyedBy: CodingKeys.self)

            // get properties
            timestamp = try container.decode(String.self, forKey: .timestamp)
            status = try container.decode(Int.self, forKey: .status)
            message = try container.decode(String.self, forKey: .message)
            data = try container.decode(RouteResDecoder.self, forKey: .data)

        }

    public func encode(with aCoder: NSCoder) {
            aCoder.encode(timestamp, forKey: CodingKeys.timestamp.rawValue)
            aCoder.encode(status, forKey: CodingKeys.status.rawValue)
            aCoder.encode(message, forKey: CodingKeys.message.rawValue)
            aCoder.encode(data, forKey: CodingKeys.data.rawValue)
        }
        init(timestamp: String, status: Int, message: String, data: RouteResDecoder) {
            self.timestamp = timestamp
            self.status = status
            self.message = message
            self.data = data
        }

    required  public init?(coder aDecoder: NSCoder) {
            timestamp = aDecoder.decodeObject(forKey: CodingKeys.timestamp.rawValue) as! String
            status = aDecoder.decodeInteger(forKey: CodingKeys.status.rawValue)
            message = aDecoder.decodeObject(forKey: CodingKeys.message.rawValue) as! String
            data =    aDecoder.decodeObject(forKey: CodingKeys.data.rawValue) as! RouteResDecoder
        }

}
```

### RouteResDecoder.swift

```swift theme={null}
import Foundation
import MapboxDirections
import MapboxCoreNavigation

public struct RouteResDecoder {
    public let httpResponse: HTTPURLResponse?

    public let identifier: String?
    public var routes: [Route]?
    public let waypoints: [Waypoint]?
    /**
     The time when this `RouteResponse` object was created, which is immediately upon recieving the raw URL response.

     If you manually start fetching a task returned by `Directions.url(forCalculating:)`, this property is set to `nil`; use the `URLSessionTaskTransactionMetrics.responseEndDate` property instead. This property may also be set to `nil` if you create this result from a JSON object or encoded object.

     This property does not persist after encoding and decoding.
     */
    public var created: Date = Date()
}

extension RouteResDecoder: Codable {
    enum CodingKeys: String, CodingKey {
        case code
        case message
        case error
        case identifier = "uuid"
        case routes
        case waypoints
    }

    public init(httpResponse: HTTPURLResponse?, identifier: String? = nil, routes: [Route]? = nil, waypoints: [Waypoint]? = nil) {
        self.httpResponse = httpResponse
        self.identifier = identifier
        self.routes = routes
        self.waypoints = waypoints
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        self.httpResponse = decoder.userInfo[.httpResponse] as? HTTPURLResponse
        self.identifier = try container.decodeIfPresent(String.self, forKey: .identifier)

        // Decode waypoints from the response and update their names according to the waypoints from DirectionsOptions.waypoints.
        let decodedWaypoints = try container.decodeIfPresent([Waypoint?].self, forKey: .waypoints)?.compactMap{ $0 }
        var optionsWaypoints: [Waypoint] = []

        if let decodedWaypoints = decodedWaypoints {
            // The response lists the same number of tracepoints as the waypoints in the request, whether or not a given waypoint is leg-separating.
            waypoints = zip(decodedWaypoints, optionsWaypoints).map { (pair) -> Waypoint in
                let (decodedWaypoint, waypointInOptions) = pair
                let waypoint = Waypoint(coordinate: decodedWaypoint.coordinate,
                                        coordinateAccuracy: waypointInOptions.coordinateAccuracy,
                                        name: waypointInOptions.name?.nonEmptyString ?? decodedWaypoint.name)

                waypoint.targetCoordinate = waypointInOptions.targetCoordinate
                waypoint.heading = waypointInOptions.heading
                waypoint.headingAccuracy = waypointInOptions.headingAccuracy
                waypoint.separatesLegs = waypointInOptions.separatesLegs
                waypoint.allowsArrivingOnOppositeSide = waypointInOptions.allowsArrivingOnOppositeSide

                return waypoint
            }
            waypoints?.first?.separatesLegs = true
            waypoints?.last?.separatesLegs = true
        } else {
            waypoints = decodedWaypoints
        }

        if let routes = try container.decodeIfPresent([Route].self, forKey: .routes) {
            // Postprocess each route.
            for route in routes {
                route.routeIdentifier = identifier
                // Imbue each route’s legs with the waypoints refined above.
//                if let waypoints = waypoints {
//                    route.legSeparators = waypoints.filter { $0.separatesLegs }
//                }
            }
            self.routes = routes
        } else {
            routes = nil
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encodeIfPresent(identifier, forKey: .identifier)
        try container.encodeIfPresent(routes, forKey: .routes)
        try container.encodeIfPresent(waypoints, forKey: .waypoints)
    }

}
```

Request a route and decode with above decoder

```swift theme={null}
// Initialize Baato with your token
let apis = BaatoSwift.API.init(token: "YOUR_BAATO_ACCESS_TOKEN")

//startPoint and endPoint are MGLPointAnnotation()

apis.startLat = startPoint.coordinate.latitude
apis.startLon = startPoint.coordinate.longitude
apis.destLat = endPoint.coordinate.latitude
apis.destLon = endPoint.coordinate.longitude
apis.navMode = navMode
apis.navInstructions = true

apis.getMapboxDirections{(result) in
                self.manager.dismissAndCallCompletionBlocks(withCategory: .none)
                            switch result {
                            case .success (let data):
                                guard let data = data else {
                                    return
                                }

                                let origin = Waypoint(coordinate: self.startPoint.coordinate, name: "Origin")
                                let destination = Waypoint(coordinate: self.endPoint.coordinate, name: "Destination")
                                 let routeOptions = NavigationRouteOptions(waypoints: [origin, destination])
                                routeOptions.distanceMeasurementSystem = .metric
                                let decoder = JSONDecoder()
                                decoder.userInfo = [.options: routeOptions]
                                guard let value = try? decoder.decode(ResType.self, from: data) else {
                                    print("Error parsing JSON")
                                    return
                                }
                                //save a route object so that it can be used while rerouting
                                self.route = value.data.routes![0]
                                //self.drawRoute(route: (value.data.routes![0].shape)!)
                                //self.bottomRouteInfo()
                                self.startNavigation(startPoint: startPoint, endPoint: endPoint)
                            case .failure (let error) :
                                print(error.localizedDescription)
                                self.mapError(error: error)
                                self.handleRouteEvent()
                            }
            }
```

### Starting navigation

Create and assign parameter to navigationviewcontroller

```swift theme={null}
 private func startNav(startPoint: MGLPointAnnotation, endPoint: MGLPointAnnotation){

        let origin = Waypoint(coordinate: startPoint.coordinate, name: "My Location")
        let destination = Waypoint(coordinate: endPoint.coordinate, name: "Destination")
          routeOptions = NavigationRouteOptions(waypoints: [origin, destination])
        routeOptions!.distanceMeasurementSystem = .metric
        navigationService = MapboxNavigationService(route: self.route, routeIndex: 0, routeOptions: routeOptions!, simulating: .onPoorGPS, routerType: LegacyRouteController.self)
        router = LegacyRouteController(along: self.route, routeIndex: 0, options: routeOptions!, dataSource: navigationService)

       let navigationOptions = NavigationOptions(styles: [mytStyle], navigationService: navigationService)
        self.navigationViewController = NavigationViewController(for: self.route, routeIndex: 0, routeOptions: routeOptions!, navigationOptions: navigationOptions)
        self.navigationViewController?.modalPresentationStyle = .fullScreen
        self.navigationViewController?.delegate = self
//        self.navigationService?.delegate = self
        self.navigationViewController?.routeLineTracksTraversal = false
        self.navigationViewController?.showsReportFeedback = false
        self.navigationViewController?.mapView?.logoView.image = UIImage(named:"baatologo")
        self.navigationViewController?.mapView?.attributionButton.isHidden = true
        self.present(self.navigationViewController!, animated: false, completion: nil)
    }
```

App will crash so unlock MapboxDirections package DirectionCredentials.swift and replace initializer as

```swift theme={null}
public init(accessToken token: String? = nil, host: URL? = nil) {
        self.accessToken = "pk.xxx"
        self.host = URL(string: "https://api.baato.io")!
    }
```

Also unlock MaboxCoreNavigation package NavigationSetting.swift so, that the metric system is always taken

```swift theme={null}

    var usesMetric: Bool {
        get {
            return true
        }
    }
```

After that rebuild the project and run, the navigation will start.

### Re-routing

Override shouldReroute from NavigationViewControllerDelegate

```swift theme={null}
extension ViewController: NavigationViewControllerDelegate {

    func navigationViewController(_ navigationViewController: NavigationViewController, didRerouteAlong route: Route) {
        print("reroute captured")
    }

    // Never reroute internally. Instead,
    // 1. Fetch a route from your server
    // 2. Map Match the coordinates from your server
    // 3. Set the route on your server
    func navigationViewController(_ navigationViewController: NavigationViewController, shouldRerouteFrom location: CLLocation) -> Bool {

        let routeOptions = NavigationRouteOptions(waypoints: [Waypoint(location: location), self.routeOptions!.waypoints.last!])
        let startPoint = MGLPointAnnotation()
        guard let userLocation = self.mView.userLocation else {
            return false
        }
        startPoint.coordinate = userLocation.coordinate
        startPoint.title = "Origin"

        apis.startLat = startPoint.coordinate.latitude
        apis.startLon = startPoint.coordinate.longitude
        apis.destLat = endPoint.coordinate.latitude
        apis.destLon = endPoint.coordinate.longitude

        apis.getMapboxDirections{(result) in
            self.manager.dismissAndCallCompletionBlocks(withCategory: .none)
                        switch result {
                        case .success (let data):
                            guard let data = data else {
                                return
                            }

                            let origin = Waypoint(coordinate: self.startPoint.coordinate, name: "Origin")
                            let destination = Waypoint(coordinate: self.endPoint.coordinate, name: "Destination")
                             let routeOptions = NavigationRouteOptions(waypoints: [origin, destination])
                            let decoder = JSONDecoder()
                            routeOptions.distanceMeasurementSystem = .metric
                            decoder.userInfo = [.options: routeOptions]
                            guard let value = try? decoder.decode(ResType.self, from: data) else {
                                print("Error parsing JSON")
                                return
                            }
                            let route = value.data.routes![0]
                            let router = self.navigationService.router! as! LegacyRouteController
                            self.router = router
                            let routeProgress = RouteProgress(route: route, routeIndex: 0, options: routeOptions)
                            self.router.routeProgress = routeProgress

                        case .failure (let error) :
                            print(error.localizedDescription)
                        }
        }
        return false
    }
    func navigationViewControllerDidDismiss(_ navigationViewController: NavigationViewController, byCanceling canceled: Bool) {
        navigationService.stop()
        dismiss(animated: true, completion: nil)
        }
}
```
