diff --git a/CarPlayExample/AppDelegate+CPTemplateApplicationSceneDelegate.swift b/CarPlayExample/AppDelegate+CPTemplateApplicationSceneDelegate.swift new file mode 100644 index 00000000..885bc686 --- /dev/null +++ b/CarPlayExample/AppDelegate+CPTemplateApplicationSceneDelegate.swift @@ -0,0 +1,38 @@ +import CarPlay + +// MARK: - CPTemplateApplicationSceneDelegate methods + +extension AppDelegate: CPTemplateApplicationSceneDelegate { + + func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, + didConnect interfaceController: CPInterfaceController, + to window: CPWindow) { + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + + appDelegate.carPlayManager.delegate = appDelegate + + appDelegate.carPlayManager.application(UIApplication.shared, + didConnectCarInterfaceController: interfaceController, + to: window) + + appDelegate.carPlayManager.templateApplicationScene(templateApplicationScene, + didConnectCarInterfaceController: interfaceController, + to: window) + } + + func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, + didDisconnect interfaceController: CPInterfaceController, + from window: CPWindow) { + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + + appDelegate.carPlayManager.delegate = nil + + appDelegate.carPlayManager.application(UIApplication.shared, + didDisconnectCarInterfaceController: interfaceController, + from: window) + + appDelegate.carPlayManager.templateApplicationScene(templateApplicationScene, + didDisconnectCarInterfaceController: interfaceController, + from: window) + } +} diff --git a/CarPlayExample/AppDelegate+CarPlayManagerDelegate.swift b/CarPlayExample/AppDelegate+CarPlayManagerDelegate.swift new file mode 100644 index 00000000..0545eb00 --- /dev/null +++ b/CarPlayExample/AppDelegate+CarPlayManagerDelegate.swift @@ -0,0 +1,219 @@ +import CarPlay +import MapboxCoreNavigation +import MapboxNavigation +import MapboxDirections +import MapboxMaps + +// MARK: - CarPlayManagerDelegate methods + +extension AppDelegate: CarPlayManagerDelegate { + + // Delegate method, which allows to provide list of leading `CPBarButton`s for specific `CarPlayActivity`. + // It's possible to provide up to two leading `CPBarButton`s. + func carPlayManager(_ carPlayManager: CarPlayManager, + leadingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, + in carPlayTemplate: CPTemplate, + for activity: CarPlayActivity) -> [CPBarButton]? { + guard let interfaceController = self.carPlayManager.interfaceController else { return nil } + + switch activity { + + case .browsing: + let searchTemplate = CPSearchTemplate() + searchTemplate.delegate = carPlaySearchController + let searchButton = carPlaySearchController.searchTemplateButton(searchTemplate: searchTemplate, + interfaceController: interfaceController, + traitCollection: traitCollection) + return [searchButton] + case .panningInBrowsingMode: + break + + case .panningInNavigationMode: + break + + case .previewing: + break + + case .navigating: + break + } + + return [] + } + + // Delegate method, which allows to provide list of trailing `CPBarButton`s for specific `CarPlayActivity`. + // It's possible to provide up to two trailing `CPBarButton`s. + func carPlayManager(_ carPlayManager: CarPlayManager, + trailingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, + in carPlayTemplate: CPTemplate, + for activity: CarPlayActivity) -> [CPBarButton]? { + switch activity { + + case .browsing: + break + + case .panningInBrowsingMode: + break + + case .panningInNavigationMode: + break + + case .previewing: + break + + case .navigating: + return [carPlayManager.exitButton] + } + + return [] + } + + // Delegate method, which allows to provide a list of `CPMapButton`, which are shown on a map. + // It's possible to provide up to four `CPMapButton`s. + func carPlayManager(_ carPlayManager: CarPlayManager, + mapButtonsCompatibleWith traitCollection: UITraitCollection, + in carPlayTemplate: CPTemplate, + for activity: CarPlayActivity) -> [CPMapButton]? { + switch activity { + + case .browsing: + break + + case .panningInBrowsingMode: + break + + case .panningInNavigationMode: + break + + case .previewing: + break + + case .navigating: + break + } + + return [] + } + + func carPlayManager(_ carPlayManager: CarPlayManager, + didFailToFetchRouteBetween waypoints: [Waypoint]?, + options: RouteOptions, + error: DirectionsError) -> CPNavigationAlert? { + let alertAction = CPAlertAction(title: "Dismiss", style: .default, handler: { _ in }) + + let navigationAlert = CPNavigationAlert(titleVariants: ["Failed to fetch"], + subtitleVariants: nil, + image: nil, + primaryAction: alertAction, + secondaryAction: nil, + duration: 2.5) + + return navigationAlert + } + + func carPlayManager(_ carPlayManager: CarPlayManager, + willPreview trip: CPTrip) -> CPTrip { + return trip + } + + func carPlayManager(_ carPlayManager: CarPlayManager, + willPreview trip: CPTrip, + with previewTextConfiguration: CPTripPreviewTextConfiguration) -> CPTripPreviewTextConfiguration { + return previewTextConfiguration + } + + func carPlayManager(_ carPlayManager: CarPlayManager, + selectedPreviewFor trip: CPTrip, + using routeChoice: CPRouteChoice) { + + } + + func carPlayManager(_ carPlayManager: CarPlayManager, + didBeginNavigationWith service: NavigationService) { + + } + + // Delegate method, which is called after ending active-guidance navigation session and dismissing + // `CarPlayNavigationViewController`. + func carPlayManagerDidEndNavigation(_ carPlayManager: CarPlayManager) { + let alertAction = CPAlertAction(title: "OK", + style: .default, + handler: { [weak self] _ in + self?.carPlayManager.interfaceController?.dismissTemplate(animated: true) + }) + + let alertTemplate = CPAlertTemplate(titleVariants: ["Did end active-guidance navigation."], + actions: [alertAction]) + + carPlayManager.interfaceController?.presentTemplate(alertTemplate, animated: true) + } + + // Delegate method, which allows to show `CPActionSheetTemplate` or `CPAlertTemplate` + // after arriving to the specific `Waypoint`. + func carPlayManager(_ carPlayManager: CarPlayManager, + shouldPresentArrivalUIFor waypoint: Waypoint) -> Bool { + return true + } + + // Delegate method, which provides the ability to disable the idle timer to avert system sleep. + func carPlayManagerShouldDisableIdleTimer(_ carPlayManager: CarPlayManager) -> Bool { + return true + } + + // Delegate method, which is called right after starting active-guidance navigation and presenting + // `CarPlayNavigationViewController`. + func carPlayManager(_ carPlayManager: CarPlayManager, + didPresent navigationViewController: CarPlayNavigationViewController) { + let alertAction = CPAlertAction(title: "OK", + style: .default, + handler: { [weak self] _ in + self?.carPlayManager.interfaceController?.dismissTemplate(animated: true) + }) + + let alertTemplate = CPAlertTemplate(titleVariants: ["Did present CarPlayNavigationViewController."], + actions: [alertAction]) + + carPlayManager.interfaceController?.presentTemplate(alertTemplate, animated: true) + } + + // Delegate method, which allows to modify final destination annotation whenever its added to + // `CarPlayMapViewController` or `CarPlayNavigationViewController`. + func carPlayManager(_ carPlayManager: CarPlayManager, + didAdd finalDestinationAnnotation: PointAnnotation, + to parentViewController: UIViewController, + pointAnnotationManager: PointAnnotationManager) { + var finalDestinationAnnotation = finalDestinationAnnotation + if let image = UIImage(named: "marker") { + finalDestinationAnnotation.image = .init(image: image, name: "marker") + } else { + let image = UIImage(named: "default_marker", in: .mapboxNavigation, compatibleWith: nil)! + finalDestinationAnnotation.image = .init(image: image, name: "marker") + } + + pointAnnotationManager.annotations = [finalDestinationAnnotation] + } + + func carPlayManager(_ carPlayManager: CarPlayManager, + templateWillAppear template: CPTemplate, + animated: Bool) { + + } + + func carPlayManager(_ carPlayManager: CarPlayManager, + templateDidAppear template: CPTemplate, + animated: Bool) { + + } + + func carPlayManager(_ carPlayManager: CarPlayManager, + templateWillDisappear template: CPTemplate, + animated: Bool) { + + } + + func carPlayManager(_ carPlayManager: CarPlayManager, + templateDidDisappear template: CPTemplate, + animated: Bool) { + + } +} diff --git a/CarPlayExample/AppDelegate+CarPlaySearchControllerDelegate.swift b/CarPlayExample/AppDelegate+CarPlaySearchControllerDelegate.swift new file mode 100644 index 00000000..dd0bd1cd --- /dev/null +++ b/CarPlayExample/AppDelegate+CarPlaySearchControllerDelegate.swift @@ -0,0 +1,126 @@ +import CarPlay +import MapboxNavigation +import MapboxDirections +import MapboxGeocoder + +// MARK: - CarPlaySearchControllerDelegate methods + +extension AppDelegate: CarPlaySearchControllerDelegate { + + func previewRoutes(to waypoint: Waypoint, completionHandler: @escaping () -> Void) { + carPlayManager.previewRoutes(to: waypoint, completionHandler: completionHandler) + } + + func resetPanButtons(_ mapTemplate: CPMapTemplate) { + carPlayManager.resetPanButtons(mapTemplate) + } + + func pushTemplate(_ template: CPTemplate, animated: Bool) { + if let listTemplate = template as? CPListTemplate { + listTemplate.delegate = carPlaySearchController + } + carPlayManager.interfaceController?.pushTemplate(template, animated: animated) + } + + func popTemplate(animated: Bool) { + carPlayManager.interfaceController?.popTemplate(animated: animated) + } + + func recentSearches(with searchText: String) -> [CPListItem] { + if searchText.isEmpty { + return recentItems.map { $0.navigationGeocodedPlacemark.listItem() } + } + + return recentItems.filter { + $0.matches(searchText) + }.map { + $0.navigationGeocodedPlacemark.listItem() + } + } + + func searchResults(with items: [CPListItem], limit: UInt?) -> [CPListItem] { + recentSearchItems = items + + if items.count > 0 { + if let limit = limit { + return Array(items.prefix(Int(limit))) + } + + return items + } else { + let noResultListItem = CPListItem(text: "No results", + detailText: nil, + image: nil, + showsDisclosureIndicator: false) + + return [noResultListItem] + } + } + + func searchTemplate(_ searchTemplate: CPSearchTemplate, + updatedSearchText searchText: String, + completionHandler: @escaping ([CPListItem]) -> Void) { + recentSearchText = searchText + + var items = recentSearches(with: searchText) + let limit: UInt = 2 + + if searchText.count > 2 { + + let forwardGeocodeOptions = ForwardGeocodeOptions(query: searchText) + forwardGeocodeOptions.locale = Locale.autoupdatingCurrent.languageCode == "en" ? nil : .autoupdatingCurrent + + var allowedScopes: PlacemarkScope = .all + allowedScopes.remove(.postalCode) + + forwardGeocodeOptions.allowedScopes = allowedScopes + forwardGeocodeOptions.maximumResultCount = 10 + forwardGeocodeOptions.includesRoutableLocations = true + + Geocoder.shared.geocode(forwardGeocodeOptions, + completionHandler: { [weak self] (placemarks, attribution, error) in + guard let self = self else { + completionHandler([]) + return + } + + guard let placemarks = placemarks else { + completionHandler(self.searchResults(with: items, limit: limit)) + return + } + + let navigationGeocodedPlacemarks = placemarks.map { + NavigationGeocodedPlacemark(title: $0.formattedName, + subtitle: $0.address, + location: $0.location, + routableLocations: $0.routableLocations) + } + + let results = navigationGeocodedPlacemarks.map { $0.listItem() } + items.append(contentsOf: results) + completionHandler(self.searchResults(with: results, limit: limit)) + }) + } else { + completionHandler(self.searchResults(with: items, limit: limit)) + } + } + + func searchTemplate(_ searchTemplate: CPSearchTemplate, + selectedResult item: CPListItem, + completionHandler: @escaping () -> Void) { + guard let userInfo = item.userInfo as? [String: Any], + let placemark = userInfo[CarPlaySearchController.CarPlayGeocodedPlacemarkKey] as? NavigationGeocodedPlacemark, + let location = placemark.routableLocations?.first ?? placemark.location else { + completionHandler() + return + } + + recentItems.add(RecentItem(placemark)) + recentItems.save() + + let destinationWaypoint = Waypoint(location: location, + heading: nil, + name: placemark.title) + previewRoutes(to: destinationWaypoint, completionHandler: completionHandler) + } +} diff --git a/CarPlayExample/AppDelegate.swift b/CarPlayExample/AppDelegate.swift new file mode 100644 index 00000000..3324f3d8 --- /dev/null +++ b/CarPlayExample/AppDelegate.swift @@ -0,0 +1,46 @@ +import UIKit +import CarPlay +import MapboxCoreNavigation +import MapboxNavigation +import MapboxDirections +import MapboxMaps + +@main +class AppDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + lazy var carPlayManager: CarPlayManager = CarPlayManager(styles: [CustomStyle()]) + + lazy var carPlaySearchController: CarPlaySearchController = CarPlaySearchController() + + lazy var recentSearchItems: [CPListItem]? = [] + + var recentItems: [RecentItem] = RecentItem.loadDefaults() + + var recentSearchText: String? = "" +} + +// MARK: - UIApplicationDelegate methods + +extension AppDelegate: UIApplicationDelegate { + + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + return true + } + + @available(iOS 13.0, *) + func application(_ application: UIApplication, + configurationForConnecting connectingSceneSession: UISceneSession, + options: UIScene.ConnectionOptions) -> UISceneConfiguration { + if connectingSceneSession.role == .carTemplateApplication { + carPlaySearchController.delegate = self + return UISceneConfiguration(name: "CarPlay Configuration", + sessionRole: connectingSceneSession.role) + } + + return UISceneConfiguration(name: "Default Configuration", + sessionRole: connectingSceneSession.role) + } +} diff --git a/CarPlayExample/Assets.xcassets/AccentColor.colorset/Contents.json b/CarPlayExample/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/CarPlayExample/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CarPlayExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/CarPlayExample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..9221b9bb --- /dev/null +++ b/CarPlayExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CarPlayExample/Assets.xcassets/Contents.json b/CarPlayExample/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/CarPlayExample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CarPlayExample/Assets.xcassets/marker.imageset/Contents.json b/CarPlayExample/Assets.xcassets/marker.imageset/Contents.json new file mode 100644 index 00000000..6a0d2bc4 --- /dev/null +++ b/CarPlayExample/Assets.xcassets/marker.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "marker.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CarPlayExample/Assets.xcassets/marker.imageset/marker.pdf b/CarPlayExample/Assets.xcassets/marker.imageset/marker.pdf new file mode 100644 index 00000000..182f34ee Binary files /dev/null and b/CarPlayExample/Assets.xcassets/marker.imageset/marker.pdf differ diff --git a/CarPlayExample/Base.lproj/LaunchScreen.storyboard b/CarPlayExample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/CarPlayExample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CarPlayExample/Base.lproj/Main.storyboard b/CarPlayExample/Base.lproj/Main.storyboard new file mode 100644 index 00000000..06134660 --- /dev/null +++ b/CarPlayExample/Base.lproj/Main.storyboard @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CarPlayExample/CustomStyle.swift b/CarPlayExample/CustomStyle.swift new file mode 100644 index 00000000..1a413652 --- /dev/null +++ b/CarPlayExample/CustomStyle.swift @@ -0,0 +1,16 @@ +import MapboxNavigation +import MapboxMaps + +class CustomStyle: NightStyle { + + required init() { + super.init() + + mapStyleURL = URL(string: StyleURI.dark.rawValue)! + previewMapStyleURL = URL(string: StyleURI.dark.rawValue)! + } + + override func apply() { + super.apply() + } +} diff --git a/CarPlayExample/Entitlements.plist b/CarPlayExample/Entitlements.plist new file mode 100644 index 00000000..d74cc083 --- /dev/null +++ b/CarPlayExample/Entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.developer.carplay-maps + + + diff --git a/CarPlayExample/Info.plist b/CarPlayExample/Info.plist new file mode 100644 index 00000000..921dc6c5 --- /dev/null +++ b/CarPlayExample/Info.plist @@ -0,0 +1,87 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + CPSupportsDashboardNavigationScene + + LSRequiresIPhoneOS + + MBXAccessToken + PASTE MAPBOX ACCESS TOKEN HERE + NSLocationWhenInUseUsageDescription + Get the user location for navigation purposes. + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + CPTemplateApplicationSceneSessionRoleApplication + + + UISceneClassName + CPTemplateApplicationScene + UISceneConfigurationName + CarPlay Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).AppDelegate + + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UIBackgroundModes + + audio + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/CarPlayExample/SceneDelegate.swift b/CarPlayExample/SceneDelegate.swift new file mode 100644 index 00000000..e68a968a --- /dev/null +++ b/CarPlayExample/SceneDelegate.swift @@ -0,0 +1,6 @@ +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? +} diff --git a/CarPlayExample/ViewController.swift b/CarPlayExample/ViewController.swift new file mode 100644 index 00000000..712e7dd3 --- /dev/null +++ b/CarPlayExample/ViewController.swift @@ -0,0 +1,225 @@ +import UIKit +import MapboxCoreNavigation +import MapboxNavigation +import MapboxDirections + +class ViewController: UIViewController { + + typealias ActionHandler = (UIAlertAction) -> Void + + var navigationMapView: NavigationMapView! + + var navigationRouteOptions: NavigationRouteOptions! + + var routeResponse: RouteResponse? { + didSet { + guard let routes = routeResponse?.routes, let currentRoute = routes.first else { + navigationMapView.removeRoutes() + navigationMapView.removeWaypoints() + waypoints.removeAll() + return + } + + navigationMapView.show(routes) + navigationMapView.showWaypoints(on: currentRoute) + } + } + + var waypoints: [Waypoint] = [] + + // MARK: - UIViewController lifecycle methods + + override func viewDidLoad() { + super.viewDidLoad() + + setupNavigationMapView() + setupPerformActionBarButtonItem() + setupGestureRecognizers() + } + + // MARK: - Setting-up methods + + func setupNavigationMapView() { + navigationMapView = NavigationMapView(frame: view.bounds) + navigationMapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + navigationMapView.delegate = self + navigationMapView.userLocationStyle = .puck2D() + + let navigationViewportDataSource = NavigationViewportDataSource(navigationMapView.mapView, + viewportDataSourceType: .raw) + navigationMapView.navigationCamera.viewportDataSource = navigationViewportDataSource + + view.addSubview(navigationMapView) + } + + func setupPerformActionBarButtonItem() { + let settingsBarButtonItem = UIBarButtonItem(title: NSString(string: "\u{2699}\u{0000FE0E}") as String, + style: .plain, + target: self, + action: #selector(performAction)) + let attributes = [ + NSAttributedString.Key.font: UIFont.systemFont(ofSize: 30) + ] + settingsBarButtonItem.setTitleTextAttributes(attributes, for: .normal) + settingsBarButtonItem.setTitleTextAttributes(attributes, for: .highlighted) + navigationItem.rightBarButtonItem = settingsBarButtonItem + } + + // MARK: - UIGestureRecognizer related methods + + func setupGestureRecognizers() { + let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, + action: #selector(handleLongPress(_:))) + navigationMapView.addGestureRecognizer(longPressGestureRecognizer) + } + + @objc func performAction(_ sender: Any) { + let alertController = UIAlertController(title: "Perform action", + message: "Select specific action to perform it", + preferredStyle: .actionSheet) + + let startNavigation: ActionHandler = { _ in self.startNavigation() } + let removeRoutes: ActionHandler = { _ in self.routeResponse = nil } + + let actions: [(String, UIAlertAction.Style, ActionHandler?)] = [ + ("Start Navigation", .default, startNavigation), + ("Remove Routes", .default, removeRoutes), + ("Cancel", .cancel, nil) + ] + + actions + .map({ payload in UIAlertAction(title: payload.0, style: payload.1, handler: payload.2) }) + .forEach(alertController.addAction(_:)) + + if let popoverController = alertController.popoverPresentationController { + popoverController.barButtonItem = navigationItem.rightBarButtonItem + } + + present(alertController, animated: true, completion: nil) + } + + func startNavigation() { + guard let routeResponse = routeResponse, + let navigationRouteOptions = navigationRouteOptions else { + presentAlert(message: "Please select at least one destination coordinate to start navigation.") + return + } + + let navigationService = MapboxNavigationService(routeResponse: routeResponse, + routeIndex: 0, + routeOptions: navigationRouteOptions, + simulating: .always) + let navigationViewController = NavigationViewController(navigationService: navigationService) + navigationViewController.delegate = self + navigationViewController.modalPresentationStyle = .fullScreen + + present(navigationViewController, animated: true) { + let delegate = UIApplication.shared.delegate as? AppDelegate + + if #available(iOS 12.0, *), + let location = navigationService.router.location { + delegate?.carPlayManager.beginNavigationWithCarPlay(using: location.coordinate, + navigationService: navigationService) + } + } + } + + @objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) { + guard gesture.state == .began else { return } + + let mapView = navigationMapView.mapView + createWaypoints(for: mapView?.mapboxMap.coordinate(for: gesture.location(in: mapView))) + requestRoute() + } + + func createWaypoints(for destinationCoordinate: CLLocationCoordinate2D?) { + guard let destinationCoordinate = destinationCoordinate else { return } + guard let userCoordinate = navigationMapView.mapView.location.latestLocation?.coordinate else { + presentAlert(message: "User coordinate is not valid. Make sure to enable Location Services.") + return + } + + // In case if origin waypoint is not present in list of waypoints - add it. + let userLocationName = "User location" + let userWaypoint = Waypoint(coordinate: userCoordinate, name: userLocationName) + if waypoints.first?.name != userLocationName { + waypoints.insert(userWaypoint, at: 0) + } + + // Add destination waypoint to list of waypoints. + let waypoint = Waypoint(coordinate: destinationCoordinate) + waypoint.targetCoordinate = destinationCoordinate + waypoints.append(waypoint) + } + + func requestRoute() { + let navigationRouteOptions = NavigationRouteOptions(waypoints: waypoints) + Directions.shared.calculate(navigationRouteOptions) { [weak self] (_, result) in + guard let self = self else { return } + + switch result { + case .failure(let error): + self.presentAlert(message: error.localizedDescription) + + // In case if direction calculation failed - remove last destination waypoint. + self.waypoints.removeLast() + case .success(let response): + self.routeResponse = response + self.navigationRouteOptions = navigationRouteOptions + } + } + } + + // MARK: - Utility methods + + func presentAlert(_ title: String? = nil, message: String? = nil) { + let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (_) in + alertController.dismiss(animated: true, completion: nil) + })) + + present(alertController, animated: true, completion: nil) + } +} + +// MARK: - NavigationMapViewDelegate methods + +extension ViewController: NavigationMapViewDelegate { + + func navigationMapView(_ mapView: NavigationMapView, didSelect route: Route) { + guard let routes = routeResponse?.routes, + let routeIndex = routes.firstIndex(where: { $0 === route }) else { return } + + routeResponse?.routes?.swapAt(routeIndex, 0) + } +} + +// MARK: - NavigationViewControllerDelegate methods + +extension ViewController: NavigationViewControllerDelegate { + + func navigationViewController(_ navigationViewController: NavigationViewController, + didArriveAt waypoint: Waypoint) -> Bool { + if navigationViewController.navigationService.router.routeProgress.isFinalLeg { + return true + } + + // In case of intermediate waypoint - proceed to next leg only after specific delay. + let delay = 5.0 + DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: { + guard let navigationService = (self.presentedViewController as? NavigationViewController)?.navigationService else { return } + let router = navigationService.router + guard router.route.legs.count > router.routeProgress.legIndex + 1 else { return } + router.routeProgress.legIndex += 1 + + navigationService.start() + }) + + return false + } + + func navigationViewControllerDidDismiss(_ navigationViewController: NavigationViewController, + byCanceling canceled: Bool) { + dismiss(animated: true, completion: nil) + } +} diff --git a/Navigation-Examples.xcodeproj/project.pbxproj b/Navigation-Examples.xcodeproj/project.pbxproj index c87fd042..03c38255 100644 --- a/Navigation-Examples.xcodeproj/project.pbxproj +++ b/Navigation-Examples.xcodeproj/project.pbxproj @@ -19,8 +19,18 @@ 8A2DFA6C261651600034A87E /* Custom-Navigation-Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A2DFA6B261651600034A87E /* Custom-Navigation-Camera.swift */; }; 8A31388726402DF400763714 /* Navigation-From-Segue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A31388626402DF400763714 /* Navigation-From-Segue.swift */; }; 8A33629624E4844E0086C647 /* Building-Extrusion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A33629524E4844E0086C647 /* Building-Extrusion.swift */; }; + 8A3F6B1E26B09C48009EE612 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3F6B1D26B09C48009EE612 /* AppDelegate.swift */; }; + 8A3F6B2026B09C48009EE612 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3F6B1F26B09C48009EE612 /* SceneDelegate.swift */; }; + 8A3F6B2226B09C48009EE612 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3F6B2126B09C48009EE612 /* ViewController.swift */; }; + 8A3F6B2526B09C48009EE612 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8A3F6B2326B09C48009EE612 /* Main.storyboard */; }; + 8A3F6B2726B09C4A009EE612 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8A3F6B2626B09C4A009EE612 /* Assets.xcassets */; }; + 8A3F6B2A26B09C4A009EE612 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8A3F6B2826B09C4A009EE612 /* LaunchScreen.storyboard */; }; 8A96379C2492B366008DEF2A /* Route-Deserialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A96379A2492B366008DEF2A /* Route-Deserialization.swift */; }; 8A96379D2492B366008DEF2A /* route.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A96379B2492B366008DEF2A /* route.json */; }; + 8AB316AD26BB0BAF00C3AC76 /* CustomStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB316AC26BB0BAE00C3AC76 /* CustomStyle.swift */; }; + 8AB316B226BC870B00C3AC76 /* AppDelegate+CarPlayManagerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB316B126BC870B00C3AC76 /* AppDelegate+CarPlayManagerDelegate.swift */; }; + 8AB316B426BC876700C3AC76 /* AppDelegate+CarPlaySearchControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB316B326BC876700C3AC76 /* AppDelegate+CarPlaySearchControllerDelegate.swift */; }; + 8AB316B626BC87D000C3AC76 /* AppDelegate+CPTemplateApplicationSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB316B526BC87D000C3AC76 /* AppDelegate+CPTemplateApplicationSceneDelegate.swift */; }; 8AC9129D2494106100B6941E /* Route-Initialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC9129C2494106100B6941E /* Route-Initialization.swift */; }; 8AE033222628EDFF000E7145 /* Route-Lines-Styling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE033212628EDFF000E7145 /* Route-Lines-Styling.swift */; }; 8DF510741FEB08F70049DB9C /* Embedded-Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF510731FEB08F70049DB9C /* Embedded-Navigation.swift */; }; @@ -52,24 +62,40 @@ C5C0D63820589422003A3B1D /* Custom-Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5C0D63720589422003A3B1D /* Custom-Server.swift */; }; C5F130A01FE9B44600463E86 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F1309F1FE9B44600463E86 /* Constants.swift */; }; C5F130A61FEB2D7800463E86 /* Advanced.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F130A51FEB2D7800463E86 /* Advanced.swift */; }; + D1B95EB8E655F6466A9518CC /* Pods_CarPlayExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CFC17203A5AA0333106A668 /* Pods_CarPlayExample.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 020D595E7751FB434720AACD /* Pods_Navigation_Examples.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Navigation_Examples.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 11B6E81626176F3600872E4D /* Upcoming-Intersection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Upcoming-Intersection.swift"; sourceTree = ""; }; 11DC36F126161DAF0042CD4A /* Location-Snapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Location-Snapping.swift"; sourceTree = ""; }; + 1CFC17203A5AA0333106A668 /* Pods_CarPlayExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CarPlayExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2B521D4D24090A3A00984CF8 /* CustomBarsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBarsViewController.swift; sourceTree = ""; }; 2B521D4F2409240E00984CF8 /* CustomBottomBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBottomBannerView.swift; sourceTree = ""; }; 2B521D512409242A00984CF8 /* CustomBottomBannerView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CustomBottomBannerView.xib; sourceTree = ""; }; 4EF74F60792B221A1A61A3CE /* Pods-Navigation-Examples.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Navigation-Examples.debug.xcconfig"; path = "Target Support Files/Pods-Navigation-Examples/Pods-Navigation-Examples.debug.xcconfig"; sourceTree = ""; }; + 5F805B58F5491201CD2D2D5F /* Pods-CarPlayExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CarPlayExample.debug.xcconfig"; path = "Target Support Files/Pods-CarPlayExample/Pods-CarPlayExample.debug.xcconfig"; sourceTree = ""; }; 8A0B182226553482005107DE /* CustomSegue.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = CustomSegue.storyboard; sourceTree = ""; }; 8A2DFA55261650A60034A87E /* CustomCameraStateTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomCameraStateTransition.swift; sourceTree = ""; }; 8A2DFA56261650A60034A87E /* CustomViewportDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomViewportDataSource.swift; sourceTree = ""; }; 8A2DFA6B261651600034A87E /* Custom-Navigation-Camera.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Custom-Navigation-Camera.swift"; sourceTree = ""; }; 8A31388626402DF400763714 /* Navigation-From-Segue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Navigation-From-Segue.swift"; sourceTree = ""; }; 8A33629524E4844E0086C647 /* Building-Extrusion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Building-Extrusion.swift"; sourceTree = ""; }; + 8A3F6B1B26B09C48009EE612 /* CarPlayExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CarPlayExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8A3F6B1D26B09C48009EE612 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 8A3F6B1F26B09C48009EE612 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 8A3F6B2126B09C48009EE612 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 8A3F6B2426B09C48009EE612 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 8A3F6B2626B09C4A009EE612 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8A3F6B2926B09C4A009EE612 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 8A3F6B2B26B09C4A009EE612 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8A3F6B3326B0B253009EE612 /* Entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Entitlements.plist; sourceTree = ""; }; 8A96379A2492B366008DEF2A /* Route-Deserialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Route-Deserialization.swift"; sourceTree = ""; }; 8A96379B2492B366008DEF2A /* route.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = route.json; sourceTree = ""; }; + 8AB316AC26BB0BAE00C3AC76 /* CustomStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomStyle.swift; sourceTree = ""; }; + 8AB316B126BC870B00C3AC76 /* AppDelegate+CarPlayManagerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+CarPlayManagerDelegate.swift"; sourceTree = ""; }; + 8AB316B326BC876700C3AC76 /* AppDelegate+CarPlaySearchControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+CarPlaySearchControllerDelegate.swift"; sourceTree = ""; }; + 8AB316B526BC87D000C3AC76 /* AppDelegate+CPTemplateApplicationSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+CPTemplateApplicationSceneDelegate.swift"; sourceTree = ""; }; 8AC9129C2494106100B6941E /* Route-Initialization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Route-Initialization.swift"; sourceTree = ""; }; 8AD2F2132489B5C800F81353 /* apply-mapbox-access-token.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "apply-mapbox-access-token.sh"; sourceTree = ""; }; 8AE033212628EDFF000E7145 /* Route-Lines-Styling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Route-Lines-Styling.swift"; sourceTree = ""; }; @@ -110,10 +136,19 @@ C5C0D63720589422003A3B1D /* Custom-Server.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Custom-Server.swift"; sourceTree = ""; }; C5F1309F1FE9B44600463E86 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; C5F130A51FEB2D7800463E86 /* Advanced.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advanced.swift; sourceTree = ""; }; + EBDF518BF4786E6F4DB25952 /* Pods-CarPlayExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CarPlayExample.release.xcconfig"; path = "Target Support Files/Pods-CarPlayExample/Pods-CarPlayExample.release.xcconfig"; sourceTree = ""; }; FAC970C66A93305A51421FFF /* Pods-DocsCode.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DocsCode.release.xcconfig"; path = "Target Support Files/Pods-DocsCode/Pods-DocsCode.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 8A3F6B1826B09C48009EE612 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D1B95EB8E655F6466A9518CC /* Pods_CarPlayExample.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 961FC4FB22E0265300C72877 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -154,6 +189,25 @@ path = NavigationCamera; sourceTree = ""; }; + 8A3F6B1C26B09C48009EE612 /* CarPlayExample */ = { + isa = PBXGroup; + children = ( + 8AB316AC26BB0BAE00C3AC76 /* CustomStyle.swift */, + 8A3F6B1D26B09C48009EE612 /* AppDelegate.swift */, + 8AB316B126BC870B00C3AC76 /* AppDelegate+CarPlayManagerDelegate.swift */, + 8AB316B526BC87D000C3AC76 /* AppDelegate+CPTemplateApplicationSceneDelegate.swift */, + 8AB316B326BC876700C3AC76 /* AppDelegate+CarPlaySearchControllerDelegate.swift */, + 8A3F6B1F26B09C48009EE612 /* SceneDelegate.swift */, + 8A3F6B2126B09C48009EE612 /* ViewController.swift */, + 8A3F6B2326B09C48009EE612 /* Main.storyboard */, + 8A3F6B2626B09C4A009EE612 /* Assets.xcassets */, + 8A3F6B2826B09C4A009EE612 /* LaunchScreen.storyboard */, + 8A3F6B2B26B09C4A009EE612 /* Info.plist */, + 8A3F6B3326B0B253009EE612 /* Entitlements.plist */, + ); + path = CarPlayExample; + sourceTree = ""; + }; 8A5BBF6A2489B0A000FA4960 /* Scripts */ = { isa = PBXGroup; children = ( @@ -205,6 +259,7 @@ C58FB84D1FE899B800C4B491 = { isa = PBXGroup; children = ( + 8A3F6B1C26B09C48009EE612 /* CarPlayExample */, C58FB8581FE899B800C4B491 /* Navigation-Examples */, 961FC4E622E0260A00C72877 /* DocsCode */, 8A5BBF6A2489B0A000FA4960 /* Scripts */, @@ -219,6 +274,7 @@ children = ( C58FB8561FE899B800C4B491 /* Navigation-Examples.app */, 961FC4FE22E0265300C72877 /* DocsCode.app */, + 8A3F6B1B26B09C48009EE612 /* CarPlayExample.app */, ); name = Products; sourceTree = ""; @@ -278,8 +334,9 @@ FAC970C66A93305A51421FFF /* Pods-DocsCode.release.xcconfig */, 4EF74F60792B221A1A61A3CE /* Pods-Navigation-Examples.debug.xcconfig */, A8BC0BE26AF9683D3B034741 /* Pods-Navigation-Examples.release.xcconfig */, + 5F805B58F5491201CD2D2D5F /* Pods-CarPlayExample.debug.xcconfig */, + EBDF518BF4786E6F4DB25952 /* Pods-CarPlayExample.release.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -289,6 +346,7 @@ 8DF510761FEB0CA00049DB9C /* MapboxNavigation.framework */, 020D595E7751FB434720AACD /* Pods_Navigation_Examples.framework */, 9B796D93B6E7934BFD326FA8 /* Pods_DocsCode.framework */, + 1CFC17203A5AA0333106A668 /* Pods_CarPlayExample.framework */, ); name = Frameworks; sourceTree = ""; @@ -296,6 +354,26 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 8A3F6B1A26B09C48009EE612 /* CarPlayExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8A3F6B2E26B09C4A009EE612 /* Build configuration list for PBXNativeTarget "CarPlayExample" */; + buildPhases = ( + 5159D55EA629EFFB8E5F1015 /* [CP] Check Pods Manifest.lock */, + 8A3F6B1726B09C48009EE612 /* Sources */, + 8A3F6B1826B09C48009EE612 /* Frameworks */, + 8A3F6B1926B09C48009EE612 /* Resources */, + 89DFE9A4155B596CE41A9EA3 /* [CP] Embed Pods Frameworks */, + 8A3F6B2F26B09D8F009EE612 /* Apply Mapbox Access Token */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CarPlayExample; + productName = CarPlayExample; + productReference = 8A3F6B1B26B09C48009EE612 /* CarPlayExample.app */; + productType = "com.apple.product-type.application"; + }; 961FC4FD22E0265300C72877 /* DocsCode */ = { isa = PBXNativeTarget; buildConfigurationList = 961FC51122E0265400C72877 /* Build configuration list for PBXNativeTarget "DocsCode" */; @@ -344,10 +422,14 @@ C58FB84E1FE899B800C4B491 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 0920; + LastSwiftUpdateCheck = 1250; LastUpgradeCheck = 1220; ORGANIZATIONNAME = Mapbox; TargetAttributes = { + 8A3F6B1A26B09C48009EE612 = { + CreatedOnToolsVersion = 12.5.1; + ProvisioningStyle = Manual; + }; 961FC4FD22E0265300C72877 = { CreatedOnToolsVersion = 10.2.1; LastSwiftMigration = 1110; @@ -375,11 +457,22 @@ targets = ( C58FB8551FE899B800C4B491 /* Navigation-Examples */, 961FC4FD22E0265300C72877 /* DocsCode */, + 8A3F6B1A26B09C48009EE612 /* CarPlayExample */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 8A3F6B1926B09C48009EE612 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8A3F6B2A26B09C4A009EE612 /* LaunchScreen.storyboard in Resources */, + 8A3F6B2726B09C4A009EE612 /* Assets.xcassets in Resources */, + 8A3F6B2526B09C48009EE612 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 961FC4FC22E0265300C72877 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -452,6 +545,7 @@ "${PODS_ROOT}/Target Support Files/Pods-Navigation-Examples/Pods-Navigation-Examples-frameworks.sh", "${BUILT_PRODUCTS_DIR}/MapboxCoreNavigation/MapboxCoreNavigation.framework", "${BUILT_PRODUCTS_DIR}/MapboxDirections/MapboxDirections.framework", + "${BUILT_PRODUCTS_DIR}/MapboxGeocoder.swift/MapboxGeocoder.framework", "${BUILT_PRODUCTS_DIR}/MapboxMaps/MapboxMaps.framework", "${BUILT_PRODUCTS_DIR}/MapboxNavigation/MapboxNavigation.framework", "${BUILT_PRODUCTS_DIR}/MapboxSpeech/MapboxSpeech.framework", @@ -467,6 +561,7 @@ outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCoreNavigation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxDirections.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxGeocoder.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMaps.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxNavigation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxSpeech.framework", @@ -505,6 +600,89 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 5159D55EA629EFFB8E5F1015 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-CarPlayExample-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 89DFE9A4155B596CE41A9EA3 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-CarPlayExample/Pods-CarPlayExample-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/MapboxCoreNavigation/MapboxCoreNavigation.framework", + "${BUILT_PRODUCTS_DIR}/MapboxDirections/MapboxDirections.framework", + "${BUILT_PRODUCTS_DIR}/MapboxGeocoder.swift/MapboxGeocoder.framework", + "${BUILT_PRODUCTS_DIR}/MapboxMaps/MapboxMaps.framework", + "${BUILT_PRODUCTS_DIR}/MapboxNavigation/MapboxNavigation.framework", + "${BUILT_PRODUCTS_DIR}/MapboxSpeech/MapboxSpeech.framework", + "${BUILT_PRODUCTS_DIR}/Polyline/Polyline.framework", + "${BUILT_PRODUCTS_DIR}/Solar-dev/Solar.framework", + "${BUILT_PRODUCTS_DIR}/Turf/Turf.framework", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCommon/MapboxCommon.framework/MapboxCommon", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCoreMaps/MapboxCoreMaps.framework/MapboxCoreMaps", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxMobileEvents/MapboxMobileEvents.framework/MapboxMobileEvents", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxNavigationNative/MapboxNavigationNative.framework/MapboxNavigationNative", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCoreNavigation.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxDirections.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxGeocoder.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMaps.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxNavigation.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxSpeech.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Polyline.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Solar.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Turf.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCommon.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCoreMaps.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMobileEvents.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxNavigationNative.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-CarPlayExample/Pods-CarPlayExample-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 8A3F6B2F26B09D8F009EE612 /* Apply Mapbox Access Token */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)", + ); + name = "Apply Mapbox Access Token"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "$SRCROOT/Scripts/apply-mapbox-access-token.sh\n"; + }; 8AAD08F22486DA4700664446 /* Apply Mapbox Access Token */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -566,6 +744,7 @@ "${PODS_ROOT}/Target Support Files/Pods-DocsCode/Pods-DocsCode-frameworks.sh", "${BUILT_PRODUCTS_DIR}/MapboxCoreNavigation/MapboxCoreNavigation.framework", "${BUILT_PRODUCTS_DIR}/MapboxDirections/MapboxDirections.framework", + "${BUILT_PRODUCTS_DIR}/MapboxGeocoder.swift/MapboxGeocoder.framework", "${BUILT_PRODUCTS_DIR}/MapboxMaps/MapboxMaps.framework", "${BUILT_PRODUCTS_DIR}/MapboxNavigation/MapboxNavigation.framework", "${BUILT_PRODUCTS_DIR}/MapboxSpeech/MapboxSpeech.framework", @@ -581,6 +760,7 @@ outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCoreNavigation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxDirections.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxGeocoder.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMaps.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxNavigation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxSpeech.framework", @@ -600,6 +780,20 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 8A3F6B1726B09C48009EE612 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8AB316B426BC876700C3AC76 /* AppDelegate+CarPlaySearchControllerDelegate.swift in Sources */, + 8AB316AD26BB0BAF00C3AC76 /* CustomStyle.swift in Sources */, + 8A3F6B2226B09C48009EE612 /* ViewController.swift in Sources */, + 8AB316B226BC870B00C3AC76 /* AppDelegate+CarPlayManagerDelegate.swift in Sources */, + 8A3F6B1E26B09C48009EE612 /* AppDelegate.swift in Sources */, + 8AB316B626BC87D000C3AC76 /* AppDelegate+CPTemplateApplicationSceneDelegate.swift in Sources */, + 8A3F6B2026B09C48009EE612 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 961FC4FA22E0265300C72877 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -649,6 +843,22 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ + 8A3F6B2326B09C48009EE612 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8A3F6B2426B09C48009EE612 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 8A3F6B2826B09C4A009EE612 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8A3F6B2926B09C4A009EE612 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; C58FB85D1FE899B800C4B491 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -668,6 +878,51 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 8A3F6B2C26B09C4A009EE612 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5F805B58F5491201CD2D2D5F /* Pods-CarPlayExample.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_ENTITLEMENTS = "${SRCROOT}/CarPlayExample/Entitlements.plist"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = GJZR2MEM28; + INFOPLIST_FILE = CarPlayExample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.Mapbox.CarPlay-Example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = "CarPlay Provisioning Profile"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8A3F6B2D26B09C4A009EE612 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EBDF518BF4786E6F4DB25952 /* Pods-CarPlayExample.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_ENTITLEMENTS = "${SRCROOT}/CarPlayExample/Entitlements.plist"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = GJZR2MEM28; + INFOPLIST_FILE = CarPlayExample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.Mapbox.CarPlay-Example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = "CarPlay Provisioning Profile"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 961FC51222E0265400C72877 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9223D8DB7C82389A1E85E3E5 /* Pods-DocsCode.debug.xcconfig */; @@ -853,6 +1108,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 8A3F6B2E26B09C4A009EE612 /* Build configuration list for PBXNativeTarget "CarPlayExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8A3F6B2C26B09C4A009EE612 /* Debug */, + 8A3F6B2D26B09C4A009EE612 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 961FC51122E0265400C72877 /* Build configuration list for PBXNativeTarget "DocsCode" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Podfile b/Podfile index 7811a67f..64f9f3e5 100644 --- a/Podfile +++ b/Podfile @@ -3,11 +3,15 @@ install! 'cocoapods', :warn_for_unused_master_specs_repo => false platform :ios, '11.0' use_frameworks! -pod 'MapboxCoreNavigation', '~> 2.0' -pod 'MapboxNavigation', '~> 2.0' +pod 'MapboxCoreNavigation', :git => 'https://github.com/mapbox/mapbox-navigation-ios.git', :branch => 'main' +pod 'MapboxNavigation', :git => 'https://github.com/mapbox/mapbox-navigation-ios.git', :branch => 'main' +pod 'MapboxGeocoder.swift', '~> 0.14' target 'Navigation-Examples' do end target 'DocsCode' do end + +target 'CarPlayExample' do +end diff --git a/Podfile.lock b/Podfile.lock index becda88d..863921a8 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -9,6 +9,7 @@ PODS: - MapboxDirections (2.2.0): - Polyline (~> 5.0) - Turf (~> 2.0) + - MapboxGeocoder.swift (0.14.0) - MapboxMaps (10.2.0): - MapboxCommon (= 21.0.1) - MapboxCoreMaps (= 10.2.0) @@ -21,46 +22,63 @@ PODS: - MapboxMobileEvents (~> 1.0) - MapboxSpeech (~> 2.0) - Solar-dev (~> 3.0) - - MapboxNavigationNative (83.0.0): + - MapboxNavigationNative (83.0.1): - MapboxCommon (~> 21.0) - MapboxSpeech (2.0.0) - Polyline (5.0.2) - Solar-dev (3.0.1) - - Turf (2.1.0) + - Turf (2.2.0) DEPENDENCIES: - - MapboxCoreNavigation (~> 2.0) - - MapboxNavigation (~> 2.0) + - MapboxCoreNavigation (from `https://github.com/mapbox/mapbox-navigation-ios.git`, branch `main`) + - MapboxGeocoder.swift (~> 0.14) + - MapboxNavigation (from `https://github.com/mapbox/mapbox-navigation-ios.git`, branch `main`) SPEC REPOS: trunk: - MapboxCommon - MapboxCoreMaps - - MapboxCoreNavigation - MapboxDirections + - MapboxGeocoder.swift - MapboxMaps - MapboxMobileEvents - - MapboxNavigation - MapboxNavigationNative - MapboxSpeech - Polyline - Solar-dev - Turf +EXTERNAL SOURCES: + MapboxCoreNavigation: + :branch: main + :git: https://github.com/mapbox/mapbox-navigation-ios.git + MapboxNavigation: + :branch: main + :git: https://github.com/mapbox/mapbox-navigation-ios.git + +CHECKOUT OPTIONS: + MapboxCoreNavigation: + :commit: c93f8c3de58598e88a270f7bfe86b22e3a3d0f98 + :git: https://github.com/mapbox/mapbox-navigation-ios.git + MapboxNavigation: + :commit: c93f8c3de58598e88a270f7bfe86b22e3a3d0f98 + :git: https://github.com/mapbox/mapbox-navigation-ios.git + SPEC CHECKSUMS: MapboxCommon: fdfdbd4e87a225190913f2f4b4a73ec51ed77eaa MapboxCoreMaps: bdd0115bfd8bf6df402468710cb23c926938b03d MapboxCoreNavigation: 12d31f4dd5bd08120097ec52c540a032fd58f4de MapboxDirections: aaa6f1ae1612692ef3b4211d20d22404ad8ef607 + MapboxGeocoder.swift: a288e92ec13fbd65bbdfd7c54c61b0e80d19e734 MapboxMaps: 1ed477cbe573a9e740801dfc9c636ef33bbc3f54 MapboxMobileEvents: 14d7ac3ee95b4142c4fec2205dfd48ff453e8871 MapboxNavigation: 77fce7e09f1323a1f10c5ee5d9c238aaee2d7d85 - MapboxNavigationNative: a82594cb6fa26c129e2f1a08d1466c3f98cdc999 + MapboxNavigationNative: 46ae8348a7aea3e1b9697743e9ba505eabd523ac MapboxSpeech: e4ed02984444b6373374c72c369edaf045cc490c Polyline: fce41d72e1146c41c6d081f7656827226f643dff Solar-dev: 4612dc9878b9fed2667d23b327f1d4e54e16e8d0 - Turf: d1220730a00b50a9fb5862c1fb6a5dd698c9e367 + Turf: 1a6bc2d0142f84610445159565c3c93d62b83897 -PODFILE CHECKSUM: ade747cd70b06f12dd4076e55177eb029b4813b0 +PODFILE CHECKSUM: 11a4d6e5f78652e7052548bf1e61f7f5e2b7f9e8 COCOAPODS: 1.11.2