Directions API beta query parameters
Demonstrates how to subclass NavigationRouteOptions to take advantage of the beta query parameters available from the Directions API.
/*This code example is part of the Mapbox Navigation SDK for iOS demo app,which you can build and run: https://github.com/mapbox/mapbox-navigation-ios-examplesTo learn more about each example in this app, including descriptions and linksto documentation, see our docs: https://docs.mapbox.com/ios/navigation/examples*/ import UIKitimport MapboxCoreNavigationimport MapboxNavigationimport MapboxDirectionsimport MapboxMaps class BetaQueryViewController: UIViewController, NavigationMapViewDelegate, NavigationViewControllerDelegate { var navigationMapView: NavigationMapView! var routeResponse: RouteResponse? {didSet {guard let routes = routeResponse?.routes, let currentRoute = routes.first else {navigationMapView.removeRoutes()return}navigationMapView.show(routes)navigationMapView.showWaypoints(on: currentRoute)}} var startButton: UIButton!var datePicker: UIDatePicker!var dateTextField: UITextField!var departureTime: Date! // MARK: - UIViewController lifecycle methods override func viewDidLoad() {super.viewDidLoad() navigationMapView = NavigationMapView(frame: view.bounds)navigationMapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]navigationMapView.delegate = selfnavigationMapView.userLocationStyle = .puck2D() let navigationViewportDataSource = NavigationViewportDataSource(navigationMapView.mapView, viewportDataSourceType: .raw)navigationViewportDataSource.options.followingCameraOptions.zoomUpdatesAllowed = falsenavigationViewportDataSource.followingMobileCamera.zoom = 13.0navigationMapView.navigationCamera.viewportDataSource = navigationViewportDataSource view.addSubview(navigationMapView) startButton = UIButton()startButton.setTitle("Start Navigation", for: .normal)startButton.translatesAutoresizingMaskIntoConstraints = falsestartButton.backgroundColor = .bluestartButton.contentEdgeInsets = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)startButton.addTarget(self, action: #selector(tappedStartButton(sender:)), for: .touchUpInside)startButton.isHidden = trueview.addSubview(startButton) startButton.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -20).isActive = truestartButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = trueview.setNeedsLayout() setupDateProperties()} // Override layout lifecycle callback to be able to style the start button.override func viewDidLayoutSubviews() {super.viewDidLayoutSubviews() startButton.layer.cornerRadius = startButton.bounds.midYstartButton.clipsToBounds = truestartButton.setNeedsDisplay()} func setupDateProperties() {dateTextField = UITextField(frame: CGRect(x: 75, y: 100, width: 200, height: 35))dateTextField.placeholder = "Select departure time"dateTextField.backgroundColor = UIColor.whitedateTextField.borderStyle = .roundedRectdateTextField.center.x = view.center.xdateTextField.isHidden = falseshowDatePicker()view.addSubview(dateTextField)} func showDatePicker() {datePicker = UIDatePicker()datePicker.datePickerMode = .timeif #available(iOS 13.4, *) {datePicker.preferredDatePickerStyle = .wheels}datePicker.minimumDate = Date() let toolbar = UIToolbar()toolbar.sizeToFit() let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(doneButtonPressed))toolbar.setItems([doneButton], animated: true) dateTextField?.inputAccessoryView = toolbardateTextField?.inputView = datePicker} @objc func doneButtonPressed() {let dateFormatter = DateFormatter()dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm" // format date correctlydateTextField.text = dateFormatter.string(from: datePicker.date)self.view.endEditing(true) // only allow user to request route after selecting departure timelet gesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))navigationMapView.addGestureRecognizer(gesture)} @objc func tappedStartButton(sender: UIButton) {guard let routeResponse = routeResponse else { return } // For demonstration purposes, simulate locations if the Simulate Navigation option is on.let indexedRouteResponse = IndexedRouteResponse(routeResponse: routeResponse, routeIndex: 0)let navigationService = MapboxNavigationService(indexedRouteResponse: indexedRouteResponse,customRoutingProvider: NavigationSettings.shared.directions,credentials: NavigationSettings.shared.directions.credentials,simulating: simulationIsEnabled ? .always : .onPoorGPS)let navigationOptions = NavigationOptions(navigationService: navigationService)let navigationViewController = NavigationViewController(for: indexedRouteResponse,navigationOptions: navigationOptions)navigationViewController.delegate = self present(navigationViewController, animated: true, completion: nil)} @objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {guard gesture.state == .ended else { return }let location = navigationMapView.mapView.mapboxMap.coordinate(for: gesture.location(in: navigationMapView.mapView)) requestRoute(destination: location)} func requestRoute(destination: CLLocationCoordinate2D) {guard let userLocation = navigationMapView.mapView.location.latestLocation else { return } let location = CLLocation(latitude: userLocation.coordinate.latitude,longitude: userLocation.coordinate.longitude) let userWaypoint = Waypoint(location: location,heading: userLocation.heading,name: "user") let destinationWaypoint = Waypoint(coordinate: destination)let navigationRouteOptions = MopedRouteOptions(waypoints: [userWaypoint, destinationWaypoint], departTime: dateTextField.text!) Directions.shared.calculate(navigationRouteOptions) { [weak self] (_, result) inswitch result {case .failure(let error):print(error.localizedDescription)case .success(let response):guard let routes = response.routes,let currentRoute = routes.first,let self = self else { return } self.routeResponse = responseself.startButton?.isHidden = falseself.dateTextField?.isHidden = trueself.navigationMapView.show(routes)self.navigationMapView.showWaypoints(on: currentRoute)}}} func navigationViewControllerDidDismiss(_ navigationViewController: NavigationViewController, byCanceling canceled: Bool) {dismiss(animated: true, completion: nil)}} class MopedRouteOptions: NavigationRouteOptions {var departureTime: String! // add departureTime to URLQueryItemsoverride var urlQueryItems: [URLQueryItem] {var items = super.urlQueryItemsitems.append(URLQueryItem(name: "depart_at", value: departureTime))return items} // create initializer to take in the departure timepublic init(waypoints: [Waypoint], departTime: String) {departureTime = departTimesuper.init(waypoints: waypoints)} required init(from decoder: Decoder) throws {fatalError("init(from:) has not been implemented")} required init(waypoints: [Waypoint], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) {fatalError("init(waypoints:profileIdentifier:) has not been implemented")} required init(waypoints: [Waypoint], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic, queryItems: [URLQueryItem]? = nil) {fatalError("init(waypoints:profileIdentifier:queryItems:) has not been implemented")}}