diff --git a/Bench/AppDelegate.swift b/Bench/AppDelegate.swift index 7a5edc83357..98e68885bcc 100644 --- a/Bench/AppDelegate.swift +++ b/Bench/AppDelegate.swift @@ -2,12 +2,9 @@ import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - window = UIWindow(frame: UIScreen.main.bounds) if isRunningTests() { diff --git a/Bench/BenchViewController.swift b/Bench/BenchViewController.swift index b30281cb308..1edbd328645 100644 --- a/Bench/BenchViewController.swift +++ b/Bench/BenchViewController.swift @@ -26,12 +26,10 @@ struct Item { } class BenchViewController: UITableViewController { - var dataSource = [Section]() let cellIdentifier = "cellId" override func viewDidLoad() { - super.viewDidLoad() tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier) @@ -66,7 +64,6 @@ class BenchViewController: UITableViewController { } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) let item = dataSource[indexPath.section].items[indexPath.row] @@ -76,7 +73,6 @@ class BenchViewController: UITableViewController { } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - tableView.deselectRow(at: indexPath, animated: true) let item = dataSource[indexPath.section].items[indexPath.row] @@ -90,7 +86,6 @@ class BenchViewController: UITableViewController { } extension BenchViewController: NavigationViewControllerDelegate { - func navigationViewControllerDidDismiss(_ navigationViewController: NavigationViewController, byCanceling canceled: Bool) { navigationController?.popViewController(animated: true) } diff --git a/Bench/ControlRouteViewController.swift b/Bench/ControlRouteViewController.swift index 7c5183e5121..07e5d1ee5c2 100644 --- a/Bench/ControlRouteViewController.swift +++ b/Bench/ControlRouteViewController.swift @@ -1,9 +1,7 @@ import UIKit import MapboxNavigation - class ControlRouteViewController: NavigationViewController { - override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) diff --git a/BenchTests/BenchTests.swift b/BenchTests/BenchTests.swift index f7145a056bd..38e0251e48c 100644 --- a/BenchTests/BenchTests.swift +++ b/BenchTests/BenchTests.swift @@ -5,9 +5,7 @@ import MapboxDirections @testable import MapboxNavigation @testable import Bench - class BenchTests: XCTestCase, CLLocationManagerDelegate { - let token = "deadbeef" override func setUp() { @@ -16,7 +14,6 @@ class BenchTests: XCTestCase, CLLocationManagerDelegate { } func testControlRoute1() { - let route = Fixture.route(from: "PipeFittersUnion-FourSeasonsBoston") let trace = Fixture.locations(from: "PipeFittersUnion-FourSeasonsBoston.trace") @@ -33,7 +30,6 @@ class BenchTests: XCTestCase, CLLocationManagerDelegate { } func testControlRoute2() { - let route = Fixture.route(from: "DCA-Arboretum") let trace = Fixture.locations(from: "DCA-Arboretum.trace") @@ -50,7 +46,6 @@ class BenchTests: XCTestCase, CLLocationManagerDelegate { } func navigationViewController(route: Route, locationManager: ReplayLocationManager) -> NavigationViewController { - let speechAPI = SpeechAPISpy(accessToken: token) let directions = DirectionsSpy(accessToken: token) let service = MapboxNavigationService(route: route, @@ -66,5 +61,3 @@ class BenchTests: XCTestCase, CLLocationManagerDelegate { return NavigationViewController(for: route, options: options) } } - - diff --git a/BenchUITests/BenchUITests.swift b/BenchUITests/BenchUITests.swift index cb36eaa0efb..b62ed91de82 100644 --- a/BenchUITests/BenchUITests.swift +++ b/BenchUITests/BenchUITests.swift @@ -1,13 +1,11 @@ import XCTest class BenchUITests: XCTestCase { - override func setUp() { continueAfterFailure = false XCUIApplication().launch() } func testExample() { - } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 90cf5b38b80..9517fb26df8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,22 @@ ## master +### Packaging +* This library can no longer be used in applications written in pure Objective-C. If you need to use this library’s public API from Objective-C code, you will need to implement a wrapper in Swift that bridges the subset of the API you need from Swift to Objective-C. ([#2230](https://github.com/mapbox/mapbox-navigation-ios/pull/2230)) + +### User interface +* `UserCourseView` is now a type alias of the `UIView` class and the `CourseUpdatable` protocol rather than a protocol in its own right. ([#2230](https://github.com/mapbox/mapbox-navigation-ios/pull/2230)) * Fixed an issue where user notifications displayed right turn arrows for left turn maneuvers. ([#2270](https://github.com/mapbox/mapbox-navigation-ios/pull/2270)) +* Renamed `NavigationMapView.showRoutes(_:legIndex:)` to `NavigationMapView.show(_:legIndex:)`. ([#2230](https://github.com/mapbox/mapbox-navigation-ios/pull/2230)) +* Renamed `NavigationMapView.showWaypoints(_:legIndex:)` to `NavigationMapView.showWaypoints(on:legIndex:)`. ([#2230](https://github.com/mapbox/mapbox-navigation-ios/pull/2230)) +* Renamed `MapboxVoiceController.play(_:)` to `MapboxVoiceController.play(instruction:data:)`. ([#2230](https://github.com/mapbox/mapbox-navigation-ios/pull/2230)) + +### Error handling +* The `MapboxVoiceController` and `RouteVoiceController` now emit `SpeechError`s instead of an `NSError` object. ([#2230](https://github.com/mapbox/mapbox-navigation-ios/pull/2230)) +* Added the `VoiceControllerDelegate.voiceController(_:didFallBackTo:becauseOf:)` method for detecting when the voice controller falls back to `AVSpeechSynthesizer`. ([#2230](https://github.com/mapbox/mapbox-navigation-ios/pull/2230)) + +### Other changes +* Since pure Swift protocols cannot have optional methods, various delegate protocols now provide default no-op implementations for all their methods and conform to the `UnimplementedLogging` protocol, which can inform you at runtime when a delegate method is called but has not been implemented. Messages are sent through Apple Unified Logging and can be disabled globally through [Unifed Logging](https://developer.apple.com/documentation/os/logging#2878594), or by overriding the delegate function with a no-op implementation. ([#2230](https://github.com/mapbox/mapbox-navigation-ios/pull/2230)) ## v0.38.0 @@ -146,7 +161,6 @@ * You can now customize the control layer of the map template comprising of the navigation bar's leading and trailing buttons and the map buttons. ([#1962](https://github.com/mapbox/mapbox-navigation-ios/pull/1962)) * Added new map buttons in the `CarPlayManager` and the `CarPlayMapViewController`. You can now access map buttons that perform built-in actions on the map by accessing read-only properties such as: `CarPlayManager.exitButton`, `CarPlayManager.muteButton`, `CarPlayManager.showFeedbackButton`, `CarPlayManager.overviewButton`, `CarPlayMapViewController.recenterButton`, `CarPlayMapViewController.zoomInButton`, `CarPlayMapViewController.zoomOutButton`, `CarPlayMapViewController.panningInterfaceDisplayButton(for:)`, `CarPlayMapViewController.panningInterfaceDismissalButton(for:)`. ([#1962](https://github.com/mapbox/mapbox-navigation-ios/pull/1962)) - ### Other changes * Replaced `NavigationViewController(for:styles:navigationService:viewController:)` with `NavigationViewController(for:options:)`, which accepts a `NavigationOptions` object (not to be confused with `NavigationRouteOptions`). `NavigationOptions` contains various options for customizing the user experience of a turn-by-turn navigation session, including replacing the bottom banner with a custom view controller. ([#1951](https://github.com/mapbox/mapbox-navigation-ios/pull/1951)) @@ -630,7 +644,6 @@ * Exposes `RouteVoiceController.speak(_:)` which would allow custom subclass of MapboxVoiceController to override this method and pass a modified SpokenInstruction to our superclass implementation. - ## v0.13.1 (February 7, 2018) ### Core Navigation diff --git a/Cartfile.resolved b/Cartfile.resolved index ccb1afd4c18..fee82c1017c 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,9 +1,9 @@ -binary "https://www.mapbox.com/ios-sdk/Mapbox-iOS-SDK.json" "5.2.0" +binary "https://www.mapbox.com/ios-sdk/Mapbox-iOS-SDK.json" "5.5.0" binary "https://www.mapbox.com/ios-sdk/MapboxNavigationNative.json" "6.2.1" github "AndriiDoroshko/SnappyShrimp" "1.6.4" github "CedarBDD/Cedar" "v1.0" -github "Quick/Nimble" "v8.0.2" -github "Quick/Quick" "v2.1.0" +github "Quick/Nimble" "v8.0.4" +github "Quick/Quick" "v2.2.0" github "ceeK/Solar" "2.1.0" github "mapbox/MapboxDirections.swift" "v0.30.0" github "mapbox/MapboxGeocoder.swift" "v0.10.2" diff --git a/Example/AppDelegate+CarPlay.swift b/Example/AppDelegate+CarPlay.swift index 439a0e8c089..bbc5c8c3cb1 100644 --- a/Example/AppDelegate+CarPlay.swift +++ b/Example/AppDelegate+CarPlay.swift @@ -19,7 +19,6 @@ let CarPlayWaypointKey: String = "MBCarPlayWaypoint" */ @available(iOS 12.0, *) extension AppDelegate: CPApplicationDelegate { - // MARK: CPApplicationDelegate func application(_ application: UIApplication, didConnectCarInterfaceController interfaceController: CPInterfaceController, to window: CPWindow) { @@ -46,7 +45,6 @@ extension AppDelegate: CPApplicationDelegate { @available(iOS 12.0, *) extension AppDelegate: CarPlayManagerDelegate { func carPlayManager(_ carPlayManager: CarPlayManager, navigationServiceAlong route: Route, desiredSimulationMode: SimulationMode) -> NavigationService { - if let nvc = self.window?.rootViewController?.presentedViewController as? NavigationViewController, let service = nvc.navigationService { //Do not set simulation mode if we already have an active navigation session. return service @@ -54,7 +52,6 @@ extension AppDelegate: CarPlayManagerDelegate { return MapboxNavigationService(route: route, simulating: desiredSimulationMode) } - // MARK: CarPlayManagerDelegate func carPlayManager(_ carPlayManager: CarPlayManager, didBeginNavigationWith service: NavigationService) { currentAppRootViewController?.beginNavigationWithCarplay(navigationService: service) @@ -78,7 +75,6 @@ extension AppDelegate: CarPlayManagerDelegate { } func carPlayManager(_ carPlayManager: CarPlayManager, leadingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, in template: CPTemplate, for activity: CarPlayActivity) -> [CPBarButton]? { - guard let interfaceController = self.carPlayManager.interfaceController else { return nil } @@ -107,7 +103,6 @@ extension AppDelegate: CarPlayManagerDelegate { } func carPlayManager(_ carPlayManager: CarPlayManager, trailingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, in template: CPTemplate, for activity: CarPlayActivity) -> [CPBarButton]? { - switch activity { case .previewing: let disableSimulateText = "Disable Simulation" @@ -133,11 +128,10 @@ extension AppDelegate: CarPlayManagerDelegate { } func carPlayManager(_ carPlayManager: CarPlayManager, mapButtonsCompatibleWith traitCollection: UITraitCollection, in template: CPTemplate, for activity: CarPlayActivity) -> [CPMapButton]? { - switch activity { case .browsing: guard let mapViewController = carPlayManager.carPlayMapViewController, - let mapTemplate = template as? CPMapTemplate else { + let mapTemplate = template as? CPMapTemplate else { return nil } var mapButtons = [mapViewController.recenterButton, @@ -175,7 +169,6 @@ extension AppDelegate: CarPlaySearchControllerDelegate { @available(iOS 12.0, *) extension AppDelegate: CPListTemplateDelegate { - func listTemplate(_ listTemplate: CPListTemplate, didSelect item: CPListItem, completionHandler: @escaping () -> Void) { // Selected a favorite if let userInfo = item.userInfo as? [String: Any], diff --git a/Example/AppDelegate.swift b/Example/AppDelegate.swift index c2895ca98c6..1f817842098 100644 --- a/Example/AppDelegate.swift +++ b/Example/AppDelegate.swift @@ -1,10 +1,8 @@ import UIKit import MapboxNavigation - @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - weak var currentAppRootViewController: ViewController? var window: UIWindow? @@ -15,7 +13,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { lazy var carPlaySearchController: CarPlaySearchController = CarPlaySearchController() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - if isRunningTests() { window!.rootViewController = UIViewController() } diff --git a/Example/CustomStyles.swift b/Example/CustomStyles.swift index fc44343966c..c501b52f005 100644 --- a/Example/CustomStyles.swift +++ b/Example/CustomStyles.swift @@ -1,12 +1,12 @@ import Foundation import MapboxNavigation +// MARK: CustomDayStyle + /** To find more pieces of the UI to customize, checkout DayStyle.swift. */ -// MARK: CustomDayStyle class CustomDayStyle: DayStyle { - required init() { super.init() mapStyleURL = URL(string: "mapbox://styles/mapbox/satellite-streets-v9")! @@ -22,7 +22,6 @@ class CustomDayStyle: DayStyle { // MARK: CustomNightStyle class CustomNightStyle: NightStyle { - required init() { super.init() mapStyleURL = URL(string: "mapbox://styles/mapbox/satellite-streets-v9")! diff --git a/Example/CustomViewController.swift b/Example/CustomViewController.swift index 3d9cdec4e7a..fab50669761 100644 --- a/Example/CustomViewController.swift +++ b/Example/CustomViewController.swift @@ -8,7 +8,6 @@ import MapboxDirections import Turf class CustomViewController: UIViewController, MGLMapViewDelegate { - var destination: MGLPointAnnotation! let directions = Directions.shared var navigationService: NavigationService! @@ -81,7 +80,7 @@ class CustomViewController: UIViewController, MGLMapViewDelegate { } func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) { - self.mapView.showRoutes([navigationService.route]) + self.mapView.show([navigationService.route]) } // Notifications sent on all location updates @@ -115,7 +114,7 @@ class CustomViewController: UIViewController, MGLMapViewDelegate { // Fired when the user is no longer on the route. // Update the route on the map. @objc func rerouted(_ notification: NSNotification) { - self.mapView.showRoutes([navigationService.route]) + self.mapView.show([navigationService.route]) } @IBAction func cancelButtonPressed(_ sender: Any) { @@ -161,9 +160,9 @@ class CustomViewController: UIViewController, MGLMapViewDelegate { let route = navigationService.route // find the leg that contains the step, legIndex, and stepIndex - guard let leg = route.legs.first(where: { $0.steps.contains(step) }), - let legIndex = route.legs.firstIndex(of: leg), - let stepIndex = leg.steps.firstIndex(of: step) else { + guard let leg = route.legs.first(where: { $0.steps.contains(step) }), + let legIndex = route.legs.firstIndex(of: leg), + let stepIndex = leg.steps.firstIndex(of: step) else { return } @@ -207,7 +206,7 @@ class CustomViewController: UIViewController, MGLMapViewDelegate { guard let view = previewInstructionsView else { return } view.removeFromSuperview() - // reclaim the delegate, from the preview banner + // reclaim the delegate, from the preview banner instructionsBannerView.delegate = self // nil out both the view and index diff --git a/Example/FavoritesList.swift b/Example/FavoritesList.swift index 1ff20e6ce0a..3e811319f2e 100644 --- a/Example/FavoritesList.swift +++ b/Example/FavoritesList.swift @@ -4,7 +4,6 @@ import CarPlay import CoreLocation public enum FavoritesList { - enum POI: RawRepresentable { typealias RawValue = String case mapboxSF, timesSquare diff --git a/Example/MBViewController.h b/Example/MBViewController.h deleted file mode 100644 index 0517998c1fb..00000000000 --- a/Example/MBViewController.h +++ /dev/null @@ -1,9 +0,0 @@ -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface MBViewController : UIViewController - -@end - -NS_ASSUME_NONNULL_END diff --git a/Example/MBViewController.m b/Example/MBViewController.m deleted file mode 100644 index 4764bb79254..00000000000 --- a/Example/MBViewController.m +++ /dev/null @@ -1,127 +0,0 @@ -#import "MBViewController.h" - -@import AVFoundation; -@import MapboxCoreNavigation; -@import MapboxDirections; -@import MapboxNavigation; -@import Mapbox; - -@interface MBViewController () -@property (nonatomic, weak) IBOutlet MGLMapView *mapView; -@property (weak, nonatomic) IBOutlet UIButton *toggleNavigationButton; -@property (weak, nonatomic) IBOutlet UILabel *howToBeginLabel; -@property (nonatomic, assign) CLLocationCoordinate2D destination; -@property (nonatomic) MBDirections *directions; -@property (nonatomic) MBRoute *route; -@property (nonatomic) MBNavigationService *navigation; -@property (nonatomic) NSLengthFormatter *lengthFormatter; -@end - -@implementation MBViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view, typically from a nib. - self.mapView.userTrackingMode = MGLUserTrackingModeFollow; - - self.lengthFormatter = [[NSLengthFormatter alloc] init]; - self.lengthFormatter.unitStyle = NSFormattingUnitStyleShort; - self.directions = [MBDirections sharedDirections]; - - [self resumeNotifications]; -} -- (void)viewDidAppear:(BOOL)animated { - [self.navigation start]; - [super viewDidAppear:animated]; -} - -- (void)viewDidDisappear:(BOOL)animated { - [super viewDidDisappear:animated]; - - [self suspendNotifications]; - [self.navigation stop]; -} - -- (IBAction)didLongPress:(UILongPressGestureRecognizer *)sender { - if (sender.state != UIGestureRecognizerStateBegan) { - return; - } - - CGPoint point = [sender locationInView:self.mapView]; - self.destination = [self.mapView convertPoint:point toCoordinateFromView:self.mapView]; - [self getRoute]; -} - -- (void)resumeNotifications { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(progressDidChange:) name:MBRouteControllerProgressDidChangeNotification object:_navigation]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willReroute:) name:MBRouteControllerWillRerouteNotification object:_navigation]; -} - -- (void)suspendNotifications { - [[NSNotificationCenter defaultCenter] removeObserver:self name:MBRouteControllerProgressDidChangeNotification object:_navigation]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:MBRouteControllerWillRerouteNotification object:_navigation]; -} - -- (void)progressDidChange:(NSNotification *)notification { - // If you are using MapboxCoreNavigation, - // this would be a good time to update UI elements. - // You can grab the current routeProgress like: - // MBRouteProgress *progress = notification.userInfo[MBRouteControllerRouteProgressKey]; -} - -- (void)willReroute:(NSNotification *)notification { - [self getRoute]; -} - -- (void)getRoute { - NSArray *waypoints = @[[[MBWaypoint alloc] initWithCoordinate:self.mapView.userLocation.coordinate coordinateAccuracy:-1 name:nil], - [[MBWaypoint alloc] initWithCoordinate:self.destination coordinateAccuracy:-1 name:nil]]; - - MBNavigationRouteOptions *options = [[MBNavigationRouteOptions alloc] initWithWaypoints:waypoints profileIdentifier:MBDirectionsProfileIdentifierAutomobileAvoidingTraffic]; - options.includesSteps = YES; - options.routeShapeResolution = MBRouteShapeResolutionFull; - - NSURLSessionDataTask *task = [[MBDirections sharedDirections] calculateDirectionsWithOptions:options completionHandler:^(NSArray * _Nullable waypoints, NSArray * _Nullable routes, NSError * _Nullable error) { - - if (!routes.firstObject) { - return; - } - - if (self.mapView.annotations) { - [self.mapView removeAnnotations:self.mapView.annotations]; - } - - MBRoute *route = routes.firstObject; - CLLocationCoordinate2D *routeCoordinates = malloc(route.coordinateCount * sizeof(CLLocationCoordinate2D)); - [route getCoordinates:routeCoordinates]; - - MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:routeCoordinates count:route.coordinateCount]; - - [self.mapView addAnnotation:polyline]; - [self.mapView setVisibleCoordinates:routeCoordinates count:route.coordinateCount edgePadding:UIEdgeInsetsZero animated:YES]; - - free(routeCoordinates); - - self.route = route; - - [self startNavigation:route]; - }]; - - [task resume]; -} - -- (void)startNavigation:(MBRoute *)route { - MBNavigationService *service = [[MBNavigationService alloc] initWithRoute:route directions:self.directions locationSource:nil eventsManagerType:nil simulating:MBNavigationSimulationOptionsAlways routerType:nil]; - - self.navigation = service; - MBNavigationOptions *options = [[MBNavigationOptions alloc] init]; - options.navigationService = service; - MBNavigationViewController *controller = [[MBNavigationViewController alloc] initWithRoute:route options:options]; - - [self presentViewController:controller animated:YES completion:nil]; - - // Suspend notifications and let `MBNavigationViewController` handle all progress and voice updates. - [self suspendNotifications]; -} - -@end diff --git a/Example/OfflineViewController.swift b/Example/OfflineViewController.swift index 2e4e3b8b93c..227d61202a6 100644 --- a/Example/OfflineViewController.swift +++ b/Example/OfflineViewController.swift @@ -4,7 +4,6 @@ import MapboxDirections import MapboxCoreNavigation class OfflineViewController: UIViewController, MGLMapViewDelegate { - var mapView: MGLMapView! var resizableView: ResizableView! var backgroundLayer = CAShapeLayer() @@ -47,14 +46,12 @@ class OfflineViewController: UIViewController, MGLMapViewDelegate { } @objc func downloadRegion() { - let mapCoordinateBounds = mapView.convert(resizableView.frame, toCoordinateBoundsFrom: nil) let coordinateBounds = CoordinateBounds(coordinates: [mapCoordinateBounds.sw, mapCoordinateBounds.ne]) disableUserInterface() _ = Directions.shared.fetchAvailableOfflineVersions { [weak self] (versions, error) in - guard let version = versions?.first(where: { !$0.isEmpty } ) else { let title = NSLocalizedString("OFFLINE_TITLE_VERSION_FETCHING_FAILED", value:"Unable to fetch available routing tile versions", comment: "Error title to display when no routing tile versions are available") let message = NSLocalizedString("OFFLINE_MESSAGE_VERSION_FETCHING_FAILED", value:"No routing tile versions are available for download. Please try again later.", comment: "Error message to display when no routing tile versions are available") @@ -95,14 +92,11 @@ class OfflineViewController: UIViewController, MGLMapViewDelegate { outputDirectoryURL?.ensureDirectoryExists() NavigationDirections.unpackTilePack(at: url, outputDirectoryURL: outputDirectoryURL!, progressHandler: { (totalBytes, bytesRemaining) in - let progress = Float(bytesRemaining) / Float(totalBytes) let formattedProgress = NumberFormatter.localizedString(from: progress as NSNumber, number: .percent) let title = String.localizedStringWithFormat(NSLocalizedString("OFFLINE_TITLE_UNPACKING_FMT", value: "Unpacking… (%@)", comment: "Status item while downloading an offline region; 1 = percentage complete"), formattedProgress) self?.updateTitle(title) - }, completionHandler: { (result, error) in - self?.navigationController?.popViewController(animated: true) }) } @@ -117,7 +111,6 @@ class OfflineViewController: UIViewController, MGLMapViewDelegate { } extension CGRect { - var minXY: CGPoint { return CGPoint(x: minX, y: minY) } @@ -128,7 +121,6 @@ extension CGRect { } extension URL { - func ensureDirectoryExists() { try? FileManager.default.createDirectory(at: self, withIntermediateDirectories: true, attributes: nil) } diff --git a/Example/ResizableView.swift b/Example/ResizableView.swift index 5162fd30ee6..a68e33e94f4 100644 --- a/Example/ResizableView.swift +++ b/Example/ResizableView.swift @@ -1,8 +1,6 @@ import UIKit - class ResizableView: UIControl { - let lineLayer = CAShapeLayer() // Associated background layer will be masked by the frame of the resizable view weak var backgroundLayer: CAShapeLayer? @@ -17,7 +15,6 @@ class ResizableView: UIControl { } override init(frame: CGRect) { - super.init(frame: frame) clipsToBounds = false @@ -61,11 +58,9 @@ class ResizableView: UIControl { } @objc func resizePan(_ sender: UIPanGestureRecognizer) { - let location = sender.location(in: superview) if sender.state == .began || sender.state == .changed { - let origin = CGPoint(x: frame.minX, y: frame.minY) frame = CGRect(origin: origin, size: CGSize(width: location.x - origin.x, @@ -108,18 +103,15 @@ class ResizableView: UIControl { bringSubviewToFront(imageView) } - } fileprivate extension CGFloat { - var mid: CGFloat { return self / 2 } } extension UIImage { - func withPadding(x: CGFloat, y: CGFloat) -> UIImage? { let width: CGFloat = size.width + x let height: CGFloat = size.height + y diff --git a/Example/Settings.swift b/Example/Settings.swift index bc04f413466..c03f4d2c680 100644 --- a/Example/Settings.swift +++ b/Example/Settings.swift @@ -2,11 +2,8 @@ import Foundation import MapboxCoreNavigation import MapboxDirections - struct Settings { - static var directions: NavigationDirections = NavigationDirections() static var selectedOfflineVersion: String? = nil - } diff --git a/Example/SettingsItems.swift b/Example/SettingsItems.swift index 779070f4a89..da18af78afc 100644 --- a/Example/SettingsItems.swift +++ b/Example/SettingsItems.swift @@ -68,9 +68,7 @@ struct Section { } extension SettingsViewController { - func sections() -> [Section] { - let offlineItem = Item(title: NSLocalizedString("SETTINGS_ITEM_DOWNLOAD_REGION_TITLE", value: "Download Region", comment: "Title of table view item that downloads a new offline region"), viewControllerType: OfflineViewController.self, payload: nil) let offlineSection = Section(title: NSLocalizedString("SETTINGS_SECTION_OFFLINE_EXAMPLES", value: "Offline Examples", comment: "Section of offline settings table view"), items: [offlineItem]) let versionSection = Section(title: NSLocalizedString("SETTINGS_SECTION_DOWNLOADED_VERSIONS", value: "Downloaded Versions", comment: "Section of offline settings table view"), items: versionDirectories()) @@ -79,7 +77,6 @@ extension SettingsViewController { } func versionDirectories() -> [ItemProtocol] { - var versions = [OfflineVersionItem]() let directories = try? FileManager.default.contentsOfDirectory(atPath: Bundle.mapboxCoreNavigation.suggestedTileURL!.path) diff --git a/Example/SettingsViewController.swift b/Example/SettingsViewController.swift index 5fc190a06bd..78e91918ae5 100644 --- a/Example/SettingsViewController.swift +++ b/Example/SettingsViewController.swift @@ -2,9 +2,7 @@ import UIKit import MapboxDirections import MapboxCoreNavigation - class SettingsViewController: UITableViewController { - let cellIdentifier = "cellId" var dataSource: [Section]! @@ -26,7 +24,6 @@ class SettingsViewController: UITableViewController { } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - var cell: UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) if cell == nil { cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier) @@ -79,7 +76,6 @@ class SettingsViewController: UITableViewController { } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - tableView.deselectRow(at: indexPath, animated: true) let item = dataSource[indexPath.section].items[indexPath.row] diff --git a/Example/UIViewController.swift b/Example/UIViewController.swift index 5b48ce47913..f5587e3ea8a 100644 --- a/Example/UIViewController.swift +++ b/Example/UIViewController.swift @@ -1,7 +1,6 @@ import UIKit extension UIViewController { - func presentAlert(_ title: String? = nil, message: String? = nil) { let controller = UIAlertController(title: title, message: message, preferredStyle: .alert) controller.addAction(UIAlertAction(title: NSLocalizedString("ALERT_OK", value: "OK", comment: "Alert action"), style: .default, handler: { (action) in diff --git a/Example/ViewController+GuidanceCards.swift b/Example/ViewController+GuidanceCards.swift index 6a9b57e32c6..baaff866d36 100644 --- a/Example/ViewController+GuidanceCards.swift +++ b/Example/ViewController+GuidanceCards.swift @@ -4,9 +4,7 @@ import MapboxDirections /// :nodoc: extension ViewController: InstructionsCardCollectionDelegate { - public func instructionsCardCollection(_ instructionsCardCollection: InstructionsCardViewController, didPreview step: RouteStep) { - guard let route = routes?.first else { return } // find the leg that contains the step, legIndex, and stepIndex diff --git a/Example/ViewController.swift b/Example/ViewController.swift index 5d9952ebb21..10a2f545522 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -3,13 +3,12 @@ import MapboxCoreNavigation import MapboxNavigation import MapboxDirections import UserNotifications - +import AVKit private typealias RouteRequestSuccess = (([Route]) -> Void) private typealias RouteRequestFailure = ((NSError) -> Void) class ViewController: UIViewController { - // MARK: - IBOutlets @IBOutlet weak var longPressHintView: UIView! @IBOutlet weak var simulationButton: UIButton! @@ -39,11 +38,13 @@ class ViewController: UIViewController { var routes: [Route]? { didSet { startButton.isEnabled = (routes?.count ?? 0 > 0) - guard let routes = routes, - let current = routes.first else { mapView?.removeRoutes(); return } + guard let routes = routes, let current = routes.first else { + mapView?.removeRoutes() + return + } - mapView?.showRoutes(routes) - mapView?.showWaypoints(current) + mapView?.show(routes) + mapView?.showWaypoints(on: current) } } @@ -85,7 +86,6 @@ class ViewController: UIViewController { } } - // MARK: - Lifecycle Methods override func viewDidLoad() { @@ -172,7 +172,6 @@ class ViewController: UIViewController { requestRoute() } - // MARK: - IBActions @IBAction func simulateButtonPressed(_ sender: Any) { @@ -206,7 +205,6 @@ class ViewController: UIViewController { } fileprivate func requestRoute(with options: RouteOptions, success: @escaping RouteRequestSuccess, failure: RouteRequestFailure?) { - let handler: Directions.RouteCompletionHandler = { (waypoints, routes, error) in if let error = error { failure?(error) } guard let routes = routes else { return } @@ -303,7 +301,6 @@ class ViewController: UIViewController { presentAndRemoveMapview(navigationViewController, completion: beginCarPlayNavigation) } - func navigationService(route: Route) -> NavigationService { let simulate = simulationButton.isSelected let mode: SimulationMode = simulate ? .always : .onPoorGPS @@ -368,8 +365,8 @@ extension ViewController: MGLMapViewDelegate { if let routes = routes, let currentRoute = routes.first, let coords = currentRoute.coordinates { mapView.setVisibleCoordinateBounds(MGLPolygon(coordinates: coords, count: currentRoute.coordinateCount).overlayBounds, animated: false) - self.mapView?.showRoutes(routes) - self.mapView?.showWaypoints(currentRoute) + self.mapView?.show(routes) + self.mapView?.showWaypoints(on: currentRoute) } } } @@ -410,10 +407,16 @@ extension ViewController: NavigationMapViewDelegate { // MARK: VoiceControllerDelegate methods // To use these delegate methods, set the `VoiceControllerDelegate` on your `VoiceController`. extension ViewController: VoiceControllerDelegate { + // called when there is an error that requires the speech controller to fall back to a native engine. + func voiceController(_ voiceController: RouteVoiceController, didFallBackTo synthesizer: AVSpeechSynthesizer, error: SpeechError) { + print(error) + } + // Called when there is an error with speaking a voice instruction. - func voiceController(_ voiceController: RouteVoiceController, spokenInstructionsDidFailWith error: Error) { - print(error.localizedDescription) + func voiceController(_ voiceController: RouteVoiceController, spokenInstructionsDidFailWith error: SpeechError) { + print(error) } + // Called when an instruction is interrupted by a new voice instruction. func voiceController(_ voiceController: RouteVoiceController, didInterrupt interruptedInstruction: SpokenInstruction, with interruptingInstruction: SpokenInstruction) { print(interruptedInstruction.text, interruptingInstruction.text) @@ -432,7 +435,6 @@ extension ViewController: VoiceControllerDelegate { } func navigationViewController(_ navigationViewController: NavigationViewController, shouldRerouteFrom location: CLLocation) -> Bool { - let shouldUseOfflineRouting = Settings.selectedOfflineVersion != nil guard shouldUseOfflineRouting == true else { @@ -463,7 +465,7 @@ extension ViewController: WaypointConfirmationViewControllerDelegate { func confirmationControllerDidConfirm(_ confirmationController: WaypointConfirmationViewController) { confirmationController.dismiss(animated: true, completion: { guard let navigationViewController = self.presentedViewController as? NavigationViewController, - let navService = navigationViewController.navigationService else { return } + let navService = navigationViewController.navigationService else { return } let router = navService.router! guard router.route.legs.count > router.routeProgress.legIndex + 1 else { return } @@ -511,11 +513,9 @@ extension ViewController: NavigationViewControllerDelegate { } } - // Mark: VisualInstructionDelegate extension ViewController: VisualInstructionDelegate { func label(_ label: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { - // Uncomment to mutate the instruction shown in the top instruction banner // let range = NSRange(location: 0, length: presented.length) // let mutable = NSMutableAttributedString(attributedString: presented) diff --git a/Example/WaypointConfirmationViewController.swift b/Example/WaypointConfirmationViewController.swift index e1098f166b4..fd8fa25eafe 100644 --- a/Example/WaypointConfirmationViewController.swift +++ b/Example/WaypointConfirmationViewController.swift @@ -5,7 +5,6 @@ protocol WaypointConfirmationViewControllerDelegate: NSObjectProtocol { } class WaypointConfirmationViewController: UIViewController { - weak var delegate: WaypointConfirmationViewControllerDelegate? @IBAction func continueButtonPressed(_ sender: Any) { diff --git a/MapboxCoreNavigation/BundleAdditions.swift b/MapboxCoreNavigation/BundleAdditions.swift index 81485ae2daf..9f4972bb825 100644 --- a/MapboxCoreNavigation/BundleAdditions.swift +++ b/MapboxCoreNavigation/BundleAdditions.swift @@ -32,7 +32,7 @@ extension Bundle { /** The Mapbox Core Navigation framework bundle. */ - @objc public class var mapboxCoreNavigation: Bundle { + public class var mapboxCoreNavigation: Bundle { return Bundle(for: RouteController.self) } @@ -45,8 +45,7 @@ extension Bundle { /** A file URL representing a directory in which the application can place downloaded tile files. */ - @objc public var suggestedTileURL: URL? { - + public var suggestedTileURL: URL? { guard let cachesDirectory = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first else { return nil } @@ -60,7 +59,6 @@ extension Bundle { /** A file URL at which the application can place a downloaded tile file with the given version identifier. */ - @objc(suggestedTileURLWithVersion:) public func suggestedTileURL(version: String) -> URL? { return suggestedTileURL?.appendingPathComponent(version) } diff --git a/MapboxCoreNavigation/CLLocation.swift b/MapboxCoreNavigation/CLLocation.swift index af52111f4fd..5d6a3f6e8f3 100644 --- a/MapboxCoreNavigation/CLLocation.swift +++ b/MapboxCoreNavigation/CLLocation.swift @@ -4,7 +4,6 @@ import MapboxNavigationNative import Turf extension CLLocation { - var isQualified: Bool { return 0...100 ~= horizontalAccuracy } @@ -78,13 +77,11 @@ extension CLLocation { if let upcomingStep = legProgress.upcomingStep, let initialHeading = upcomingStep.initialHeading, let finalHeading = upcomingStep.finalHeading { - // The max here is 180. The closer it is to 180, the sharper the turn. if initialHeading.clockwiseDifference(from: finalHeading) > 180 - RouteSnappingMaxManipulatedCourseAngle { return stepCoordinates } - if finalHeading.difference(from: course) > RouteControllerMaximumAllowedDegreeOffsetForTurnCompletion { return stepCoordinates } @@ -97,7 +94,6 @@ extension CLLocation { return nearbyCoordinates } - /** Given a location and a series of coordinates, compute what the course should be for a the location. */ @@ -142,7 +138,6 @@ extension CLLocation { Determines if the a location is qualified enough to allow the user puck to become unsnapped. */ func shouldSnap(toRouteWith course: CLLocationDirection, distanceToFirstCoordinateOnLeg: CLLocationDistance = CLLocationDistanceMax) -> Bool { - // If the user is near the beginning of leg, allow for unsnapped more often. let isWithinDepatureStep = distanceToFirstCoordinateOnLeg < RouteControllerManeuverZoneRadius diff --git a/MapboxCoreNavigation/CoreConstants.swift b/MapboxCoreNavigation/CoreConstants.swift index 4c94d802254..1819c1e3488 100644 --- a/MapboxCoreNavigation/CoreConstants.swift +++ b/MapboxCoreNavigation/CoreConstants.swift @@ -2,7 +2,6 @@ import Foundation import CoreLocation import MapboxDirections - // MARK: - RouteController /** Maximum number of meters the user can travel away from step before `RouteControllerShouldReroute` is emitted. @@ -73,7 +72,6 @@ public var RouteControllerMinimumNumberLocationUpdatesBackwards = 3 */ public var RouteControllerNumberOfSecondsForRerouteFeedback: TimeInterval = 10 - /** Minimum duration remaining in seconds for proactive rerouting to be active. */ @@ -112,59 +110,109 @@ public var RouteControllerMinNumberOfInCorrectCourses: Int = 4 */ public var RouteControllerIncorrectCourseMultiplier: Int = 4 - - /** When calculating the user's snapped location, this constant will be used for deciding upon which step coordinates to include in the calculation. */ public var RouteControllerMaximumSpeedForUsingCurrentStep: CLLocationSpeed = 1 -/** - Keys in the user info dictionaries of various notifications posted by instances - of `RouteController`. - */ -public typealias RouteControllerNotificationUserInfoKey = MBRouteControllerNotificationUserInfoKey - -extension Notification.Name { +public extension Notification.Name { /** - Posted when `RouteController` fails to reroute the user after the user diverges from the expected route. + Posted when `RouteController` receives a user location update representing movement along the expected route. - The user info dictionary contains the key `RouteControllerNotificationUserInfoKey.errorKey`. + The user info dictionary contains the keys `MBRouteControllerRouteProgressKey` and `MBRouteControllerLocationKey`. */ - public static let routeControllerDidFailToReroute = MBRouteControllerDidFailToReroute + static let routeControllerProgressDidChange: Notification.Name = .init(rawValue: "RouteControllerProgressDidChange") /** Posted after the user diverges from the expected route, just before `RouteController` attempts to calculate a new route. - The user info dictionary contains the key `RouteControllerNotificationUserInfoKey.locationKey`. + The user info dictionary contains the key `MBRouteControllerLocationKey`. */ - public static let routeControllerWillReroute = MBRouteControllerWillReroute + static let routeControllerWillReroute: Notification.Name = .init(rawValue: "RouteControllerWillReroute") /** Posted when `RouteController` obtains a new route in response to the user diverging from a previous route. - The user info dictionary contains the keys `RouteControllerNotificationUserInfoKey.locationKey` and `RouteControllerNotificationUserInfoKey.isProactiveKey`. + The user info dictionary contains the keys `MBRouteControllerLocationKey` and `MBRouteControllerIsProactiveKey`. */ - public static let routeControllerDidReroute = MBRouteControllerDidReroute + static let routeControllerDidReroute: Notification.Name = .init(rawValue: "RouteControllerDidReroute") /** - Posted when `RouteController` receives a user location update representing movement along the expected route. + Posted when `RouteController` fails to reroute the user after the user diverges from the expected route. - The user info dictionary contains the keys `RouteControllerNotificationUserInfoKey.routeProgressKey`, `RouteControllerNotificationUserInfoKey.locationKey`, and `RouteControllerNotificationUserInfoKey.rawLocationKey`. + The user info dictionary contains the key `MBRouteControllerRoutingErrorKey`. */ - public static let routeControllerProgressDidChange = MBRouteControllerProgressDidChange + static let routeControllerDidFailToReroute: Notification.Name = .init(rawValue: "RouteControllerDidFailToReroute") /** Posted when `RouteController` detects that the user has passed an ideal point for saying an instruction aloud. - The user info dictionary contains the key `RouteControllerNotificationUserInfoKey.routeProgressKey`. + The user info dictionary contains the key `MBRouteControllerRouteProgressKey`. + */ + static let routeControllerDidPassSpokenInstructionPoint: Notification.Name = .init(rawValue: "RouteControllerDidPassSpokenInstructionPoint") + + /** + Posted when `RouteController` detects that the user has passed an ideal point for display an instruction visually. + + The user info dictionary contains the key `MBRouteControllerRouteProgressKey`. */ - public static let routeControllerDidPassSpokenInstructionPoint = MBRouteControllerDidPassSpokenInstructionPoint + static let routeControllerDidPassVisualInstructionPoint: Notification.Name = .init(rawValue: "MBRouteControllerDidPassVisualInstructionPoint") /** - Posted when `RouteController` detects that the user has passed an ideal point for displaying an instruction. + Posted when something changes in the shared `MBNavigationSettings` object. + + The user info dictionary indicates which keys and values changed. - The user info dictionary contains the key `RouteControllerNotificationUserInfoKey.routeProgressKey`. */ - public static let routeControllerDidPassVisualInstructionPoint = MBRouteControllerDidPassVisualInstructionPoint + static let navigationSettingsDidChange: Notification.Name = .init(rawValue: "MBNavigationSettingsDidChange") +} + +/** + Keys in the user info dictionaries of various notifications posted by instances + of `RouteController`. + */ +public struct RouteControllerNotificationUserInfoKey: Hashable, Equatable, RawRepresentable { + public typealias RawValue = String + + public var rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } + + /** + A key in the user info dictionary of a `routeControllerProgressDidChange`, `routeControllerDidPassVisualInstructionPoint`, or `routeControllerDidPassSpokenInstructionPoint` notification. The corresponding value is a `RouteProgress` object representing the current route progress. + */ + public static let routeProgressKey: RouteControllerNotificationUserInfoKey = .init(rawValue: "progress") + + /** + A key in the user info dictionary of a `routeControllerProgressDidChange` or `routeControllerWillReroute` notification. The corresponding value is a `CLLocation` object representing the current idealized user location. + */ + public static let locationKey: RouteControllerNotificationUserInfoKey = .init(rawValue: "location") + + /** + A key in the user info dictionary of a `routeControllerProgressDidChange` or `routeControllerWillReroute` notification. The corresponding value is a `CLLocation` object representing the current raw user location. + */ + public static let rawLocationKey: RouteControllerNotificationUserInfoKey = .init(rawValue: "rawLocation") + + /** + A key in the user info dictionary of a `routeControllerDidFailToReroute` notification. The corresponding value is an `NSError` object indicating why `RouteController` was unable to calculate a new route. + */ + public static let routingErrorKey: RouteControllerNotificationUserInfoKey = .init(rawValue: "error") + + /** + A key in the user info dictionary of an `routeControllerDidPassVisualInstructionPoint`. The corresponding value is an `MBVisualInstruction` object representing the current visual instruction. + */ + public static let visualInstructionKey: RouteControllerNotificationUserInfoKey = .init(rawValue: "visualInstruction") + + /** + A key in the user info dictionary of a `routeControllerDidPassSpokenInstructionPoint` notification. The corresponding value is an `MBSpokenInstruction` object representing the current visual instruction. + */ + public static let spokenInstructionKey: RouteControllerNotificationUserInfoKey = .init(rawValue: "spokenInstruction") + + /** + A key in the user info dictionary of a `routeControllerDidReroute` notification. The corresponding value is an `NSNumber` instance containing a Boolean value indicating whether `RouteController` proactively rerouted the user onto a faster route. + */ + public static let isProactiveKey: RouteControllerNotificationUserInfoKey = .init(rawValue: "RouteControllerDidFindFasterRoute") } + diff --git a/MapboxCoreNavigation/DispatchTimer.swift b/MapboxCoreNavigation/DispatchTimer.swift index 0a10dd1db44..2a683fdbc6f 100644 --- a/MapboxCoreNavigation/DispatchTimer.swift +++ b/MapboxCoreNavigation/DispatchTimer.swift @@ -35,7 +35,6 @@ public class DispatchTimer { - parameter executingOn: the queue on which the timer executes. Default is main queue. - parameter payload: The payload that executes when the timer expires. */ - public init(countdown: DispatchTimeInterval, repeating repetition: DispatchTimeInterval = .never, accuracy: DispatchTimeInterval = defaultAccuracy, executingOn executionQueue: DispatchQueue = .main, payload: @escaping Payload) { countdownInterval = countdown repetitionInterval = repetition @@ -63,7 +62,7 @@ public class DispatchTimer { /** Arm the timer. Countdown will begin after this function returns. - */ + */ public func arm() { guard state == .disarmed, !timer.isCancelled else { return } state = .armed @@ -72,7 +71,6 @@ public class DispatchTimer { if let unwrappedSelf = self { unwrappedSelf.executionQueue.async(execute: unwrappedSelf.payload) } - } timer.resume() } diff --git a/MapboxCoreNavigation/DistanceFormatter.swift b/MapboxCoreNavigation/DistanceFormatter.swift index 95722c33103..629034dc828 100644 --- a/MapboxCoreNavigation/DistanceFormatter.swift +++ b/MapboxCoreNavigation/DistanceFormatter.swift @@ -239,7 +239,6 @@ open class DistanceFormatter: Formatter, NSSecureCoding { } extension Double { - func rounded(precision: Double) -> Double { if precision == 0 { return Double(Int(rounded())) diff --git a/MapboxCoreNavigation/EndOfRouteFeedback.swift b/MapboxCoreNavigation/EndOfRouteFeedback.swift index 39f93d54af4..d65de5f0df7 100644 --- a/MapboxCoreNavigation/EndOfRouteFeedback.swift +++ b/MapboxCoreNavigation/EndOfRouteFeedback.swift @@ -1,17 +1,17 @@ import Foundation /** - Feedback Model Object for End Of Route Experience. + Feedback Model Object for End Of Route Experience. */ -@objc open class EndOfRouteFeedback: NSObject { +open class EndOfRouteFeedback: NSObject { /** Rating: The user's rating for the route. Normalized between 0 and 100. - */ + */ let rating: Int? /** Comment: Any comments that the user had about the route. - */ + */ let comment: String? @nonobjc public init(rating: Int? = nil, comment: String? = nil) { @@ -19,7 +19,7 @@ import Foundation self.comment = comment super.init() } - @objc public convenience init(rating ratingNumber: NSNumber?, comment: String?) { + public convenience init(rating ratingNumber: NSNumber?, comment: String?) { let rating = ratingNumber?.intValue self.init(rating: rating, comment: comment) } diff --git a/MapboxCoreNavigation/EventDetails.swift b/MapboxCoreNavigation/EventDetails.swift index 8c6191c8e13..d5c16e4ec8c 100644 --- a/MapboxCoreNavigation/EventDetails.swift +++ b/MapboxCoreNavigation/EventDetails.swift @@ -53,7 +53,6 @@ struct PerformanceEventDetails: EventDetails { } struct NavigationEventDetails: EventDetails { - let audioType: String = AVAudioSession.sharedInstance().audioType let applicationState = UIApplication.shared.applicationState let batteryLevel: Int = UIDevice.current.batteryLevel >= 0 ? Int(UIDevice.current.batteryLevel * 100) : -1 @@ -121,8 +120,8 @@ struct NavigationEventDetails: EventDetails { requestIdentifier = dataSource.routeProgress.route.routeIdentifier if let location = dataSource.router.rawLocation, - let coordinates = dataSource.routeProgress.route.coordinates, - let lastCoord = coordinates.last { + let coordinates = dataSource.routeProgress.route.coordinates, + let lastCoord = coordinates.last { userAbsoluteDistanceToDestination = location.distance(from: CLLocation(latitude: lastCoord.latitude, longitude: lastCoord.longitude)) } else { userAbsoluteDistanceToDestination = nil @@ -159,7 +158,6 @@ struct NavigationEventDetails: EventDetails { locationEngine = String(describing: dataSource.locationProvider) locationManagerDesiredAccuracy = dataSource.desiredAccuracy - var totalTimeInPortrait = session.timeSpentInPortrait var totalTimeInLandscape = session.timeSpentInLandscape if UIDevice.current.orientation.isPortrait { @@ -178,7 +176,6 @@ struct NavigationEventDetails: EventDetails { } percentTimeInForeground = totalTimeInPortrait + totalTimeInLandscape == 0 ? 100 : Int((totalTimeInPortrait / (totalTimeInPortrait + totalTimeInLandscape) * 100)) - stepIndex = dataSource.routeProgress.currentLegProgress.stepIndex stepCount = dataSource.routeProgress.currentLeg.steps.count legIndex = dataSource.routeProgress.legIndex @@ -299,7 +296,6 @@ struct NavigationEventDetails: EventDetails { } extension RouteLegProgress: Encodable { - private enum CodingKeys: String, CodingKey { case upcomingInstruction case upcomingType diff --git a/MapboxCoreNavigation/Feedback.swift b/MapboxCoreNavigation/Feedback.swift index bf9765fa9f3..22c2f990fb0 100644 --- a/MapboxCoreNavigation/Feedback.swift +++ b/MapboxCoreNavigation/Feedback.swift @@ -3,7 +3,6 @@ import Foundation /** Feedback type is used to specify the type of feedback being recorded with `NavigationEventsManager.recordFeedback(type:description:)`. */ -@objc(MBFeedbackType) public enum FeedbackType: Int, CustomStringConvertible { /** Indicates general feedback. You should provide a `description` string to `NavigationEventsManager.recordFeedback(type:description:)` to elaborate on the feedback if possible. @@ -88,7 +87,6 @@ public enum FeedbackType: Int, CustomStringConvertible { } } -@objc(MBFeedbackSource) public enum FeedbackSource: Int, CustomStringConvertible { case user case reroute diff --git a/MapboxCoreNavigation/LegacyRouteController.swift b/MapboxCoreNavigation/LegacyRouteController.swift index 1bf070b8ab6..851b0ae91f1 100644 --- a/MapboxCoreNavigation/LegacyRouteController.swift +++ b/MapboxCoreNavigation/LegacyRouteController.swift @@ -5,46 +5,42 @@ import Polyline import MapboxMobileEvents import Turf - protocol RouteControllerDataSource: class { var location: CLLocation? { get } var locationProvider: NavigationLocationManager.Type { get } } - -@objc(MBLegacyRouteController) @available(*, deprecated, renamed: "RouteController") -open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationManagerDelegate { +open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationManagerDelegate { - @objc public weak var delegate: RouterDelegate? + public weak var delegate: RouterDelegate? - @objc public unowned var dataSource: RouterDataSource + public unowned var dataSource: RouterDataSource /** The Directions object used to create the route. */ - @objc public var directions: Directions - + public var directions: Directions /** The threshold used when we determine when the user has arrived at the waypoint. By default, we claim arrival 5 seconds before the user is physically estimated to arrive. - */ - @objc public var waypointArrivalThreshold: TimeInterval = 5.0 + */ + public var waypointArrivalThreshold: TimeInterval = 5.0 - @objc public var reroutesProactively = true + public var reroutesProactively = true var didFindFasterRoute = false var lastProactiveRerouteDate: Date? - @objc public var routeProgress: RouteProgress { + public var routeProgress: RouteProgress { get { return _routeProgress } set { if let location = self.location { - delegate?.router?(self, willRerouteFrom: location) + delegate?.router(self, willRerouteFrom: location) } _routeProgress = newValue announce(reroute: routeProgress.route, at: location, proactive: didFindFasterRoute) @@ -95,14 +91,12 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa } deinit { - if delegate?.routerShouldDisableBatteryMonitoring?(self) ?? RouteController.DefaultBehavior.shouldDisableBatteryMonitoring { + if let del = delegate, del.routerShouldDisableBatteryMonitoring(self) { UIDevice.current.isBatteryMonitoringEnabled = false } - } - @objc public var location: CLLocation? { - + public var location: CLLocation? { // If there is no snapped location, and the rawLocation course is unqualified, use the user's heading as long as it is accurate. if snappedLocation == nil, let heading = heading, @@ -146,7 +140,7 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa userSnapToStepDistanceFromManeuver = Polyline(coordinates).distance(from: coordinate) } - @objc public var reroutingTolerance: CLLocationDistance { + public var reroutingTolerance: CLLocationDistance { guard let intersections = routeProgress.currentLegProgress.currentStepProgress.intersectionsIncludingUpcomingManeuverIntersection else { return RouteControllerMaximumDistanceBeforeRecalculating } guard let userLocation = rawLocation else { return RouteControllerMaximumDistanceBeforeRecalculating } @@ -180,12 +174,11 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa return true } - @objc public func userIsOnRoute(_ location: CLLocation) -> Bool { - + public func userIsOnRoute(_ location: CLLocation) -> Bool { // If the user has arrived, do not continue monitor reroutes, step progress, etc if routeProgress.currentLegProgress.userHasArrivedAtWaypoint && - (delegate?.router?(self, shouldPreventReroutesWhenArrivingAt: routeProgress.currentLeg.destination) ?? - RouteController.DefaultBehavior.shouldPreventReroutesWhenArrivingAtWaypoint) { + (delegate?.router(self, shouldPreventReroutesWhenArrivingAt: routeProgress.currentLeg.destination) ?? + RouteController.DefaultBehavior.shouldPreventReroutesWhenArrivingAtWaypoint) { return true } @@ -222,11 +215,11 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa // MARK: CLLocationManagerDelegate methods - @objc public func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) { + public func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) { heading = newHeading } - @objc public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { let filteredLocations = locations.filter { return $0.isQualified } @@ -244,8 +237,7 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa potentialLocation = lastFiltered // `filteredLocations` does not contain good locations and we have found at least one good location previously. } else if hasFoundOneQualifiedLocation { - if let lastLocation = locations.last, delegate?.router?(self, shouldDiscard: lastLocation) ?? RouteController.DefaultBehavior.shouldDiscardLocation { - + if let lastLocation = locations.last, delegate?.router(self, shouldDiscard: lastLocation) ?? RouteController.DefaultBehavior.shouldDiscardLocation { // Allow the user puck to advance. A stationary puck is not great. self.rawLocation = lastLocation @@ -263,7 +255,6 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa self.rawLocation = location - updateIntersectionIndex(for: currentStepProgress) // Notify observers if the step’s remaining distance has changed. @@ -273,7 +264,7 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa updateRouteLegProgress(for: location) updateVisualInstructionProgress() - if !userIsOnRoute(location) && delegate?.router?(self, shouldRerouteFrom: location) ?? RouteController.DefaultBehavior.shouldRerouteFromLocation { + if !userIsOnRoute(location) && delegate?.router(self, shouldRerouteFrom: location) ?? RouteController.DefaultBehavior.shouldRerouteFromLocation { reroute(from: location, along: routeProgress) return } @@ -285,7 +276,6 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa } private func update(progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { - let stepProgress = progress.currentLegProgress.currentStepProgress let step = stepProgress.step @@ -297,7 +287,7 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa stepProgress.distanceTraveled = distanceTraveled //Fire the delegate method - delegate?.router?(self, didUpdate: progress, with: location, rawLocation: rawLocation) + delegate?.router(self, didUpdate: progress, with: location, rawLocation: rawLocation) //Fire the notification (for now) NotificationCenter.default.post(name: .routeControllerProgressDidChange, object: self, userInfo: [ @@ -321,20 +311,18 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa // We are at least at the "You will arrive" instruction if legProgress.remainingSteps.count <= 1 && remainingVoiceInstructions.count <= 1 && currentDestination != previousArrivalWaypoint { - //Have we actually arrived? Last instruction is "You have arrived" if remainingVoiceInstructions.count == 0, legProgress.durationRemaining <= waypointArrivalThreshold { previousArrivalWaypoint = currentDestination legProgress.userHasArrivedAtWaypoint = true - let advancesToNextLeg = delegate?.router?(self, didArriveAt: currentDestination) ?? RouteController.DefaultBehavior.didArriveAtWaypoint + let advancesToNextLeg = delegate?.router(self, didArriveAt: currentDestination) ?? RouteController.DefaultBehavior.didArriveAtWaypoint guard !routeProgress.isFinalLeg && advancesToNextLeg else { return } advanceLegIndex(location: location) updateDistanceToManeuver() - } else { //we are approaching the destination - delegate?.router?(self, willArriveAt: currentDestination, after: legProgress.durationRemaining, distance: legProgress.distanceRemaining) + delegate?.router(self, willArriveAt: currentDestination, after: legProgress.durationRemaining, distance: legProgress.distanceRemaining) } } } @@ -352,7 +340,7 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa isRerouting = true - delegate?.router?(self, willRerouteFrom: location) + delegate?.router(self, willRerouteFrom: location) NotificationCenter.default.post(name: .routeControllerWillReroute, object: self, userInfo: [ RouteControllerNotificationUserInfoKey.locationKey: location ]) @@ -366,7 +354,7 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa strongSelf.isRerouting = false if let error = error { - strongSelf.delegate?.router?(strongSelf, didFailToRerouteWith: error) + strongSelf.delegate?.router(strongSelf, didFailToRerouteWith: error) NotificationCenter.default.post(name: .routeControllerDidFailToReroute, object: self, userInfo: [ RouteControllerNotificationUserInfoKey.routingErrorKey: error ]) @@ -477,8 +465,7 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa for spokenInstruction in spokenInstructions { if userSnapToStepDistanceFromManeuver <= spokenInstruction.distanceAlongStep || firstInstructionOnFirstStep { - - delegate?.router?(self, didPassSpokenInstructionPoint: spokenInstruction, routeProgress: routeProgress) + delegate?.router(self, didPassSpokenInstructionPoint: spokenInstruction, routeProgress: routeProgress) NotificationCenter.default.post(name: .routeControllerDidPassSpokenInstructionPoint, object: self, userInfo: [ RouteControllerNotificationUserInfoKey.routeProgressKey: routeProgress, RouteControllerNotificationUserInfoKey.spokenInstructionKey: spokenInstruction @@ -497,7 +484,7 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa for visualInstruction in visualInstructions { if userSnapToStepDistanceFromManeuver <= visualInstruction.distanceAlongStep || isFirstLocation { - delegate?.router?(self, didPassVisualInstructionPoint: visualInstruction, routeProgress: routeProgress) + delegate?.router(self, didPassVisualInstructionPoint: visualInstruction, routeProgress: routeProgress) NotificationCenter.default.post(name: .routeControllerDidPassVisualInstructionPoint, object: self, userInfo: [ RouteControllerNotificationUserInfoKey.routeProgressKey: routeProgress, RouteControllerNotificationUserInfoKey.visualInstructionKey: visualInstruction, @@ -532,14 +519,14 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa @available(*, deprecated, message: "MapboxNavigationService is now the point-of-entry to MapboxCoreNavigation. Direct use of RouteController is no longer reccomended. See MapboxNavigationService for more information.") /// :nodoc: Obsoleted method. - @objc(initWithRoute:directions:dataSource:eventsManager:) + public convenience init(along route: Route, directions: Directions = Directions.shared, dataSource: NavigationLocationManager = NavigationLocationManager(), eventsManager: NavigationEventsManager) { fatalError() } @available(*, deprecated, message: "RouteController no longer manages a location manager directly. Instead, the Router protocol conforms to CLLocationManagerDelegate, and RouteControllerDataSource provides access to synchronous location requests.") /// :nodoc: obsoleted - @objc public final var locationManager: NavigationLocationManager! { + public final var locationManager: NavigationLocationManager! { get { fatalError() } @@ -549,7 +536,7 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa } @available(*, deprecated, renamed: "NavigationService.locationManager", message: "NavigationViewController no longer directly manages an NavigationLocationManager. See MapboxNavigationService, which contains a reference to the locationManager, for more information.") /// :nodoc: obsoleted - @objc public final var tunnelIntersectionManager: Any! { + public final var tunnelIntersectionManager: Any! { get { fatalError() } @@ -559,7 +546,7 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa } @available(*, deprecated, renamed: "navigationService.eventsManager", message: "NavigationViewController no longer directly manages a NavigationEventsManager. See MapboxNavigationService, which contains a reference to the eventsManager, for more information.") /// :nodoc: obsoleted - @objc public final var eventsManager: NavigationEventsManager! { + public final var eventsManager: NavigationEventsManager! { get { fatalError() } @@ -567,4 +554,19 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa fatalError() } } + + /// Required through `Router` protocol. No-op + public func enableLocationRecording() { + // no-op + } + /// Required through `Router` protocol. No-op + + public func disableLocationRecording() { + // no-op + } + + /// Required through `Router` protocol. No-op + public func locationHistory() -> String? { + return nil + } } diff --git a/MapboxCoreNavigation/Locale.swift b/MapboxCoreNavigation/Locale.swift index 04843110a61..8fb17afc56d 100644 --- a/MapboxCoreNavigation/Locale.swift +++ b/MapboxCoreNavigation/Locale.swift @@ -1,7 +1,6 @@ import Foundation extension Locale { - /** Given the app's localized language setting, returns a string representing the user's localization. */ diff --git a/MapboxCoreNavigation/MBNavigationSettings.h b/MapboxCoreNavigation/MBNavigationSettings.h deleted file mode 100644 index 397a739a0a1..00000000000 --- a/MapboxCoreNavigation/MBNavigationSettings.h +++ /dev/null @@ -1,10 +0,0 @@ -#import - -/** - Posted when something changes in the shared `MBNavigationSettings` object. - - The user info dictionary indicates which keys and values changed. - - :nodoc: - */ -extern const NSNotificationName MBNavigationSettingsDidChangeNotification; diff --git a/MapboxCoreNavigation/MBNavigationSettings.m b/MapboxCoreNavigation/MBNavigationSettings.m deleted file mode 100644 index 3e5d80fd392..00000000000 --- a/MapboxCoreNavigation/MBNavigationSettings.m +++ /dev/null @@ -1,3 +0,0 @@ -#import "MBNavigationSettings.h" - -const NSNotificationName MBNavigationSettingsDidChangeNotification = @"MBNavigationSettingsDidChange"; diff --git a/MapboxCoreNavigation/MBNavigator.swift b/MapboxCoreNavigation/MBNavigator.swift index f02ff435cc2..55c275fbc68 100644 --- a/MapboxCoreNavigation/MBNavigator.swift +++ b/MapboxCoreNavigation/MBNavigator.swift @@ -3,7 +3,6 @@ import MapboxNavigationNative import CoreLocation extension MBFixLocation { - convenience init(_ location: CLLocation) { self.init(coordinate: location.coordinate, time: location.timestamp, @@ -16,7 +15,6 @@ extension MBFixLocation { } extension MBRouteState: CustomStringConvertible { - public var description: String { switch self { case .invalid: diff --git a/MapboxCoreNavigation/MBRouteController.h b/MapboxCoreNavigation/MBRouteController.h deleted file mode 100644 index 517934a8c79..00000000000 --- a/MapboxCoreNavigation/MBRouteController.h +++ /dev/null @@ -1,95 +0,0 @@ -#import - -/** - Posted when `MBRouteController` receives a user location update representing movement along the expected route. - - The user info dictionary contains the keys `MBRouteControllerRouteProgressKey` and `MBRouteControllerLocationKey`. - - :nodoc: - */ -extern const _Nonnull NSNotificationName MBRouteControllerProgressDidChangeNotification; - -/** - Posted after the user diverges from the expected route, just before `MBRouteController` attempts to calculate a new route. - - The user info dictionary contains the key `MBRouteControllerLocationKey`. - - :nodoc: - */ -extern const _Nonnull NSNotificationName MBRouteControllerWillRerouteNotification; - -/** - Posted when `MBRouteController` obtains a new route in response to the user diverging from a previous route. - - The user info dictionary contains the keys `MBRouteControllerLocationKey` and `MBRouteControllerIsProactiveKey`. - - :nodoc: - */ -extern const _Nonnull NSNotificationName MBRouteControllerDidRerouteNotification; - -/** - Posted when `MBRouteController` fails to reroute the user after the user diverges from the expected route. - - The user info dictionary contains the key `MBRouteControllerRoutingErrorKey`. - - :nodoc: - */ -extern const _Nonnull NSNotificationName MBRouteControllerDidFailToRerouteNotification; - -/** - Posted when `MBRouteController` detects that the user has passed an ideal point for saying an instruction aloud. - - The user info dictionary contains the key `MBRouteControllerRouteProgressKey`. - - :nodoc: - */ -extern const _Nonnull NSNotificationName MBRouteControllerDidPassSpokenInstructionPointNotification; - -extern const _Nonnull NSNotificationName MBRouteControllerDidPassVisualInstructionPointNotification; - -/** - Keys in the user info dictionaries of various notifications posted by instances of `MBRouteController`. - - :nodoc: - */ -typedef NSString *MBRouteControllerNotificationUserInfoKey NS_EXTENSIBLE_STRING_ENUM; - -/** - A key in the user info dictionary of a `MBRouteControllerProgressDidChangeNotification`, `MBRouteControllerDidPassVisualInstructionPointNotification`, or `MBRouteControllerDidPassVisualInstructionPointNotification` notification. The corresponding value is a `RouteProgress` object representing the current route progress. - */ -extern const _Nonnull MBRouteControllerNotificationUserInfoKey MBRouteControllerRouteProgressKey; - -/** - A key in the user info dictionary of an `MBRouteControllerDidPassVisualInstructionPointNotification`. The corresponding value is an `MBVisualInstruction` object representing the current visual instruction. - */ -extern const _Nonnull MBRouteControllerNotificationUserInfoKey MBRouteControllerVisualInstructionKey; - -/** - A key in the user info dictionary of a `MBRouteControllerDidPassSpokenInstructionPointNotification` notification. The corresponding value is an `MBSpokenInstruction` object representing the current visual instruction. - */ -extern const _Nonnull MBRouteControllerNotificationUserInfoKey MBRouteControllerSpokenInstructionKey; - -/** - A key in the user info dictionary of a `MBRouteControllerProgressDidChangeNotification` or `MBRouteControllerWillRerouteNotification` notification. The corresponding value is a `CLLocation` object representing the current idealized user location. - */ -extern const _Nonnull MBRouteControllerNotificationUserInfoKey MBRouteControllerLocationKey; - -/** - A key in the user info dictionary of a `MBRouteControllerProgressDidChangeNotification` or `MBRouteControllerWillRerouteNotification` notification. The corresponding value is a `CLLocation` object representing the current raw user location. - */ -extern const _Nonnull MBRouteControllerNotificationUserInfoKey MBRouteControllerRawLocationKey; - -/** - A key in the user info dictionary of a `MBRouteControllerDidFailToRerouteNotification` notification. The corresponding value is an `NSError` object indicating why `RouteController` was unable to calculate a new route. - */ -extern const _Nonnull MBRouteControllerNotificationUserInfoKey MBRouteControllerRoutingErrorKey; - -/** - A key in the user info dictionary of a `MBRouteControllerDidRerouteNotification` notification. The corresponding value is an `NSNumber` instance containing a Boolean value indicating whether `RouteController` proactively rerouted the user onto a faster route. - */ -extern const _Nonnull MBRouteControllerNotificationUserInfoKey MBRouteControllerIsProactiveKey; - -@interface NSString (MD5) -- (NSString * _Nonnull)md5; -@end - diff --git a/MapboxCoreNavigation/MBRouteController.m b/MapboxCoreNavigation/MBRouteController.m deleted file mode 100644 index 940d235e54f..00000000000 --- a/MapboxCoreNavigation/MBRouteController.m +++ /dev/null @@ -1,36 +0,0 @@ -#import "MBRouteController.h" -#import - -const NSNotificationName MBRouteControllerProgressDidChangeNotification = @"RouteControllerProgressDidChange"; -const NSNotificationName MBRouteControllerDidPassSpokenInstructionPointNotification = @"RouteControllerDidPassSpokenInstructionPoint"; -const NSNotificationName MBRouteControllerDidPassVisualInstructionPointNotification = @"MBRouteControllerDidPassVisualInstructionPoint"; -const NSNotificationName MBRouteControllerWillRerouteNotification = @"RouteControllerWillReroute"; -const NSNotificationName MBRouteControllerDidRerouteNotification = @"RouteControllerDidReroute"; -const NSNotificationName MBRouteControllerDidFailToRerouteNotification = @"RouteControllerDidFailToReroute"; - -const MBRouteControllerNotificationUserInfoKey MBRouteControllerRouteProgressKey = @"progress"; -const MBRouteControllerNotificationUserInfoKey MBRouteControllerLocationKey = @"location"; -const MBRouteControllerNotificationUserInfoKey MBRouteControllerRawLocationKey = @"rawLocation"; -const MBRouteControllerNotificationUserInfoKey MBRouteControllerRoutingErrorKey = @"error"; -const MBRouteControllerNotificationUserInfoKey MBRouteControllerVisualInstructionKey = @"visualInstruction"; -const MBRouteControllerNotificationUserInfoKey MBRouteControllerSpokenInstructionKey = @"spokenInstruction"; -const MBRouteControllerNotificationUserInfoKey MBRouteControllerIsProactiveKey = @"RouteControllerDidFindFasterRoute"; - -NSString *const MBErrorDomain = @"ErrorDomain"; - - -@implementation NSString (MD5) -- (NSString * _Nonnull)md5 { - const char *cStr = [self UTF8String]; - unsigned char digest[16]; - CC_MD5( cStr, (CC_LONG)strlen(cStr), digest ); - - NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; - - for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) - [output appendFormat:@"%02x", digest[i]]; - - return output; -} -@end - diff --git a/MapboxCoreNavigation/MMEEventsManager.swift b/MapboxCoreNavigation/MMEEventsManager.swift index 73778bdd691..f101508b53c 100644 --- a/MapboxCoreNavigation/MMEEventsManager.swift +++ b/MapboxCoreNavigation/MMEEventsManager.swift @@ -3,7 +3,6 @@ import MapboxDirections import AVFoundation import MapboxMobileEvents - let SecondsBeforeCollectionAfterFeedbackEvent: TimeInterval = 20 let EventVersion = 8 diff --git a/MapboxCoreNavigation/MapboxCoreNavigation.h b/MapboxCoreNavigation/MapboxCoreNavigation.h index 58d10f37201..8934c627ac8 100644 --- a/MapboxCoreNavigation/MapboxCoreNavigation.h +++ b/MapboxCoreNavigation/MapboxCoreNavigation.h @@ -6,10 +6,3 @@ FOUNDATION_EXPORT double MapboxCoreNavigationVersionNumber; //! Project version string for MapboxCoreNavigation. FOUNDATION_EXPORT const unsigned char MapboxCoreNavigationVersionString[]; -#import "MBNavigationSettings.h" -#import "MBRouteController.h" - -/** - Constant representing the domain in which errors created in this library will live under. - */ -extern NSString *const MBErrorDomain; diff --git a/MapboxCoreNavigation/MinimumEditDistance.swift b/MapboxCoreNavigation/MinimumEditDistance.swift index 804ee032439..6ba6c04ebff 100644 --- a/MapboxCoreNavigation/MinimumEditDistance.swift +++ b/MapboxCoreNavigation/MinimumEditDistance.swift @@ -1,7 +1,6 @@ import Foundation extension String { - // Adapted from https://github.com/raywenderlich/swift-algorithm-club/blob/master/Minimum%20Edit%20Distance/MinimumEditDistance.playground/Contents.swift func minimumEditDistance(to word: String) -> Int { let fromWordCount = count diff --git a/MapboxCoreNavigation/NavigationEventsManager.swift b/MapboxCoreNavigation/NavigationEventsManager.swift index 2411d590c90..8017c75db39 100644 --- a/MapboxCoreNavigation/NavigationEventsManager.swift +++ b/MapboxCoreNavigation/NavigationEventsManager.swift @@ -4,10 +4,10 @@ import MapboxDirections let NavigationEventTypeRouteRetrieval = "mobile.performance_trace" - /** +/** The `EventsManagerDataSource` protocol declares values required for recording route following events. */ -@objc public protocol EventsManagerDataSource: class { +public protocol EventsManagerDataSource: class { var routeProgress: RouteProgress { get } var router: Router! { get } var desiredAccuracy: CLLocationAccuracy { get } @@ -20,9 +20,7 @@ public typealias EventsManager = NavigationEventsManager /** The `NavigationEventsManager` is responsible for being the liaison between MapboxCoreNavigation and the Mapbox telemetry framework. */ -@objc(MBNavigationEventsManager) open class NavigationEventsManager: NSObject { - var sessionState: SessionState? var outstandingFeedbackEvents = [CoreFeedbackEvent]() @@ -33,8 +31,8 @@ open class NavigationEventsManager: NSObject { Indicates whether the application depends on MapboxNavigation in addition to MapboxCoreNavigation. */ var usesDefaultUserInterface = { - // Assumption: MapboxNavigation.framework includes NavigationViewController and exposes it to the Objective-C runtime as MBNavigationViewController. - return NSClassFromString("MBNavigationViewController") != nil + // Assumption: MapboxNavigation.framework includes NavigationViewController and exposes it to the Objective-C runtime as MapboxNavigation.NavigationViewController. + return NSClassFromString("MapboxNavigation.NavigationViewController") != nil }() /// :nodoc: the internal lower-level mobile events manager is an implementation detail which should not be manipulated directly @@ -52,7 +50,7 @@ open class NavigationEventsManager: NSObject { return token }() - @objc public required init(dataSource source: EventsManagerDataSource?, accessToken possibleToken: String? = nil, mobileEventsManager: MMEEventsManager = .shared()) { + public required init(dataSource source: EventsManagerDataSource?, accessToken possibleToken: String? = nil, mobileEventsManager: MMEEventsManager = .shared()) { dataSource = source super.init() if let tokenOverride = possibleToken { @@ -82,7 +80,7 @@ open class NavigationEventsManager: NSObject { /** When set to `false`, flushing of telemetry events is not delayed. Is set to `true` by default. */ - @objc public var delaysEventFlushing = true + public var delaysEventFlushing = true func start() { let eventLoggingEnabled = UserDefaults.standard.bool(forKey: NavigationMetricsDebugLoggingEnabled) @@ -315,7 +313,7 @@ open class NavigationEventsManager: NSObject { You can then call `updateFeedback(uuid:type:source:description:)` with the returned feedback UUID to attach any additional metadata to the feedback. */ - @objc public func recordFeedback(type: FeedbackType = .general, description: String? = nil) -> UUID? { + public func recordFeedback(type: FeedbackType = .general, description: String? = nil) -> UUID? { return enqueueFeedbackEvent(type: type, description: description) } @@ -324,7 +322,7 @@ open class NavigationEventsManager: NSObject { Note that feedback is sent 20 seconds after being recorded, so you should promptly update the feedback metadata after the user discards any feedback UI. */ - @objc public func updateFeedback(uuid: UUID, type: FeedbackType, source: FeedbackSource, description: String?) { + public func updateFeedback(uuid: UUID, type: FeedbackType, source: FeedbackSource, description: String?) { if let lastFeedback = outstandingFeedbackEvents.first(where: { $0.id == uuid}) as? FeedbackEvent { lastFeedback.update(type: type, source: source, description: description) } @@ -333,7 +331,7 @@ open class NavigationEventsManager: NSObject { /** Discard a recorded feedback event, for example if you have a custom feedback UI and the user canceled feedback. */ - @objc public func cancelFeedback(uuid: UUID) { + public func cancelFeedback(uuid: UUID) { if let index = outstandingFeedbackEvents.firstIndex(where: {$0.id == uuid}) { outstandingFeedbackEvents.remove(at: index) } @@ -374,7 +372,7 @@ open class NavigationEventsManager: NSObject { latestReroute?.update(newRoute: route) } - @objc func update(progress: RouteProgress) { + func update(progress: RouteProgress) { defer { // ensure we always flush, irrespective of how the method exits sendOutstandingFeedbackEvents(forceAll: false) diff --git a/MapboxCoreNavigation/NavigationLocationManager.swift b/MapboxCoreNavigation/NavigationLocationManager.swift index 4d39ce34a94..3313777cde9 100644 --- a/MapboxCoreNavigation/NavigationLocationManager.swift +++ b/MapboxCoreNavigation/NavigationLocationManager.swift @@ -7,9 +7,7 @@ import UIKit /** `NavigationLocationManager` is the base location manager which handles permissions and background modes. */ -@objc(MBNavigationLocationManager) open class NavigationLocationManager: CLLocationManager{ - override public init() { super.init() diff --git a/MapboxCoreNavigation/NavigationRouteOptions.swift b/MapboxCoreNavigation/NavigationRouteOptions.swift index fcf6cf3b196..32124ceb956 100644 --- a/MapboxCoreNavigation/NavigationRouteOptions.swift +++ b/MapboxCoreNavigation/NavigationRouteOptions.swift @@ -6,17 +6,14 @@ import MapboxDirections `NavigationRouteOptions` is a subclass of `RouteOptions` that has been optimized for navigation. Pass an instance of this class into the `Directions.calculate(_:completionHandler:)` method. - note: `NavigationRouteOptions` is designed to be used with the `Directions` and `NavigationDirections` classes for specifying routing criteria. To customize the user experience in a `NavigationViewController`, use the `NavigationOptions` class. - */ -@objc(MBNavigationRouteOptions) open class NavigationRouteOptions: RouteOptions { - /** Initializes a navigation route options object for routes between the given waypoints and an optional profile identifier optimized for navigation. - seealso: `RouteOptions` */ - @objc public required init(waypoints: [Waypoint], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { + public required init(waypoints: [Waypoint], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { super.init(waypoints: waypoints.map { $0.coordinateAccuracy = -1 return $0 @@ -38,7 +35,7 @@ open class NavigationRouteOptions: RouteOptions { - seealso: `RouteOptions` */ - @objc public convenience init(locations: [CLLocation], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { + public convenience init(locations: [CLLocation], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { self.init(waypoints: locations.map { Waypoint(location: $0) }, profileIdentifier: profileIdentifier) } @@ -47,11 +44,11 @@ open class NavigationRouteOptions: RouteOptions { - seealso: `RouteOptions` */ - @objc public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { + public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { self.init(waypoints: coordinates.map { Waypoint(coordinate: $0) }, profileIdentifier: profileIdentifier) } - @objc public required init?(coder decoder: NSCoder) { + public required init?(coder decoder: NSCoder) { super.init(coder: decoder) } } @@ -63,15 +60,13 @@ open class NavigationRouteOptions: RouteOptions { Note: it is very important you specify the `waypoints` for the route. Usually the only two values for this `IndexSet` will be 0 and the length of the coordinates. Otherwise, all coordinates passed through will be considered waypoints. */ -@objc(MBNavigationMatchOptions) open class NavigationMatchOptions: MatchOptions { - /** Initializes a navigation route options object for routes between the given waypoints and an optional profile identifier optimized for navigation. - seealso: `MatchOptions` */ - @objc public required init(waypoints: [Waypoint], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { + public required init(waypoints: [Waypoint], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { super.init(waypoints: waypoints.map { $0.coordinateAccuracy = -1 return $0 @@ -91,7 +86,7 @@ open class NavigationMatchOptions: MatchOptions { - seealso: `MatchOptions` */ - @objc public convenience init(locations: [CLLocation], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { + public convenience init(locations: [CLLocation], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { self.init(waypoints: locations.map { Waypoint(location: $0) }, profileIdentifier: profileIdentifier) } @@ -100,11 +95,11 @@ open class NavigationMatchOptions: MatchOptions { - seealso: `MatchOptions` */ - @objc public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { + public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) { self.init(waypoints: coordinates.map { Waypoint(coordinate: $0) }, profileIdentifier: profileIdentifier) } - @objc public required init?(coder decoder: NSCoder) { + public required init?(coder decoder: NSCoder) { super.init(coder: decoder) } } diff --git a/MapboxCoreNavigation/NavigationService.swift b/MapboxCoreNavigation/NavigationService.swift index 0118738eb2e..23623f6770b 100644 --- a/MapboxCoreNavigation/NavigationService.swift +++ b/MapboxCoreNavigation/NavigationService.swift @@ -2,18 +2,14 @@ import UIKit import CoreLocation import MapboxDirections -@objc(MBNavigationSimulationIntent) public enum SimulationIntent: Int{ case manual, poorGPS } - /** The simulation mode type. Used for setting the simulation mode of the navigation service. */ -@objc(MBNavigationSimulationOptions) public enum SimulationMode: Int { - /** A setting of `.onPoorGPS` will enable simulation when we do not recieve a location update after the `poorGPSPatience` threshold has elapsed. */ @@ -39,7 +35,6 @@ public enum SimulationMode: Int { If you use a navigation service by itself, outside of `NavigationViewController`, call `start()` when the user is ready to begin navigating along the route. */ -@objc(MBNavigationService) public protocol NavigationService: CLLocationManagerDelegate, RouterDataSource, EventsManagerDataSource { /** The location manager for the service. This will be the object responsible for notifying the service of GPS updates. @@ -86,7 +81,7 @@ public protocol NavigationService: CLLocationManagerDelegate, RouterDataSource, To synchronize your application’s state with the turn-by-turn navigation experience, set this property before starting the navigation session. */ - weak var delegate: NavigationServiceDelegate? { get set } + var delegate: NavigationServiceDelegate? { get set } /** Starts the navigation service. @@ -107,7 +102,6 @@ public protocol NavigationService: CLLocationManagerDelegate, RouterDataSource, Interrogates the navigationService as to whether or not the passed-in location is in a tunnel. */ func isInTunnel(at location: CLLocation, along progress: RouteProgress) -> Bool - } /** @@ -117,9 +111,7 @@ public protocol NavigationService: CLLocationManagerDelegate, RouterDataSource, If you use a navigation service by itself, outside of `NavigationViewController`, call `start()` when the user is ready to begin navigating along the route. */ -@objc(MBNavigationService) public class MapboxNavigationService: NSObject, NavigationService { - typealias DefaultRouter = RouteController /** @@ -138,7 +130,7 @@ public class MapboxNavigationService: NSObject, NavigationService { /** The active location manager. Returns the location simulator if we're actively simulating, otherwise it returns the native location manager. - */ + */ public var locationManager: NavigationLocationManager { return simulatedLocationSource ?? nativeLocationSource } @@ -150,12 +142,12 @@ public class MapboxNavigationService: NSObject, NavigationService { /** The active router. By default, a `PortableRouteController`. - */ + */ public var router: Router! /** The events manager. Sends telemetry back to the Mapbox platform. - */ + */ public var eventsManager: NavigationEventsManager! /** @@ -165,12 +157,12 @@ public class MapboxNavigationService: NSObject, NavigationService { /** The native location source. This is a `NavigationLocationManager` by default, but can be overridden with a custom location manager at initalization. - */ + */ private var nativeLocationSource: NavigationLocationManager /** The active location simulator. Only used during `SimulationOption.always` and `SimluatedLocationManager.onPoorGPS`. If there is no simulation active, this property is `nil`. - */ + */ private var simulatedLocationSource: SimulatedLocationManager? /** @@ -214,7 +206,7 @@ public class MapboxNavigationService: NSObject, NavigationService { - parameter route: The route to follow. */ - @objc convenience init(route: Route) { + convenience init(route: Route) { self.init(route: route, directions: nil, locationSource: nil, eventsManagerType: nil) } /** @@ -227,13 +219,12 @@ public class MapboxNavigationService: NSObject, NavigationService { - parameter simulationMode: The simulation mode desired. - parameter routerType: An optional router type to use for traversing the route. */ - @objc required public init(route: Route, - directions: Directions? = nil, - locationSource: NavigationLocationManager? = nil, - eventsManagerType: NavigationEventsManager.Type? = nil, - simulating simulationMode: SimulationMode = .onPoorGPS, - routerType: Router.Type? = nil) - { + required public init(route: Route, + directions: Directions? = nil, + locationSource: NavigationLocationManager? = nil, + eventsManagerType: NavigationEventsManager.Type? = nil, + simulating simulationMode: SimulationMode = .onPoorGPS, + routerType: Router.Type? = nil) { nativeLocationSource = locationSource ?? NavigationLocationManager() self.directions = directions ?? Directions.shared self.simulationMode = simulationMode @@ -277,29 +268,28 @@ public class MapboxNavigationService: NSObject, NavigationService { public func isInTunnel(at location: CLLocation, along progress: RouteProgress) -> Bool { return TunnelAuthority.isInTunnel(at: location, along: progress) } - private func simulate(intent: SimulationIntent = .manual) { guard !isSimulating else { return } let progress = router.routeProgress - delegate?.navigationService?(self, willBeginSimulating: progress, becauseOf: intent) + delegate?.navigationService(self, willBeginSimulating: progress, becauseOf: intent) simulatedLocationSource = SimulatedLocationManager(routeProgress: progress) simulatedLocationSource?.delegate = self simulatedLocationSource?.speedMultiplier = _simulationSpeedMultiplier simulatedLocationSource?.startUpdatingLocation() simulatedLocationSource?.startUpdatingHeading() - delegate?.navigationService?(self, didBeginSimulating: progress, becauseOf: intent) + delegate?.navigationService(self, didBeginSimulating: progress, becauseOf: intent) } private func endSimulation(intent: SimulationIntent = .manual) { guard isSimulating else { return } let progress = router.routeProgress - delegate?.navigationService?(self, willEndSimulating: progress, becauseOf: intent) + delegate?.navigationService(self, willEndSimulating: progress, becauseOf: intent) simulatedLocationSource?.stopUpdatingLocation() simulatedLocationSource?.stopUpdatingHeading() simulatedLocationSource?.delegate = nil simulatedLocationSource = nil - delegate?.navigationService?(self, didEndSimulating: progress, becauseOf: intent) + delegate?.navigationService(self, didEndSimulating: progress, becauseOf: intent) } public var route: Route { @@ -354,7 +344,7 @@ public class MapboxNavigationService: NSObject, NavigationService { private func resetGPSCountdown() { //Sanity check: if we're not on this mode, we have no business here. guard simulationMode == .onPoorGPS else { return } - + // Immediately end simulation if it is occuring. if isSimulating { endSimulation(intent: .poorGPS) @@ -383,7 +373,6 @@ extension MapboxNavigationService: CLLocationManagerDelegate { } public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - //If we're always simulating, make sure this is a simulated update. if simulationMode == .always, manager != simulatedLocationSource { return } @@ -397,7 +386,6 @@ extension MapboxNavigationService: CLLocationManagerDelegate { if simulationMode == .onPoorGPS, manager == nativeLocationSource, location.isQualified { - //If the timer is disarmed, arm it. This is a good update. if poorGPSTimer.state == .disarmed, location.isQualifiedForStartingRoute { poorGPSTimer.arm() @@ -405,7 +393,6 @@ extension MapboxNavigationService: CLLocationManagerDelegate { //pass this good update onto the poor GPS timer mechanism. resetGPSCountdown() - } //Finally, pass the update onto the router. @@ -418,17 +405,15 @@ extension MapboxNavigationService: RouterDelegate { typealias Default = RouteController.DefaultBehavior public func router(_ router: Router, willRerouteFrom location: CLLocation) { - //save any progress made by the router until now eventsManager.enqueueRerouteEvent() eventsManager.incrementDistanceTraveled(by: router.routeProgress.distanceTraveled) //notify our consumer - delegate?.navigationService?(self, willRerouteFrom: location) + delegate?.navigationService(self, willRerouteFrom: location) } public func router(_ router: Router, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) { - //notify the events manager that the route has changed eventsManager.reportReroute(progress: router.routeProgress, proactive: proactive) @@ -436,49 +421,47 @@ extension MapboxNavigationService: RouterDelegate { simulatedLocationSource?.route = router.route //notify our consumer - delegate?.navigationService?(self, didRerouteAlong: route, at: location, proactive: proactive) + delegate?.navigationService(self, didRerouteAlong: route, at: location, proactive: proactive) } public func router(_ router: Router, didFailToRerouteWith error: Error) { - delegate?.navigationService?(self, didFailToRerouteWith: error) + delegate?.navigationService(self, didFailToRerouteWith: error) } public func router(_ router: Router, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { - //notify the events manager of the progress update eventsManager.update(progress: progress) //pass the update on to consumers - delegate?.navigationService?(self, didUpdate: progress, with: location, rawLocation: rawLocation) + delegate?.navigationService(self, didUpdate: progress, with: location, rawLocation: rawLocation) } public func router(_ router: Router, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { - delegate?.navigationService?(self, didPassVisualInstructionPoint: instruction, routeProgress: routeProgress) + delegate?.navigationService(self, didPassVisualInstructionPoint: instruction, routeProgress: routeProgress) } public func router(_ router: Router, didPassSpokenInstructionPoint instruction: SpokenInstruction, routeProgress: RouteProgress) { - delegate?.navigationService?(self, didPassSpokenInstructionPoint: instruction, routeProgress: routeProgress) + delegate?.navigationService(self, didPassSpokenInstructionPoint: instruction, routeProgress: routeProgress) } //MARK: Questions public func router(_ router: Router, shouldRerouteFrom location: CLLocation) -> Bool { - return delegate?.navigationService?(self, shouldRerouteFrom: location) ?? Default.shouldRerouteFromLocation + return delegate?.navigationService(self, shouldRerouteFrom: location) ?? Default.shouldRerouteFromLocation } public func router(_ router: Router, shouldDiscard location: CLLocation) -> Bool { - return delegate?.navigationService?(self, shouldDiscard: location) ?? Default.shouldDiscardLocation + return delegate?.navigationService(self, shouldDiscard: location) ?? Default.shouldDiscardLocation } public func router(_ router: Router, willArriveAt waypoint: Waypoint, after remainingTimeInterval: TimeInterval, distance: CLLocationDistance) { - delegate?.navigationService?(self, willArriveAt: waypoint, after: remainingTimeInterval, distance: distance) + delegate?.navigationService(self, willArriveAt: waypoint, after: remainingTimeInterval, distance: distance) } public func router(_ router: Router, didArriveAt waypoint: Waypoint) -> Bool { - //Notify the events manager that we've arrived at a waypoint eventsManager.arriveAtWaypoint() - let shouldAutomaticallyAdvance = delegate?.navigationService?(self, didArriveAt: waypoint) ?? Default.didArriveAtWaypoint + let shouldAutomaticallyAdvance = delegate?.navigationService(self, didArriveAt: waypoint) ?? Default.didArriveAtWaypoint if !shouldAutomaticallyAdvance { stop() } @@ -486,11 +469,11 @@ extension MapboxNavigationService: RouterDelegate { } public func router(_ router: Router, shouldPreventReroutesWhenArrivingAt waypoint: Waypoint) -> Bool { - return delegate?.navigationService?(self, shouldPreventReroutesWhenArrivingAt: waypoint) ?? Default.shouldPreventReroutesWhenArrivingAtWaypoint + return delegate?.navigationService(self, shouldPreventReroutesWhenArrivingAt: waypoint) ?? Default.shouldPreventReroutesWhenArrivingAtWaypoint } public func routerShouldDisableBatteryMonitoring(_ router: Router) -> Bool { - return delegate?.navigationServiceShouldDisableBatteryMonitoring?(self) ?? Default.shouldDisableBatteryMonitoring + return delegate?.navigationServiceShouldDisableBatteryMonitoring(self) ?? Default.shouldDisableBatteryMonitoring } } @@ -503,7 +486,6 @@ extension MapboxNavigationService { public var desiredAccuracy: CLLocationAccuracy { return self.locationManager.desiredAccuracy } - } //MARK: RouterDataSource @@ -515,7 +497,7 @@ extension MapboxNavigationService { fileprivate extension NavigationEventsManager { func incrementDistanceTraveled(by distance: CLLocationDistance) { - sessionState?.totalDistanceCompleted += distance + sessionState?.totalDistanceCompleted += distance } func arriveAtWaypoint() { @@ -537,7 +519,6 @@ private extension Double { } } - private func checkForUpdates() { #if TARGET_IPHONE_SIMULATOR guard (NSClassFromString("XCTestCase") == nil) else { return } // Short-circuit when running unit tests diff --git a/MapboxCoreNavigation/NavigationServiceDelegate.swift b/MapboxCoreNavigation/NavigationServiceDelegate.swift index 92e63f23e56..2ab1cbe5435 100644 --- a/MapboxCoreNavigation/NavigationServiceDelegate.swift +++ b/MapboxCoreNavigation/NavigationServiceDelegate.swift @@ -1,6 +1,7 @@ import Foundation import CoreLocation import MapboxDirections +import os.log /** A navigation service delegate interacts with one or more `NavigationService` instances (such as `MapboxNavigationService` objects) during turn-by-turn navigation. This protocol is the main way that your application can synchronize its state with the SDK’s location-related functionality. Each of the protocol’s methods is optional. @@ -14,7 +15,7 @@ import MapboxDirections - seealso: NavigationViewControllerDelegate - seealso: RouterDelegate */ -@objc public protocol NavigationServiceDelegate { +public protocol NavigationServiceDelegate: class, UnimplementedLogging { /** Returns whether the navigation service should be allowed to calculate a new route. @@ -23,9 +24,9 @@ import MapboxDirections - parameter service: The navigation service that has detected the need to calculate a new route. - parameter location: The user’s current location. - returns: True to allow the navigation service to calculate a new route; false to keep tracking the current route. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationService:shouldRerouteFromLocation:) - optional func navigationService(_ service: NavigationService, shouldRerouteFrom location: CLLocation) -> Bool + func navigationService(_ service: NavigationService, shouldRerouteFrom location: CLLocation) -> Bool /** Called immediately before the navigation service calculates a new route. @@ -34,9 +35,9 @@ import MapboxDirections - parameter service: The navigation service that will calculate a new route. - parameter location: The user’s current location. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationService:willRerouteFromLocation:) - optional func navigationService(_ service: NavigationService, willRerouteFrom location: CLLocation) + func navigationService(_ service: NavigationService, willRerouteFrom location: CLLocation) /** Called when a location has been identified as unqualified to navigate on. @@ -45,10 +46,10 @@ import MapboxDirections - parameter service: The navigation service that discarded the location. - parameter location: The location that will be discarded. - - return: If `true`, the location is discarded and the `NavigationService` will not consider it. If `false`, the location will not be thrown out. + - returns: If `true`, the location is discarded and the `NavigationService` will not consider it. If `false`, the location will not be thrown out. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationService:shouldDiscardLocation:) - optional func navigationService(_ service: NavigationService, shouldDiscard location: CLLocation) -> Bool + func navigationService(_ service: NavigationService, shouldDiscard location: CLLocation) -> Bool /** Called immediately after the navigation service receives a new route. @@ -57,9 +58,9 @@ import MapboxDirections - parameter service: The navigation service that has calculated a new route. - parameter route: The new route. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationService:didRerouteAlongRoute:at:proactive:) - optional func navigationService(_ service: NavigationService, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) + func navigationService(_ service: NavigationService, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) /** Called when the navigation service fails to receive a new route. @@ -68,9 +69,9 @@ import MapboxDirections - parameter service: The navigation service that has calculated a new route. - parameter error: An error raised during the process of obtaining a new route. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationService:didFailToRerouteWithError:) - optional func navigationService(_ service: NavigationService, didFailToRerouteWith error: Error) + func navigationService(_ service: NavigationService, didFailToRerouteWith error: Error) /** Called when the navigation service updates the route progress model. @@ -79,28 +80,27 @@ import MapboxDirections - parameter progress: the RouteProgress model that was updated. - parameter location: the guaranteed location, possibly snapped, associated with the progress update. - parameter rawLocation: the raw location, from the location manager, associated with the progress update. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationService:didUpdateProgress:withLocation:rawLocation:) - optional func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) + func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) /** Called when the navigation service detects that the user has passed a point at which an instruction should be displayed. - parameter service: The navigation service that passed the instruction point. - parameter instruction: The instruction to be presented. - parameter routeProgress: The route progress object that the navigation service is updating. - */ - @objc(navigationService:didPassVisualInstructionPoint:routeProgress:) - optional func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) - + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. + */ + func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) /** Called when the navigation service detects that the user has passed a point at which an instruction should be spoken. - parameter service: The navigation service that passed the instruction point. - parameter instruction: The instruction to be spoken. - parameter routeProgress: The route progress object that the navigation service is updating. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationService:didPassSpokenInstructionPoint:routeProgress:) - optional func navigationService(_ service: NavigationService, didPassSpokenInstructionPoint instruction: SpokenInstruction, routeProgress: RouteProgress) + func navigationService(_ service: NavigationService, didPassSpokenInstructionPoint instruction: SpokenInstruction, routeProgress: RouteProgress) /** Called as the navigation service approaches a waypoint. @@ -111,10 +111,9 @@ import MapboxDirections - parameter remainingTimeInterval: The estimated number of seconds until arrival. - parameter distance: The current distance from the waypoint, in meters. - important: This method will likely be called several times as you approach a destination. If only one consumption of this method is desired, then usage of an internal flag is recommended. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - - @objc(navigationService:willArriveAtWaypoint:after:distance:) - optional func navigationService(_ service: NavigationService, willArriveAt waypoint: Waypoint, after remainingTimeInterval:TimeInterval, distance: CLLocationDistance) + func navigationService(_ service: NavigationService, willArriveAt waypoint: Waypoint, after remainingTimeInterval:TimeInterval, distance: CLLocationDistance) /** Called when the navigation service arrives at a waypoint. @@ -125,9 +124,9 @@ import MapboxDirections - parameter service: The navigation service that has arrived at a waypoint. - parameter waypoint: The waypoint that the controller has arrived at. - returns: True to advance to the next leg, if any, or false to remain on the completed leg. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationService:didArriveAtWaypoint:) - optional func navigationService(_ service: NavigationService, didArriveAt waypoint: Waypoint) -> Bool + func navigationService(_ service: NavigationService, didArriveAt waypoint: Waypoint) -> Bool /** Called when the navigation service arrives at a waypoint. @@ -137,10 +136,9 @@ import MapboxDirections - parameter service: The navigation service that has arrived at a waypoint. - parameter waypoint: The waypoint that the controller has arrived at. - returns: True to prevent the navigation service from checking if the user should be rerouted. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationService:shouldPreventReroutesWhenArrivingAtWaypoint:) - optional func navigationService(_ service: NavigationService, shouldPreventReroutesWhenArrivingAt waypoint: Waypoint) -> Bool - + func navigationService(_ service: NavigationService, shouldPreventReroutesWhenArrivingAt waypoint: Waypoint) -> Bool /** Called when the navigation service will disable battery monitoring. @@ -149,10 +147,9 @@ import MapboxDirections - parameter service: The navigation service that will change the state of battery monitoring. - returns: A bool indicating whether to disable battery monitoring when the RouteController is deinited. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationServiceShouldDisableBatteryMonitoring:) - optional func navigationServiceShouldDisableBatteryMonitoring(_ service: NavigationService) -> Bool - + func navigationServiceShouldDisableBatteryMonitoring(_ service: NavigationService) -> Bool /** Called when the navigation service is about to begin location simulation. @@ -162,8 +159,9 @@ import MapboxDirections - parameter service: The navigation service that will simulate the routes' progress. - parameter progress: the current RouteProgress model. - parameter reason: The reason the simulation will be initiated. Either manual or poorGPS. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationService(_ service: NavigationService, willBeginSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) + func navigationService(_ service: NavigationService, willBeginSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) /** Called after the navigation service begins location simulation. @@ -173,8 +171,9 @@ import MapboxDirections - parameter service: The navigation service that is simulating the routes' progress. - parameter progress: the current RouteProgress model. - parameter reason: The reason the simulation has been initiated. Either manual or poorGPS. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationService(_ service: NavigationService, didBeginSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) + func navigationService(_ service: NavigationService, didBeginSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) /** Called when the navigation service is about to end location simulation. @@ -184,8 +183,9 @@ import MapboxDirections - parameter service: The navigation service that is simulating the routes' progress. - parameter progress: the current RouteProgress model. - parameter reason: The reason the simulation was initiated. Either manual or poorGPS. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationService(_ service: NavigationService, willEndSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) + func navigationService(_ service: NavigationService, willEndSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) /** Called after the navigation service ends location simulation. @@ -195,6 +195,77 @@ import MapboxDirections - parameter service: The navigation service that was simulating the routes' progress. - parameter progress: the current RouteProgress model. - parameter reason: The reason the simulation was initiated. Either manual or poorGPS. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationService(_ service: NavigationService, didEndSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) + func navigationService(_ service: NavigationService, didEndSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) +} + +public extension NavigationServiceDelegate { + func navigationService(_ service: NavigationService, shouldRerouteFrom location: CLLocation) -> Bool { + logUnimplemented( protocolType: NavigationServiceDelegate.self, level: .debug) + return MapboxNavigationService.Default.shouldRerouteFromLocation + } + + func navigationService(_ service: NavigationService, willRerouteFrom location: CLLocation) { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + } + + func navigationService(_ service: NavigationService, shouldDiscard location: CLLocation) -> Bool { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + return MapboxNavigationService.Default.shouldDiscardLocation + } + + func navigationService(_ service: NavigationService, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .info) + } + + func navigationService(_ service: NavigationService, didFailToRerouteWith error: Error) { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + } + + func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .info) + } + + func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + } + + func navigationService(_ service: NavigationService, didPassSpokenInstructionPoint instruction: SpokenInstruction, routeProgress: RouteProgress) { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + } + + func navigationService(_ service: NavigationService, willArriveAt waypoint: Waypoint, after remainingTimeInterval: TimeInterval, distance: CLLocationDistance) { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + } + + func navigationService(_ service: NavigationService, didArriveAt waypoint: Waypoint) -> Bool { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + return MapboxNavigationService.Default.didArriveAtWaypoint + } + + func navigationService(_ service: NavigationService, shouldPreventReroutesWhenArrivingAt waypoint: Waypoint) -> Bool { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + return MapboxNavigationService.Default.shouldPreventReroutesWhenArrivingAtWaypoint + } + + func navigationServiceShouldDisableBatteryMonitoring(_ service: NavigationService) -> Bool { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + return MapboxNavigationService.Default.shouldDisableBatteryMonitoring + } + func navigationService(_ service: NavigationService, willBeginSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + } + + func navigationService(_ service: NavigationService, didBeginSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + } + + func navigationService(_ service: NavigationService, willEndSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + } + + func navigationService(_ service: NavigationService, didEndSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) { + logUnimplemented(protocolType: NavigationServiceDelegate.self, level: .debug) + } } diff --git a/MapboxCoreNavigation/NavigationSettings.swift b/MapboxCoreNavigation/NavigationSettings.swift index 6e566f9a42d..c04d71951a1 100644 --- a/MapboxCoreNavigation/NavigationSettings.swift +++ b/MapboxCoreNavigation/NavigationSettings.swift @@ -1,14 +1,5 @@ import Foundation -extension Notification.Name { - /** - Posted when something changes in the shared `NavigationSettings` object. - - The user info dictionary indicates which keys and values changed. - */ - public static let navigationSettingsDidChange = MBNavigationSettingsDidChange -} - /** A wrapper for the `UserDefaults` class for navigation-specific settings. @@ -16,14 +7,12 @@ extension Notification.Name { To specify criteria when calculating routes, use the `NavigationRouteOptions` class. To customize the user experience during a particular turn-by-turn navigation session, use the `NavigationOptions` class when initializing a `NavigationViewController`. */ -@objc(MBNavigationSettings) public class NavigationSettings: NSObject { - /** The volume that the voice controller will use. This volume is relative to the system’s volume where 1.0 is same volume as the system. - */ + */ @objc public dynamic var voiceVolume: Float = 1.0 /** @@ -51,10 +40,9 @@ public class NavigationSettings: NSObject { } } - /* + /** The shared navigation settings object that affects the entire application. */ - @objc(sharedSettings) public static let shared = NavigationSettings() /// Returns a reflection of this class excluding the `properties` variable. diff --git a/MapboxCoreNavigation/OfflineDirections.swift b/MapboxCoreNavigation/OfflineDirections.swift index e8fa475ace1..8dc30a2f8fb 100644 --- a/MapboxCoreNavigation/OfflineDirections.swift +++ b/MapboxCoreNavigation/OfflineDirections.swift @@ -56,10 +56,8 @@ public typealias UnpackCompletionHandler = (_ numberOfTiles: UInt64, _ error: Er Each result produced by the directions object is stored in a `Route` object. Depending on the `RouteOptions` object you provide, each route may include detailed information suitable for turn-by-turn directions, or it may include only high-level information such as the distance, estimated travel time, and name of each leg of the trip. The waypoints that form the request may be conflated with nearby locations, as appropriate; the resulting waypoints are provided to the closure. */ -@objc(MBNavigationDirections) public class NavigationDirections: Directions { - - @objc public override init(accessToken: String? = nil, host: String? = nil) { + public override init(accessToken: String? = nil, host: String? = nil) { super.init(accessToken: accessToken, host: host) } @@ -69,7 +67,7 @@ public class NavigationDirections: Directions { - parameter tilesURL: The location where the tiles has been sideloaded to. - parameter completionHandler: A block that is called when the router is completely configured. */ - @objc public func configureRouter(tilesURL: URL, completionHandler: @escaping NavigationDirectionsCompletionHandler) { + public func configureRouter(tilesURL: URL, completionHandler: @escaping NavigationDirectionsCompletionHandler) { NavigationDirectionsConstants.offlineSerialQueue.sync { let tileCount = self.navigator.configureRouter(forTilesPath: tilesURL.path) DispatchQueue.main.async { @@ -79,7 +77,7 @@ public class NavigationDirections: Directions { } @available(*, deprecated, renamed: "NavigationDirections.configureRouter(tilesURL:completionHandler:)") - @objc public func configureRouter(tilesURL: URL, translationsURL: URL? = nil, completionHandler: @escaping NavigationDirectionsCompletionHandler) { + public func configureRouter(tilesURL: URL, translationsURL: URL? = nil, completionHandler: @escaping NavigationDirectionsCompletionHandler) { configureRouter(tilesURL: tilesURL, completionHandler: completionHandler) } @@ -92,11 +90,8 @@ public class NavigationDirections: Directions { - parameter progressHandler: Unpacking reports progress every 500ms. - parameter completionHandler: Called when unpacking completed. */ - @objc(unpackTilePackAtURL:outputDirectoryURL:progressHandler:completionHandler:) public class func unpackTilePack(at filePathURL: URL, outputDirectoryURL: URL, progressHandler: UnpackProgressHandler?, completionHandler: UnpackCompletionHandler?) { - NavigationDirectionsConstants.offlineSerialQueue.sync { - let totalPackedBytes = filePathURL.fileSize! // Report 0% progress @@ -138,9 +133,7 @@ public class NavigationDirections: Directions { - parameter offline: Determines whether to calculate the route offline or online. - parameter completionHandler: The closure (block) to call with the resulting routes. This closure is executed on the application’s main thread. */ - @objc(calculateDirectionsWithOptions:offline:completionHandler:) public func calculate(_ options: RouteOptions, offline: Bool = true, completionHandler: @escaping Directions.RouteCompletionHandler) { - guard offline == true else { super.calculate(options, completionHandler: completionHandler) return @@ -149,7 +142,6 @@ public class NavigationDirections: Directions { let url = self.url(forCalculating: options) NavigationDirectionsConstants.offlineSerialQueue.async { [weak self] in - guard let result = self?.navigator.getRouteForDirectionsUri(url.absoluteString) else { let message = NSLocalizedString("OFFLINE_NO_RESULT", bundle: .mapboxCoreNavigation, value: "Unable to calculate the requested route while offline.", comment: "Error description when an offline route request returns no result") let error = OfflineRoutingError.unexpectedRouteResult(message) @@ -170,13 +162,11 @@ public class NavigationDirections: Directions { return completionHandler(nil, nil, error as NSError) } } else { - DispatchQueue.main.async { let response = options.response(from: json) return completionHandler(response.0, response.1, nil) } } - } catch { DispatchQueue.main.async { return completionHandler(nil, nil, error as NSError) @@ -187,7 +177,6 @@ public class NavigationDirections: Directions { var _navigator: MBNavigator! var navigator: MBNavigator { - assert(currentQueueName() == NavigationDirectionsConstants.offlineSerialQueueLabel, "The offline navigator must be accessed from the dedicated serial queue") @@ -205,7 +194,6 @@ fileprivate func currentQueueName() -> String? { } extension URL { - fileprivate var fileSize: UInt64? { do { let attributes = try FileManager.default.attributesOfItem(atPath: self.path) diff --git a/MapboxCoreNavigation/ReplayLocationManager.swift b/MapboxCoreNavigation/ReplayLocationManager.swift index 40b8e83f3cc..51de41ab0ba 100644 --- a/MapboxCoreNavigation/ReplayLocationManager.swift +++ b/MapboxCoreNavigation/ReplayLocationManager.swift @@ -6,13 +6,11 @@ import CoreLocation recorded with the single exception of the location’s timestamp which will be adjusted by interval between locations. */ -@objc(MBReplayLocationManager) open class ReplayLocationManager: NavigationLocationManager { - /** `speedMultiplier` adjusts the speed of the replay. */ - @objc public var speedMultiplier: TimeInterval = 1 + public var speedMultiplier: TimeInterval = 1 var currentIndex: Int = 0 @@ -21,14 +19,14 @@ open class ReplayLocationManager: NavigationLocationManager { /** `locations` to be replayed. */ - @objc public var locations: [CLLocation]! { + public var locations: [CLLocation]! { didSet { currentIndex = 0 } } private var synthesizedLocation: CLLocation? - @objc override open var location: CLLocation? { + override open var location: CLLocation? { get { return synthesizedLocation } diff --git a/MapboxCoreNavigation/RouteController.swift b/MapboxCoreNavigation/RouteController.swift index af5d84f0b31..38eccadd06e 100644 --- a/MapboxCoreNavigation/RouteController.swift +++ b/MapboxCoreNavigation/RouteController.swift @@ -6,16 +6,13 @@ import MapboxDirections import Polyline import Turf - /** A `RouteController` tracks the user’s progress along a route, posting notifications as the user reaches significant points along the route. On every location update, the route controller evaluates the user’s location, determining whether the user remains on the route. If not, the route controller calculates a new route. `RouteController` is responsible for the core navigation logic whereas `NavigationViewController` is responsible for displaying a default drop-in navigation UI. */ -@objc(MBRouteController) open class RouteController: NSObject { - public enum DefaultBehavior { public static let shouldRerouteFromLocation: Bool = true public static let shouldDiscardLocation: Bool = true @@ -62,7 +59,7 @@ open class RouteController: NSObject { var isFirstLocation: Bool = true - @objc public var config: MBNavigatorConfig? { + public var config: MBNavigatorConfig? { get { return navigator.getConfig() } @@ -74,13 +71,13 @@ open class RouteController: NSObject { /** Details about the user’s progress along the current route, leg, and step. */ - @objc public var routeProgress: RouteProgress { + public var routeProgress: RouteProgress { get { return _routeProgress } set { if let location = self.location { - delegate?.router?(self, willRerouteFrom: location) + delegate?.router(self, willRerouteFrom: location) } _routeProgress = newValue announce(reroute: routeProgress.route, at: rawLocation, proactive: didFindFasterRoute) @@ -113,30 +110,30 @@ open class RouteController: NSObject { } } - @objc public var reroutesProactively: Bool = true + public var reroutesProactively: Bool = true var lastProactiveRerouteDate: Date? /** The route controller’s delegate. */ - @objc public weak var delegate: RouterDelegate? + public weak var delegate: RouterDelegate? /** The route controller’s associated location manager. */ - @objc public unowned var dataSource: RouterDataSource + public unowned var dataSource: RouterDataSource /** The Directions object used to create the route. */ - @objc public var directions: Directions + public var directions: Directions /** The idealized user location. Snapped to the route line, if applicable, otherwise raw. - seeAlso: snappedLocation, rawLocation */ - @objc public var location: CLLocation? { + public var location: CLLocation? { return snappedLocation ?? rawLocation } @@ -181,10 +178,9 @@ open class RouteController: NSObject { } public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - guard let location = locations.last else { return } - guard delegate?.router?(self, shouldDiscard: location) ?? DefaultBehavior.shouldDiscardLocation else { + guard delegate?.router(self, shouldDiscard: location) ?? DefaultBehavior.shouldDiscardLocation else { return } @@ -197,8 +193,8 @@ open class RouteController: NSObject { // Notify observers if the step’s remaining distance has changed. update(progress: routeProgress, with: CLLocation(status.location), rawLocation: location) - let willReroute = !userIsOnRoute(location) && delegate?.router?(self, shouldRerouteFrom: location) - ?? DefaultBehavior.shouldRerouteFromLocation + let willReroute = !userIsOnRoute(location) && delegate?.router(self, shouldRerouteFrom: location) + ?? DefaultBehavior.shouldRerouteFromLocation updateIndexes(status: status, progress: routeProgress) updateRouteLegProgress(status: status) @@ -261,27 +257,22 @@ open class RouteController: NSObject { } func updateRouteLegProgress(status: MBNavigationStatus) { - let legProgress = routeProgress.currentLegProgress let currentDestination = routeProgress.currentLeg.destination guard let remainingVoiceInstructions = legProgress.currentStepProgress.remainingSpokenInstructions else { return } // We are at least at the "You will arrive" instruction if legProgress.remainingSteps.count <= 2 && remainingVoiceInstructions.count <= 2 { - let willArrive = status.routeState == .tracking let didArrive = status.routeState == .complete && currentDestination != previousArrivalWaypoint if willArrive { - - delegate?.router?(self, willArriveAt: currentDestination, after: legProgress.durationRemaining, distance: legProgress.distanceRemaining) - + delegate?.router(self, willArriveAt: currentDestination, after: legProgress.durationRemaining, distance: legProgress.distanceRemaining) } else if didArrive { - previousArrivalWaypoint = currentDestination legProgress.userHasArrivedAtWaypoint = true - let advancesToNextLeg = delegate?.router?(self, didArriveAt: currentDestination) ?? DefaultBehavior.didArriveAtWaypoint + let advancesToNextLeg = delegate?.router(self, didArriveAt: currentDestination) ?? DefaultBehavior.didArriveAtWaypoint guard !routeProgress.isFinalLeg && advancesToNextLeg else { return } @@ -292,7 +283,6 @@ open class RouteController: NSObject { } private func update(progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { - let stepProgress = progress.currentLegProgress.currentStepProgress let step = stepProgress.step @@ -304,7 +294,7 @@ open class RouteController: NSObject { stepProgress.distanceTraveled = distanceTraveled //Fire the delegate method - delegate?.router?(self, didUpdate: progress, with: location, rawLocation: rawLocation) + delegate?.router(self, didUpdate: progress, with: location, rawLocation: rawLocation) //Fire the notification (for now) NotificationCenter.default.post(name: .routeControllerProgressDidChange, object: self, userInfo: [ @@ -316,8 +306,7 @@ open class RouteController: NSObject { } private func announcePassage(of spokenInstructionPoint: SpokenInstruction, routeProgress: RouteProgress) { - - delegate?.router?(self, didPassSpokenInstructionPoint: spokenInstructionPoint, routeProgress: routeProgress) + delegate?.router(self, didPassSpokenInstructionPoint: spokenInstructionPoint, routeProgress: routeProgress) let info: [RouteControllerNotificationUserInfoKey: Any] = [ .routeProgressKey: routeProgress, @@ -328,8 +317,7 @@ open class RouteController: NSObject { } private func announcePassage(of visualInstructionPoint: VisualInstructionBanner, routeProgress: RouteProgress) { - - delegate?.router?(self, didPassVisualInstructionPoint: visualInstructionPoint, routeProgress: routeProgress) + delegate?.router(self, didPassVisualInstructionPoint: visualInstructionPoint, routeProgress: routeProgress) let info: [RouteControllerNotificationUserInfoKey: Any] = [ .routeProgressKey: routeProgress, @@ -360,18 +348,16 @@ open class RouteController: NSObject { navigator.toggleHistoryFor(onOff: false) } - public func locationHistory() -> String { + public func locationHistory() -> String? { return navigator.getHistory() } } extension RouteController: Router { - public func userIsOnRoute(_ location: CLLocation) -> Bool { - // If the user has arrived, do not continue monitor reroutes, step progress, etc if routeProgress.currentLegProgress.userHasArrivedAtWaypoint && - (delegate?.router?(self, shouldPreventReroutesWhenArrivingAt: routeProgress.currentLeg.destination) ?? + (delegate?.router(self, shouldPreventReroutesWhenArrivingAt: routeProgress.currentLeg.destination) ?? DefaultBehavior.shouldPreventReroutesWhenArrivingAtWaypoint) { return true } @@ -388,7 +374,7 @@ extension RouteController: Router { } } - delegate?.router?(self, willRerouteFrom: location) + delegate?.router(self, willRerouteFrom: location) NotificationCenter.default.post(name: .routeControllerWillReroute, object: self, userInfo: [ RouteControllerNotificationUserInfoKey.locationKey: location ]) @@ -407,7 +393,7 @@ extension RouteController: Router { } if let error = error { - strongSelf.delegate?.router?(strongSelf, didFailToRerouteWith: error) + strongSelf.delegate?.router(strongSelf, didFailToRerouteWith: error) NotificationCenter.default.post(name: .routeControllerDidFailToReroute, object: self, userInfo: [ RouteControllerNotificationUserInfoKey.routingErrorKey: error ]) diff --git a/MapboxCoreNavigation/RouteProgress.swift b/MapboxCoreNavigation/RouteProgress.swift index 1da2d600581..ab2a4a70920 100644 --- a/MapboxCoreNavigation/RouteProgress.swift +++ b/MapboxCoreNavigation/RouteProgress.swift @@ -5,15 +5,13 @@ import Turf /** `RouteProgress` stores the user’s progress along a route. */ -@objc(MBRouteProgress) open class RouteProgress: NSObject { - private static let reroutingAccuracy: CLLocationAccuracy = 90 /** Returns the current `Route`. */ - @objc public let route: Route + public let route: Route /** Index representing current `RouteLeg`. @@ -29,27 +27,27 @@ open class RouteProgress: NSObject { /** If waypoints are provided in the `Route`, this will contain which leg the user is on. */ - @objc public var currentLeg: RouteLeg { + public var currentLeg: RouteLeg { return route.legs[legIndex] } /** Returns the remaining legs left on the current route */ - @objc public var remainingLegs: [RouteLeg] { + public var remainingLegs: [RouteLeg] { return Array(route.legs.suffix(from: legIndex + 1)) } /** Returns the remaining steps left on the current route */ - @objc public var remainingSteps: [RouteStep] { + public var remainingSteps: [RouteStep] { return currentLegProgress.remainingSteps + remainingLegs.flatMap { $0.steps } } /** - Returns true if `currentLeg` is the last leg. - */ + Returns true if `currentLeg` is the last leg. + */ public var isFinalLeg: Bool { guard let lastLeg = route.legs.last else { return false } return currentLeg == lastLeg @@ -58,28 +56,28 @@ open class RouteProgress: NSObject { /** Total distance traveled by user along all legs. */ - @objc public var distanceTraveled: CLLocationDistance { + public var distanceTraveled: CLLocationDistance { return route.legs.prefix(upTo: legIndex).map { $0.distance }.reduce(0, +) + currentLegProgress.distanceTraveled } /** Total seconds remaining on all legs. */ - @objc public var durationRemaining: TimeInterval { + public var durationRemaining: TimeInterval { return route.legs.suffix(from: legIndex + 1).map { $0.expectedTravelTime }.reduce(0, +) + currentLegProgress.durationRemaining } /** Number between 0 and 1 representing how far along the `Route` the user has traveled. */ - @objc public var fractionTraveled: Double { + public var fractionTraveled: Double { return distanceTraveled / route.distance } /** Total distance remaining in meters along route. */ - @objc public var distanceRemaining: CLLocationDistance { + public var distanceRemaining: CLLocationDistance { return route.distance - distanceTraveled } @@ -88,7 +86,7 @@ open class RouteProgress: NSObject { This property does not include waypoints whose `Waypoint.separatesLegs` property is set to `false`. */ - @objc public var remainingWaypoints: [Waypoint] { + public var remainingWaypoints: [Waypoint] { return route.legs.suffix(from: legIndex).map { $0.destination } } @@ -104,9 +102,9 @@ open class RouteProgress: NSObject { /** Returns the progress along the current `RouteLeg`. */ - @objc public var currentLegProgress: RouteLegProgress + public var currentLegProgress: RouteLegProgress - @objc public var priorLeg: RouteLeg? { + public var priorLeg: RouteLeg? { return legIndex > 0 ? route.legs[legIndex - 1] : nil } @@ -115,8 +113,7 @@ open class RouteProgress: NSObject { The prior step may be part of a different RouteLeg than the current step. If the current step is the first step along the route, this property is set to nil. */ - - @objc public var priorStep: RouteStep? { + public var priorStep: RouteStep? { return currentLegProgress.priorStep ?? priorLeg?.steps.last } @@ -124,9 +121,8 @@ open class RouteProgress: NSObject { The leg following the current leg along this route. If this leg is the last leg of the route, this property is set to nil. - */ - - @objc public var upcomingLeg: RouteLeg? { + */ + public var upcomingLeg: RouteLeg? { return legIndex + 1 < route.legs.endIndex ? route.legs[legIndex + 1] : nil } @@ -135,7 +131,6 @@ open class RouteProgress: NSObject { The upcoming step may be part of a different RouteLeg than the current step. If it is the last step along the route, this property is set to nil. */ - public var upcomingStep: RouteStep? { return currentLegProgress.upcomingStep ?? upcomingLeg?.steps.first } @@ -145,8 +140,7 @@ open class RouteProgress: NSObject { - important: The adjacent steps may be part of legs other than the current leg. */ - - @objc public var nearbyCoordinates: [CLLocationCoordinate2D] { + public var nearbyCoordinates: [CLLocationCoordinate2D] { let priorCoordinates = priorStep?.coordinates?.dropLast() ?? [] let currentCoordinates = currentLegProgress.currentStep.coordinates ?? [] let upcomingCoordinates = upcomingStep?.coordinates?.dropFirst() ?? [] @@ -174,7 +168,7 @@ open class RouteProgress: NSObject { - parameter route: The route to follow. - parameter legIndex: Zero-based index indicating the current leg the user is on. */ - @objc public init(route: Route, legIndex: Int = 0, spokenInstructionIndex: Int = 0) { + public init(route: Route, legIndex: Int = 0, spokenInstructionIndex: Int = 0) { self.route = route self.legIndex = legIndex self.currentLegProgress = RouteLegProgress(leg: route.legs[legIndex], stepIndex: 0, spokenInstructionIndex: spokenInstructionIndex) @@ -189,7 +183,6 @@ open class RouteProgress: NSObject { var congestionTravelTimesSegmentsByLeg: [[TimedCongestionLevel]] = [] if let segmentCongestionLevels = leg.segmentCongestionLevels, let expectedSegmentTravelTimes = leg.expectedSegmentTravelTimes { - for step in leg.steps { guard let coordinates = step.coordinates else { continue } let stepCoordinateCount = step.maneuverType == .arrive ? Int(step.coordinateCount) : coordinates.dropLast().count @@ -272,12 +265,11 @@ open class RouteProgress: NSObject { /** `RouteLegProgress` stores the user’s progress along a route leg. */ -@objc(MBRouteLegProgress) open class RouteLegProgress: NSObject { /** Returns the current `RouteLeg`. */ - @objc public let leg: RouteLeg + public let leg: RouteLeg /** Index representing the current step. @@ -292,44 +284,44 @@ open class RouteLegProgress: NSObject { /** The remaining steps for user to complete. */ - @objc public var remainingSteps: [RouteStep] { + public var remainingSteps: [RouteStep] { return Array(leg.steps.suffix(from: stepIndex + 1)) } /** Total distance traveled in meters along current leg. */ - @objc public var distanceTraveled: CLLocationDistance { + public var distanceTraveled: CLLocationDistance { return leg.steps.prefix(upTo: stepIndex).map { $0.distance }.reduce(0, +) + currentStepProgress.distanceTraveled } /** Duration remaining in seconds on current leg. */ - @objc public var durationRemaining: TimeInterval { + public var durationRemaining: TimeInterval { return remainingSteps.map { $0.expectedTravelTime }.reduce(0, +) + currentStepProgress.durationRemaining } /** Distance remaining on the current leg. */ - @objc public var distanceRemaining: CLLocationDistance { + public var distanceRemaining: CLLocationDistance { return remainingSteps.map { $0.distance }.reduce(0, +) + currentStepProgress.distanceRemaining } /** Number between 0 and 1 representing how far along the current leg the user has traveled. */ - @objc public var fractionTraveled: Double { + public var fractionTraveled: Double { return distanceTraveled / leg.distance } - @objc public var userHasArrivedAtWaypoint = false + public var userHasArrivedAtWaypoint = false /** Returns the `RouteStep` before a given step. Returns `nil` if there is no step prior. */ - @objc public func stepBefore(_ step: RouteStep) -> RouteStep? { + public func stepBefore(_ step: RouteStep) -> RouteStep? { guard let index = leg.steps.firstIndex(of: step) else { return nil } @@ -342,7 +334,7 @@ open class RouteLegProgress: NSObject { /** Returns the `RouteStep` after a given step. Returns `nil` if there is not a step after. */ - @objc public func stepAfter(_ step: RouteStep) -> RouteStep? { + public func stepAfter(_ step: RouteStep) -> RouteStep? { guard let index = leg.steps.firstIndex(of: step) else { return nil } @@ -357,7 +349,7 @@ open class RouteLegProgress: NSObject { If there is no `priorStep`, nil is returned. */ - @objc public var priorStep: RouteStep? { + public var priorStep: RouteStep? { guard stepIndex - 1 >= 0 else { return nil } @@ -367,7 +359,7 @@ open class RouteLegProgress: NSObject { /** Returns the current `RouteStep` for the leg the user is on. */ - @objc public var currentStep: RouteStep { + public var currentStep: RouteStep { return leg.steps[stepIndex] } @@ -377,11 +369,11 @@ open class RouteLegProgress: NSObject { If there is no `upcomingStep`, nil is returned. */ @available(*, deprecated, renamed: "upcomingStep") - @objc public var upComingStep: RouteStep? { + public var upComingStep: RouteStep? { return upcomingStep } - @objc public var upcomingStep: RouteStep? { + public var upcomingStep: RouteStep? { guard stepIndex + 1 < leg.steps.endIndex else { return nil } @@ -393,7 +385,7 @@ open class RouteLegProgress: NSObject { If there is no `followOnStep`, nil is returned. */ - @objc public var followOnStep: RouteStep? { + public var followOnStep: RouteStep? { guard stepIndex + 2 < leg.steps.endIndex else { return nil } @@ -403,14 +395,14 @@ open class RouteLegProgress: NSObject { /** Return bool whether step provided is the current `RouteStep` the user is on. */ - @objc public func isCurrentStep(_ step: RouteStep) -> Bool { + public func isCurrentStep(_ step: RouteStep) -> Bool { return step == currentStep } /** Returns the progress along the current `RouteStep`. */ - @objc public var currentStepProgress: RouteStepProgress + public var currentStepProgress: RouteStepProgress /** Intializes a new `RouteLegProgress`. @@ -418,7 +410,7 @@ open class RouteLegProgress: NSObject { - parameter leg: Leg on a `Route`. - parameter stepIndex: Current step the user is on. */ - @objc public init(leg: RouteLeg, stepIndex: Int = 0, spokenInstructionIndex: Int = 0) { + public init(leg: RouteLeg, stepIndex: Int = 0, spokenInstructionIndex: Int = 0) { self.leg = leg self.stepIndex = stepIndex currentStepProgress = RouteStepProgress(step: leg.steps[stepIndex], spokenInstructionIndex: spokenInstructionIndex) @@ -427,9 +419,8 @@ open class RouteLegProgress: NSObject { /** Returns an array of `CLLocationCoordinate2D` of the prior, current and upcoming step geometry. */ - @available(*, deprecated, message: "Use RouteProgress.nearbyCoordinates") - @objc public var nearbyCoordinates: [CLLocationCoordinate2D] { + public var nearbyCoordinates: [CLLocationCoordinate2D] { let priorCoords = priorStep?.coordinates ?? [] let upcomingCoords = upcomingStep?.coordinates ?? [] let currentCoords = currentStep.coordinates ?? [] @@ -494,35 +485,33 @@ open class RouteLegProgress: NSObject { /** `RouteStepProgress` stores the user’s progress along a route step. */ -@objc(MBRouteStepProgress) open class RouteStepProgress: NSObject { - /** Returns the current `RouteStep`. */ - @objc public let step: RouteStep + public let step: RouteStep /** Returns distance user has traveled along current step. */ - @objc public var distanceTraveled: CLLocationDistance = 0 + public var distanceTraveled: CLLocationDistance = 0 /** Returns distance from user to end of step. */ - @objc public var userDistanceToManeuverLocation: CLLocationDistance = Double.infinity + public var userDistanceToManeuverLocation: CLLocationDistance /** Total distance in meters remaining on current step. */ - @objc public var distanceRemaining: CLLocationDistance { + public var distanceRemaining: CLLocationDistance { return step.distance - distanceTraveled } /** Number between 0 and 1 representing fraction of current step traveled. */ - @objc public var fractionTraveled: Double { + public var fractionTraveled: Double { guard step.distance > 0 else { return 1 } return distanceTraveled / step.distance } @@ -530,7 +519,7 @@ open class RouteStepProgress: NSObject { /** Number of seconds remaining on current step. */ - @objc public var durationRemaining: TimeInterval { + public var durationRemaining: TimeInterval { return (1 - fractionTraveled) * step.expectedTravelTime } @@ -539,8 +528,9 @@ open class RouteStepProgress: NSObject { - parameter step: Step on a `RouteLeg`. */ - @objc public init(step: RouteStep, spokenInstructionIndex: Int = 0) { + public init(step: RouteStep, spokenInstructionIndex: Int = 0) { self.step = step + self.userDistanceToManeuverLocation = step.distance self.intersectionIndex = 0 self.spokenInstructionIndex = spokenInstructionIndex } @@ -550,14 +540,14 @@ open class RouteStepProgress: NSObject { The upcoming `RouteStep` first `Intersection` is added because it is omitted from the current step. */ - @objc public var intersectionsIncludingUpcomingManeuverIntersection: [Intersection]? + public var intersectionsIncludingUpcomingManeuverIntersection: [Intersection]? /** The next intersection the user will travel through. The step must contain `intersectionsIncludingUpcomingManeuverIntersection` otherwise this property will be `nil`. */ - @objc public var upcomingIntersection: Intersection? { + public var upcomingIntersection: Intersection? { guard let intersections = intersectionsIncludingUpcomingManeuverIntersection, intersections.startIndex..? + public var intersectionDistances: Array? /** The distance in meters the user is to the next intersection they will pass through. @@ -596,12 +586,12 @@ open class RouteStepProgress: NSObject { /** Index into `step.instructionsDisplayedAlongStep` representing the current visual instruction for the step. */ - @objc public var visualInstructionIndex: Int = 0 + public var visualInstructionIndex: Int = 0 /** An `Array` of remaining `VisualInstruction` for a step. */ - @objc public var remainingVisualInstructions: [VisualInstructionBanner]? { + public var remainingVisualInstructions: [VisualInstructionBanner]? { guard let visualInstructions = step.instructionsDisplayedAlongStep else { return nil } return Array(visualInstructions.suffix(from: visualInstructionIndex)) } @@ -609,12 +599,12 @@ open class RouteStepProgress: NSObject { /** Index into `step.instructionsSpokenAlongStep` representing the current spoken instruction. */ - @objc public var spokenInstructionIndex: Int = 0 + public var spokenInstructionIndex: Int = 0 /** An `Array` of remaining `SpokenInstruction` for a step. */ - @objc public var remainingSpokenInstructions: [SpokenInstruction]? { + public var remainingSpokenInstructions: [SpokenInstruction]? { guard let instructions = step.instructionsSpokenAlongStep else { return nil } return Array(instructions.suffix(from: spokenInstructionIndex)) } @@ -622,7 +612,7 @@ open class RouteStepProgress: NSObject { /** Current spoken instruction for the user's progress along a step. */ - @objc public var currentSpokenInstruction: SpokenInstruction? { + public var currentSpokenInstruction: SpokenInstruction? { guard let instructionsSpokenAlongStep = step.instructionsSpokenAlongStep else { return nil } guard spokenInstructionIndex < instructionsSpokenAlongStep.count else { return nil } return instructionsSpokenAlongStep[spokenInstructionIndex] @@ -631,18 +621,17 @@ open class RouteStepProgress: NSObject { /** Current visual instruction for the user's progress along a step. */ - @objc public var currentVisualInstruction: VisualInstructionBanner? { + public var currentVisualInstruction: VisualInstructionBanner? { guard let instructionsDisplayedAlongStep = step.instructionsDisplayedAlongStep else { return nil } guard visualInstructionIndex < instructionsDisplayedAlongStep.count else { return nil } return instructionsDisplayedAlongStep[visualInstructionIndex] } - @objc public var keyPathsAffectingValueForRemainingVisualInstructions: Set { + public var keyPathsAffectingValueForRemainingVisualInstructions: Set { return ["step.instructionsDisplayedAlongStep", "visualInstructionIndex"] } - @objc public var keyPathsAffectingValueForRemainingSpokenInstructions: Set { + public var keyPathsAffectingValueForRemainingSpokenInstructions: Set { return ["step.instructionsDisplayedAlongStep", "spokenInstructionIndex"] } - } diff --git a/MapboxCoreNavigation/RouteStep.swift b/MapboxCoreNavigation/RouteStep.swift index eefbe8b6bf7..87756081f72 100644 --- a/MapboxCoreNavigation/RouteStep.swift +++ b/MapboxCoreNavigation/RouteStep.swift @@ -2,7 +2,6 @@ import MapboxDirections extension RouteStep { static func ==(left: RouteStep, right: RouteStep) -> Bool { - var finalHeading = false if let leftFinalHeading = left.finalHeading, let rightFinalHeading = right.finalHeading { finalHeading = leftFinalHeading == rightFinalHeading diff --git a/MapboxCoreNavigation/Router.swift b/MapboxCoreNavigation/Router.swift index 9fc3d460c93..f70dd958106 100644 --- a/MapboxCoreNavigation/Router.swift +++ b/MapboxCoreNavigation/Router.swift @@ -5,12 +5,10 @@ import MapboxDirections /** A router data source, also known as a location manager, supplies location data to a `Router` instance. For example, a `MapboxNavigationService` supplies location data to a `RouteController` or `LegacyRouteController`. */ -@objc (MBRouterDataSource) -public protocol RouterDataSource { - +public protocol RouterDataSource: class { /** - The location provider for the `Router.` This class is designated as the object that will provide location updates when requested. - */ + The location provider for the `Router.` This class is designated as the object that will provide location updates when requested. + */ var locationProvider: NavigationLocationManager.Type { get } } @@ -19,17 +17,16 @@ public protocol RouterDataSource { There are two concrete implementations of the `Router` protocol. `RouteController`, the default implementation, is capable of client-side routing and depends on the Mapbox Navigation Native framework. `LegacyRouteController` is an alternative implementation that does not have this dependency but must be used in conjunction with the Mapbox Directions API over a network connection. */ -@objc public protocol Router: class, CLLocationManagerDelegate { - +public protocol Router: class, CLLocationManagerDelegate { /** The route controller’s associated location manager. */ - @objc unowned var dataSource: RouterDataSource { get } + var dataSource: RouterDataSource { get } /** The route controller’s delegate. */ - @objc var delegate: RouterDelegate? { get set } + var delegate: RouterDelegate? { get set } /** Intializes a new `RouteController`. @@ -38,52 +35,49 @@ public protocol RouterDataSource { - parameter directions: The Directions object that created `route`. - parameter source: The data source for the RouteController. */ - @objc(initWithRoute:directions:dataSource:) init(along route: Route, directions: Directions, dataSource source: RouterDataSource) /** Details about the user’s progress along the current route, leg, and step. */ - @objc var routeProgress: RouteProgress { get } + var routeProgress: RouteProgress { get } - @objc var route: Route { get set } + var route: Route { get set } /** Given a users current location, returns a Boolean whether they are currently on the route. If the user is not on the route, they should be rerouted. */ - @objc func userIsOnRoute(_ location: CLLocation) -> Bool - @objc func reroute(from: CLLocation, along: RouteProgress) + func userIsOnRoute(_ location: CLLocation) -> Bool + func reroute(from: CLLocation, along: RouteProgress) /** The idealized user location. Snapped to the route line, if applicable, otherwise raw or nil. */ - @objc var location: CLLocation? { get } + var location: CLLocation? { get } /** The most recently received user location. - note: This is a raw location received from `locationManager`. To obtain an idealized location, use the `location` property. */ - @objc var rawLocation: CLLocation? { get } - + var rawLocation: CLLocation? { get } /** If true, the `RouteController` attempts to calculate a more optimal route for the user on an interval defined by `RouteControllerProactiveReroutingInterval`. */ - @objc var reroutesProactively: Bool { get set } + var reroutesProactively: Bool { get set } /** Advances the leg index. This is a convienence method provided to advance the leg index of any given router without having to worry about the internal data structure of the router. */ - @objc(advanceLegIndexWithLocation:) func advanceLegIndex(location: CLLocation) - @objc optional func enableLocationRecording() - @objc optional func disableLocationRecording() - @objc optional func locationHistory() -> String + func enableLocationRecording() + func disableLocationRecording() + func locationHistory() -> String? } protocol InternalRouter: class { @@ -105,7 +99,6 @@ protocol InternalRouter: class { } extension InternalRouter where Self: Router { - func checkForFasterRoute(from location: CLLocation, routeProgress: RouteProgress) { // Check for faster route given users current location guard reroutesProactively else { return } @@ -151,7 +144,6 @@ extension InternalRouter where Self: Router { if routeIsFaster { self?.setRoute(route: route, proactive: true) - } } } @@ -163,7 +155,6 @@ extension InternalRouter where Self: Router { lastRerouteLocation = location routeTask = directions.calculate(options) {(waypoints, routes, error) in - guard let routes = routes else { return completion(nil, error) } @@ -193,7 +184,7 @@ extension InternalRouter where Self: Router { } userInfo[.isProactiveKey] = proactive NotificationCenter.default.post(name: .routeControllerDidReroute, object: self, userInfo: userInfo) - delegate?.router?(self, didRerouteAlong: routeProgress.route, at: location, proactive: proactive) + delegate?.router(self, didRerouteAlong: routeProgress.route, at: location, proactive: proactive) } } diff --git a/MapboxCoreNavigation/RouterDelegate.swift b/MapboxCoreNavigation/RouterDelegate.swift index 08939b66d50..fa944d3d298 100644 --- a/MapboxCoreNavigation/RouterDelegate.swift +++ b/MapboxCoreNavigation/RouterDelegate.swift @@ -2,8 +2,6 @@ import Foundation import CoreLocation import MapboxDirections - - /** A router delegate interacts with one or more `Router` instances, such as `RouteController` objects, during turn-by-turn navigation. This protocol is similar to `NavigationServiceDelegate`, which is the main way that your application can synchronize its state with the SDK’s location-related functionality. Normally, you should not need to make a class conform to the `RouterDelegate` protocol or call any of its methods directly, but you would need to call this protocol’s methods if you implement a custom `Router` class. @@ -12,9 +10,7 @@ import MapboxDirections - seealso: MapboxNavigationService - seealso: NavigationServiceDelegate */ -@objc(MBRouterDelegate) -public protocol RouterDelegate: class { - +public protocol RouterDelegate: class, UnimplementedLogging { /** Returns whether the router should be allowed to calculate a new route. @@ -24,8 +20,7 @@ public protocol RouterDelegate: class { - parameter location: The user’s current location. - returns: True to allow the router to calculate a new route; false to keep tracking the current route. */ - @objc(router:shouldRerouteFromLocation:) - optional func router(_ router: Router, shouldRerouteFrom location: CLLocation) -> Bool + func router(_ router: Router, shouldRerouteFrom location: CLLocation) -> Bool /** Called immediately before the router calculates a new route. @@ -35,8 +30,7 @@ public protocol RouterDelegate: class { - parameter router: The router that will calculate a new route. - parameter location: The user’s current location. */ - @objc(router:willRerouteFromLocation:) - optional func router(_ router: Router, willRerouteFrom location: CLLocation) + func router(_ router: Router, willRerouteFrom location: CLLocation) /** Called when a location has been identified as unqualified to navigate on. @@ -47,8 +41,7 @@ public protocol RouterDelegate: class { - parameter location: The location that will be discarded. - return: If `true`, the location is discarded and the `Router` will not consider it. If `false`, the location will not be thrown out. */ - @objc(router:shouldDiscardLocation:) - optional func router(_ router: Router, shouldDiscard location: CLLocation) -> Bool + func router(_ router: Router, shouldDiscard location: CLLocation) -> Bool /** Called immediately after the router receives a new route. @@ -58,8 +51,7 @@ public protocol RouterDelegate: class { - parameter router: The router that has calculated a new route. - parameter route: The new route. */ - @objc(router:didRerouteAlongRoute:at:proactive:) - optional func router(_ router: Router, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) + func router(_ router: Router, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) /** Called when the router fails to receive a new route. @@ -69,8 +61,7 @@ public protocol RouterDelegate: class { - parameter router: The router that has calculated a new route. - parameter error: An error raised during the process of obtaining a new route. */ - @objc(router:didFailToRerouteWithError:) - optional func router(_ router: Router, didFailToRerouteWith error: Error) + func router(_ router: Router, didFailToRerouteWith error: Error) /** Called when the router updates the route progress model. @@ -80,8 +71,7 @@ public protocol RouterDelegate: class { - parameter location: the guaranteed location, possibly snapped, associated with the progress update. - parameter rawLocation: the raw location, from the location manager, associated with the progress update. */ - @objc(router:didUpdateProgress:withLocation:rawLocation:) - optional func router(_ router: Router, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) + func router(_ router: Router, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) /** Called when the router detects that the user has passed a point at which an instruction should be displayed. @@ -89,8 +79,7 @@ public protocol RouterDelegate: class { - parameter instruction: The instruction to be presented. - parameter routeProgress: The route progress object that the router is updating. */ - @objc(router:didPassVisualInstructionPoint:routeProgress:) - optional func router(_ router: Router, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) + func router(_ router: Router, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) /** Called when the router detects that the user has passed a point at which an instruction should be spoken. @@ -98,8 +87,7 @@ public protocol RouterDelegate: class { - parameter instruction: The instruction to be spoken. - parameter routeProgress: The route progress object that the router is updating. */ - @objc(router:didPassSpokenInstructionPoint:routeProgress:) - optional func router(_ router: Router, didPassSpokenInstructionPoint instruction: SpokenInstruction, routeProgress: RouteProgress) + func router(_ router: Router, didPassSpokenInstructionPoint instruction: SpokenInstruction, routeProgress: RouteProgress) /** Called as the router approaches a waypoint. @@ -111,8 +99,7 @@ public protocol RouterDelegate: class { - parameter distance: The current distance from the waypoint, in meters. - important: This method will likely be called several times as you approach a destination. If only one consumption of this method is desired, then usage of an internal flag is recommended. */ - @objc(router:willArriveAtWaypoint:in:distance:) - optional func router(_ router: Router, willArriveAt waypoint: Waypoint, after remainingTimeInterval:TimeInterval, distance: CLLocationDistance) + func router(_ router: Router, willArriveAt waypoint: Waypoint, after remainingTimeInterval:TimeInterval, distance: CLLocationDistance) /** Called when the router arrives at a waypoint. @@ -124,8 +111,7 @@ public protocol RouterDelegate: class { - parameter waypoint: The waypoint that the controller has arrived at. - returns: True to advance to the next leg, if any, or false to remain on the completed leg. */ - @objc(router:didArriveAtWaypoint:) - optional func router(_ router: Router, didArriveAt waypoint: Waypoint) -> Bool + func router(_ router: Router, didArriveAt waypoint: Waypoint) -> Bool /** Called when the router arrives at a waypoint. @@ -136,8 +122,7 @@ public protocol RouterDelegate: class { - parameter waypoint: The waypoint that the controller has arrived at. - returns: True to prevent the router from checking if the user should be rerouted. */ - @objc(router:shouldPreventReroutesWhenArrivingAtWaypoint:) - optional func router(_ router: Router, shouldPreventReroutesWhenArrivingAt waypoint: Waypoint) -> Bool + func router(_ router: Router, shouldPreventReroutesWhenArrivingAt waypoint: Waypoint) -> Bool /** Called when the router will disable battery monitoring. @@ -147,7 +132,61 @@ public protocol RouterDelegate: class { - parameter router: The router that will change the state of battery monitoring. - returns: A bool indicating whether to disable battery monitoring when the RouteController is deinited. */ - @objc(routerShouldDisableBatteryMonitoring:) - optional func routerShouldDisableBatteryMonitoring(_ router: Router) -> Bool + func routerShouldDisableBatteryMonitoring(_ router: Router) -> Bool +} + +public extension RouterDelegate { + func router(_ router: Router, shouldRerouteFrom location: CLLocation) -> Bool { + logUnimplemented(protocolType: RouterDelegate.self, level: .debug) + return RouteController.DefaultBehavior.shouldRerouteFromLocation + } + + func router(_ router: Router, willRerouteFrom location: CLLocation) { + logUnimplemented(protocolType: RouterDelegate.self, level: .debug) + } + + func router(_ router: Router, shouldDiscard location: CLLocation) -> Bool { + logUnimplemented(protocolType: RouterDelegate.self, level: .debug) + return RouteController.DefaultBehavior.shouldDiscardLocation + } + + func router(_ router: Router, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) { + logUnimplemented(protocolType: RouterDelegate.self, level: .info) + } + + func router(_ router: Router, didFailToRerouteWith error: Error) { + logUnimplemented(protocolType: RouterDelegate.self, level: .debug) + } + + func router(_ router: Router, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { + logUnimplemented(protocolType: RouterDelegate.self, level: .info) + } + + func router(_ router: Router, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { + logUnimplemented(protocolType: RouterDelegate.self, level: .debug) + } + + func router(_ router: Router, didPassSpokenInstructionPoint instruction: SpokenInstruction, routeProgress: RouteProgress) { + logUnimplemented(protocolType: RouterDelegate.self, level: .debug) + } + + func router(_ router: Router, willArriveAt waypoint: Waypoint, after remainingTimeInterval: TimeInterval, distance: CLLocationDistance) { + logUnimplemented(protocolType: RouterDelegate.self, level: .debug) + } + + func router(_ router: Router, didArriveAt waypoint: Waypoint) -> Bool { + logUnimplemented(protocolType: RouterDelegate.self, level: .info) + return RouteController.DefaultBehavior.didArriveAtWaypoint + } + + func router(_ router: Router, shouldPreventReroutesWhenArrivingAt waypoint: Waypoint) -> Bool { + logUnimplemented(protocolType: RouterDelegate.self, level: .info) + return RouteController.DefaultBehavior.shouldPreventReroutesWhenArrivingAtWaypoint + } + + func routerShouldDisableBatteryMonitoring(_ router: Router) -> Bool { + logUnimplemented(protocolType: RouterDelegate.self, level: .info) + return RouteController.DefaultBehavior.shouldDisableBatteryMonitoring + } } diff --git a/MapboxCoreNavigation/ScreenCapture.swift b/MapboxCoreNavigation/ScreenCapture.swift index 05d240403e7..a8c14eb97c3 100644 --- a/MapboxCoreNavigation/ScreenCapture.swift +++ b/MapboxCoreNavigation/ScreenCapture.swift @@ -3,10 +3,8 @@ import Foundation import UIKit extension UIWindow { - /// Returns a screenshot of the current window public func capture() -> UIImage? { - UIGraphicsBeginImageContextWithOptions(frame.size, isOpaque, UIScreen.main.scale) drawHierarchy(in: bounds, afterScreenUpdates: false) @@ -20,9 +18,7 @@ extension UIWindow { } extension UIImage { - func scaled(toFit newWidth: CGFloat) -> UIImage? { - let factor = newWidth / size.width let newSize = CGSize(width: size.width * factor, height: size.height * factor) diff --git a/MapboxCoreNavigation/SessionState.swift b/MapboxCoreNavigation/SessionState.swift index 1c76a29fd39..60f12b17ce7 100644 --- a/MapboxCoreNavigation/SessionState.swift +++ b/MapboxCoreNavigation/SessionState.swift @@ -7,7 +7,6 @@ import UIKit.UIDevice `SessionState` is a struct which stores information needed to send to the Mapbox telemetry platform. */ struct SessionState { - let identifier = UUID() var departureTimestamp: Date? var arrivalTimestamp: Date? diff --git a/MapboxCoreNavigation/SimulatedLocationManager.swift b/MapboxCoreNavigation/SimulatedLocationManager.swift index b8d81f34409..85234850373 100644 --- a/MapboxCoreNavigation/SimulatedLocationManager.swift +++ b/MapboxCoreNavigation/SimulatedLocationManager.swift @@ -27,8 +27,6 @@ fileprivate class SimulatedLocation: CLLocation { The route will be replaced upon a `RouteControllerDidReroute` notification. */ - -@objc(MBSimulatedLocationManager) open class SimulatedLocationManager: NavigationLocationManager { internal var currentDistance: CLLocationDistance = 0 fileprivate var currentSpeed: CLLocationSpeed = 30 @@ -42,9 +40,9 @@ open class SimulatedLocationManager: NavigationLocationManager { /** Specify the multiplier to use when calculating speed based on the RouteLeg’s `expectedSegmentTravelTimes`. */ - @objc public var speedMultiplier: Double = 1 + public var speedMultiplier: Double = 1 fileprivate var simulatedLocation: CLLocation? - @objc override open var location: CLLocation? { + override open var location: CLLocation? { get { return simulatedLocation } @@ -78,7 +76,7 @@ open class SimulatedLocationManager: NavigationLocationManager { - parameter route: The initial route. - returns: A `SimulatedLocationManager` */ - @objc public init(route: Route) { + public init(route: Route) { super.init() commonInit(for: route, currentDistance: 0, currentSpeed: 30) } @@ -89,14 +87,13 @@ open class SimulatedLocationManager: NavigationLocationManager { - parameter routeProgress: The routeProgress of the current route. - returns: A `SimulatedLocationManager` */ - @objc public init(routeProgress: RouteProgress) { + public init(routeProgress: RouteProgress) { super.init() let currentDistance = calculateCurrentDistance(routeProgress.distanceTraveled) commonInit(for: routeProgress.route, currentDistance: currentDistance, currentSpeed: 0) } private func commonInit(for route: Route, currentDistance: CLLocationDistance, currentSpeed: CLLocationSpeed) { - self.currentSpeed = currentSpeed self.currentDistance = currentDistance self.route = route @@ -147,7 +144,7 @@ open class SimulatedLocationManager: NavigationLocationManager { timer.disarm() } - @objc internal func tick() { + internal func tick() { let polyline = Polyline(routeLine) guard let newCoordinate = polyline.coordinateFromStart(distance: currentDistance) else { @@ -191,7 +188,6 @@ open class SimulatedLocationManager: NavigationLocationManager { } private func calculateCurrentSpeed(distance: CLLocationDistance, coordinatesNearby: [CLLocationCoordinate2D]? = nil, closestLocation: SimulatedLocation) -> CLLocationSpeed { - // More than 10 nearby coordinates indicates that we are in a roundabout or similar complex shape. if let coordinatesNearby = coordinatesNearby, coordinatesNearby.count >= 10 { return minimumSpeed @@ -243,7 +239,6 @@ extension Array where Element : Equatable { } extension Array where Element == CLLocationCoordinate2D { - // Calculate turn penalty for each coordinate. fileprivate func simulatedLocationsWithTurnPenalties() -> [SimulatedLocation] { var locations = [SimulatedLocation]() diff --git a/MapboxCoreNavigation/TunnelAuthority.swift b/MapboxCoreNavigation/TunnelAuthority.swift index 57ff9e8348e..c969184e7cc 100644 --- a/MapboxCoreNavigation/TunnelAuthority.swift +++ b/MapboxCoreNavigation/TunnelAuthority.swift @@ -27,7 +27,6 @@ class TunnelAuthority { if let upcomingIntersection = currentStepProgress.upcomingIntersection, let outletRoadClasses = upcomingIntersection.outletRoadClasses, outletRoadClasses.contains(.tunnel) { - // If we are entering sufficiently fast and we are either within the // tunnel entrance radius or the location is not qualified if location.speed >= Constants.minimumTunnelEntranceSpeed, @@ -47,5 +46,3 @@ class TunnelAuthority { return false } } - - diff --git a/MapboxCoreNavigation/UnimplementedLogging.swift b/MapboxCoreNavigation/UnimplementedLogging.swift new file mode 100644 index 00000000000..c54869c995a --- /dev/null +++ b/MapboxCoreNavigation/UnimplementedLogging.swift @@ -0,0 +1,44 @@ +import Foundation +import os.log +import Dispatch + +/** + Protocols that provide no-op default method implementations can use this protocol to log a message to the console whenever an unimplemented delegate method is called. + + In Swift, optional protocol methods exist only for Objective-C compatibility. However, various protocols in this library follow a classic Objective-C delegate pattern in which the protocol would have a number of optional methods. Instead of the disallowed `optional` keyword, these protocols conform to the `UnimplementedLogging` protocol to inform about unimplemented methods at runtime. These console messages are logged to the subsystem `com.mapbox.com` with a category of the format “delegation.ProtocolName”, where ProtocolName is the name of the protocol that defines the method. + + The default method implementations should be provided as part of the protocol or an extension thereof. If the default implementations reside in an extension, the extension should have the same visibility level as the protocol itself. + */ +public protocol UnimplementedLogging { + func logUnimplemented(protocolType: Any, level: OSLogType, function: String) +} + +public extension UnimplementedLogging { + + func logUnimplemented(protocolType: Any, level: OSLogType, function: String = #function) { + + let protocolDescription = String(describing: protocolType) + let selfDescription = String(describing: type(of: self)) + + let description = (selfDescription, function) + + let alreadyWarned = warned.contains { elem -> Bool in + elem == description + } + + guard !alreadyWarned else { + return + } + + let log = OSLog(subsystem: "com.mapbox.navigation", category: "delegation.\(selfDescription)") + let formatted: StaticString = "Unimplemented Delegate Method in %@: %@.%@. This message will only be logged once." + os_log(formatted, log: log, type: level, selfDescription, protocolDescription, function) + unimplementedTestLogs?.append((selfDescription, function)) + warned.append(description) + } +} + +fileprivate var warned: [(String, String)] = [] + + +var unimplementedTestLogs: [(String, String)]? = nil diff --git a/MapboxCoreNavigationTests/BridgingTests.m b/MapboxCoreNavigationTests/BridgingTests.m deleted file mode 100644 index 7d14e4624cb..00000000000 --- a/MapboxCoreNavigationTests/BridgingTests.m +++ /dev/null @@ -1,77 +0,0 @@ -#import -@import Mapbox; -@import MapboxCoreNavigation; -@import MapboxDirections; -@import TestHelper; -@import MapKit; - -@interface BridgingTests : XCTestCase -@property (nonatomic) MBRouteController *routeController; -@property (nonatomic) CLLocationManager *locationManager; -@end - -@implementation BridgingTests - -- (void)setUp { - [super setUp]; -} - -- (void)testUpdateRoute { - NSDictionary *response = [MBFixture JSONFromFileNamedWithName:@"routeWithInstructions"]; - NSDictionary *routeDict = response[@"routes"][0]; - MBWaypoint *wp1 = [[MBWaypoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(37.795042, -122.413165) coordinateAccuracy:0 name:@"wp1"]; - MBWaypoint *wp2 = [[MBWaypoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(37.7727, -122.433378) coordinateAccuracy:0 name:@"wp2"]; - NSArray *waypoints = @[wp1, wp2]; - MBNavigationRouteOptions *options = [[MBNavigationRouteOptions alloc] initWithWaypoints:waypoints profileIdentifier:MBDirectionsProfileIdentifierAutomobileAvoidingTraffic]; - MBRoute *route = [[MBRoute alloc] initWithJSON:routeDict waypoints:waypoints routeOptions:options]; - route.accessToken = @"garbage"; - XCTAssertNotNil(route); - - - MBDirectionsSpy *directions = [[MBDirectionsSpy alloc] initWithAccessToken:@"garbage" host:nil]; - MBNavigationLocationManager *locationManager = [[MBNavigationLocationManager alloc] init]; - _locationManager = locationManager; - _routeController = [[MBRouteController alloc] initWithRoute:route directions:directions dataSource:locationManager]; - XCTAssertNotNil(_routeController); - - XCTestExpectation *expectation = [self expectationForNotification:MBRouteControllerDidRerouteNotification object:nil handler:^BOOL(NSNotification * _Nonnull notification) { - return YES; - }]; - - _routeController.routeProgress = [[MBRouteProgress alloc] initWithRoute:route legIndex:0 spokenInstructionIndex:0]; - [self waitForExpectations:@[expectation] timeout:5]; -} - -// This test is excluded from the test suite. We are just verifying that offline routing bridges to Obj-C at compile time. -- (void)testOfflineRouting { - (void)[[MBDirections sharedDirections] fetchAvailableOfflineVersionsWithCompletionHandler:^(NSArray * _Nullable versions, NSError * _Nullable error) { - - MBCoordinateBounds *bounds = [[MBCoordinateBounds alloc] initWithNorthWest:CLLocationCoordinate2DMake(0, 0) southEast:CLLocationCoordinate2DMake(1, 1)]; - - (void)[[MBDirections sharedDirections] downloadTilesInCoordinateBounds:bounds version:versions.firstObject completionHandler:^(NSURL * _Nullable url, NSURLResponse * _Nullable response, NSError * _Nullable error) { - NSURL *outputDirectoryURL = [[NSBundle mapboxCoreNavigation] suggestedTileURLWithVersion:versions.firstObject]; - - [MBNavigationDirections unpackTilePackAtURL:url outputDirectoryURL:outputDirectoryURL progressHandler:^(uint64_t totalBytes, uint64_t bytesRemaining) { - // Show unpacking progress - } completionHandler:^(uint64_t numberOfTiles, NSError * _Nullable error) { - // Dismiss UI - }]; - }]; - }]; - - MBNavigationRouteOptions *options = [[MBNavigationRouteOptions alloc] initWithLocations:@[] profileIdentifier:MBDirectionsProfileIdentifierCycling]; - - MBNavigationDirections *directions = nil; - - [directions calculateDirectionsWithOptions:options offline:YES completionHandler:^(NSArray * _Nullable waypoints, NSArray * _Nullable routes, NSError * _Nullable error) { - - }]; - - NSURL *url = [NSURL URLWithString:@""]; - [directions configureRouterWithTilesURL:url completionHandler:^(uint64_t numberOfTiles) { - - }]; -} - -@end - diff --git a/MapboxCoreNavigationTests/CLLocationTests.swift b/MapboxCoreNavigationTests/CLLocationTests.swift index 373065b5a36..a8487396128 100644 --- a/MapboxCoreNavigationTests/CLLocationTests.swift +++ b/MapboxCoreNavigationTests/CLLocationTests.swift @@ -4,7 +4,6 @@ import CoreLocation @testable import MapboxNavigationNative class CLLocationTests: XCTestCase { - func testFixLocationToCLLocation() { let coordinate = CLLocationCoordinate2D(latitude: 0, longitude: 1) let timestamp = Date() diff --git a/MapboxCoreNavigationTests/DistanceFormatterTests.swift b/MapboxCoreNavigationTests/DistanceFormatterTests.swift index 34de68f8b7b..cc86c656257 100644 --- a/MapboxCoreNavigationTests/DistanceFormatterTests.swift +++ b/MapboxCoreNavigationTests/DistanceFormatterTests.swift @@ -3,7 +3,6 @@ import CoreLocation @testable import MapboxCoreNavigation class DistanceFormatterTests: XCTestCase { - var distanceFormatter = DistanceFormatter() override func setUp() { diff --git a/MapboxCoreNavigationTests/LocationTests.swift b/MapboxCoreNavigationTests/LocationTests.swift index 4b2b2e63846..a18ab9e5efa 100644 --- a/MapboxCoreNavigationTests/LocationTests.swift +++ b/MapboxCoreNavigationTests/LocationTests.swift @@ -4,7 +4,6 @@ import CoreLocation @testable import TestHelper class LocationTests: XCTestCase { - var setup: (progress: RouteProgress, firstLocation: CLLocation) { let progress = RouteProgress(route: route) let firstCoord = progress.nearbyCoordinates.first! @@ -59,7 +58,6 @@ class LocationTests: XCTestCase { let initialHeadingOnFirstStep = progress.currentLegProgress.currentStepProgress.step.finalHeading! XCTAssertTrue(calculatedCourse - initialHeadingOnFirstStep < 1, "At the beginning of the route, the final heading of the departure step should be very similar to the caclulated course of the first location update.") } - func testShouldSnap() { let progress = setup.progress @@ -73,5 +71,4 @@ class LocationTests: XCTestCase { XCTAssertFalse(differentCourseAndAccurateLocation.shouldSnap(toRouteWith: initialHeadingOnFirstStep), "Should not snap when user course is different, the location is accurate and moving") } - } diff --git a/MapboxCoreNavigationTests/MapboxCoreNavigationTests.swift b/MapboxCoreNavigationTests/MapboxCoreNavigationTests.swift index 2846cda4320..cbdaec678fb 100644 --- a/MapboxCoreNavigationTests/MapboxCoreNavigationTests.swift +++ b/MapboxCoreNavigationTests/MapboxCoreNavigationTests.swift @@ -13,9 +13,7 @@ let route: Route = { let waitForInterval: TimeInterval = 5 - class MapboxCoreNavigationTests: XCTestCase { - var navigation: MapboxNavigationService! func testNavigationNotificationsInfoDict() { @@ -30,7 +28,6 @@ class MapboxCoreNavigationTests: XCTestCase { verticalAccuracy: -1, course: -1, speed: 10, timestamp: now + $0.offset) } - let spokenTest = expectation(forNotification: .routeControllerDidPassSpokenInstructionPoint, object: navigation.router) { (note) -> Bool in return note.userInfo!.count == 2 } @@ -46,10 +43,7 @@ class MapboxCoreNavigationTests: XCTestCase { navigation.locationManager(navigation.locationManager, didUpdateLocations: [location]) - - wait(for: [spokenTest], timeout: waitForInterval) - } func testDepart() { diff --git a/MapboxCoreNavigationTests/MapboxNavigationServiceSpec.swift b/MapboxCoreNavigationTests/MapboxNavigationServiceSpec.swift index 2e531a1b3c6..02c501ca75d 100644 --- a/MapboxCoreNavigationTests/MapboxNavigationServiceSpec.swift +++ b/MapboxCoreNavigationTests/MapboxNavigationServiceSpec.swift @@ -6,7 +6,6 @@ import TestHelper @testable import MapboxCoreNavigation class MapboxNavigationServiceSpec: QuickSpec { - lazy var initialRoute: Route = { let jsonRoute = (response["routes"] as! [AnyObject]).first as! [String: Any] let waypoint1 = Waypoint(coordinate: CLLocationCoordinate2D(latitude: 37.795042, longitude: -122.413165)) diff --git a/MapboxCoreNavigationTests/NavigationEventsManagerTests.swift b/MapboxCoreNavigationTests/NavigationEventsManagerTests.swift index d0f2715b4e2..2cea6c23dcf 100644 --- a/MapboxCoreNavigationTests/NavigationEventsManagerTests.swift +++ b/MapboxCoreNavigationTests/NavigationEventsManagerTests.swift @@ -3,9 +3,7 @@ import MapboxMobileEvents @testable import TestHelper @testable import MapboxCoreNavigation - class NavigationEventsManagerTests: XCTestCase { - func testMobileEventsManagerIsInitializedImmediately() { let mobileEventsManagerSpy = MMEEventsManagerSpy() let _ = NavigationEventsManager(dataSource: nil, accessToken: "example token", mobileEventsManager: mobileEventsManagerSpy) @@ -14,7 +12,6 @@ class NavigationEventsManagerTests: XCTestCase { } func testDepartRerouteArrive() { - let firstRoute = Fixture.route(from: "DCA-Arboretum") let secondRoute = Fixture.route(from: "PipeFittersUnion-FourSeasonsBoston") diff --git a/MapboxCoreNavigationTests/NavigationLocationManagerTests.swift b/MapboxCoreNavigationTests/NavigationLocationManagerTests.swift index 52717893b0c..d0e71b3eff6 100644 --- a/MapboxCoreNavigationTests/NavigationLocationManagerTests.swift +++ b/MapboxCoreNavigationTests/NavigationLocationManagerTests.swift @@ -2,7 +2,6 @@ import XCTest import MapboxCoreNavigation class NavigationLocationManagerTests: XCTestCase { - func testNavigationLocationManagerDefaultAccuracy() { let locationManager = NavigationLocationManager() XCTAssertEqual(locationManager.desiredAccuracy, kCLLocationAccuracyBest) diff --git a/MapboxCoreNavigationTests/NavigationServiceTests.swift b/MapboxCoreNavigationTests/NavigationServiceTests.swift index 2ef7dbc8d3f..50e90d9ddc5 100644 --- a/MapboxCoreNavigationTests/NavigationServiceTests.swift +++ b/MapboxCoreNavigationTests/NavigationServiceTests.swift @@ -2,13 +2,13 @@ import XCTest import MapboxDirections import Turf import MapboxMobileEvents +import os.log @testable import TestHelper @testable import MapboxCoreNavigation fileprivate let mbTestHeading: CLLocationDirection = 50 class NavigationServiceTests: XCTestCase { - var eventsManagerSpy: NavigationEventsManagerSpy! let directionsClientSpy = DirectionsSpy(accessToken: "garbage", host: nil) let delegate = NavigationServiceDelegateSpy() @@ -115,7 +115,6 @@ class NavigationServiceTests: XCTestCase { navigation.locationManager!(navigation.locationManager, didUpdateLocations: [firstLocation]) XCTAssertEqual(navigation.router.location!.coordinate.latitude, firstLocation.coordinate.latitude, accuracy: 0.0005, "Check snapped location is working") XCTAssertEqual(navigation.router.location!.coordinate.longitude, firstLocation.coordinate.longitude, accuracy: 0.0005, "Check snapped location is working") - } func testSnappedAtEndOfStepLocationWhenMovingSlowly() { @@ -171,7 +170,6 @@ class NavigationServiceTests: XCTestCase { XCTAssertEqual(navigation.router.location!.coordinate.latitude, futureInaccurateLocation.coordinate.latitude, accuracy: 0.0005, "Inaccurate location is still snapped") XCTAssertEqual(navigation.router.location!.coordinate.longitude, futureInaccurateLocation.coordinate.longitude, accuracy: 0.0005, "Inaccurate location is still snapped") - } func testUserPuckShouldFaceBackwards() { @@ -202,7 +200,6 @@ class NavigationServiceTests: XCTestCase { //TODO: Broken by PortableRoutecontroller & MBNavigator -- needs team discussion. func x_testLocationShouldUseHeading() { - let navigation = dependencies.navigationService let firstLocation = dependencies.routeLocations.firstLocation navigation.locationManager!(navigation.locationManager, didUpdateLocations: [firstLocation]) @@ -247,7 +244,6 @@ class NavigationServiceTests: XCTestCase { XCTAssert(simulatedLocationManager.route == alternateRoute, "Simulated Location Manager should be updated with new route progress model") } - func testReroutingFromALocationSendsEvents() { let navigationService = dependencies.navigationService let router = navigationService.router! @@ -363,7 +359,6 @@ class NavigationServiceTests: XCTestCase { } func testRouteControllerDoesNotHaveRetainCycle() { - weak var subject: RouteController? = nil autoreleasepool { @@ -376,20 +371,18 @@ class NavigationServiceTests: XCTestCase { } func testLegacyRouteControllerDoesNotHaveRetainCycle() { - - weak var subject: LegacyRouteController? = nil - - autoreleasepool { - let fakeDataSource = RouteControllerDataSourceFake() - let routeController = LegacyRouteController(along: initialRoute, directions: directionsClientSpy, dataSource: fakeDataSource) - subject = routeController - } - - XCTAssertNil(subject, "Expected LegacyRouteController not to live beyond autorelease pool") - } + weak var subject: LegacyRouteController? = nil + + autoreleasepool { + let fakeDataSource = RouteControllerDataSourceFake() + let routeController = LegacyRouteController(along: initialRoute, directions: directionsClientSpy, dataSource: fakeDataSource) + subject = routeController + } + + XCTAssertNil(subject, "Expected LegacyRouteController not to live beyond autorelease pool") + } func testRouteControllerDoesNotRetainDataSource() { - weak var subject: RouterDataSource? = nil autoreleasepool { @@ -420,7 +413,6 @@ class NavigationServiceTests: XCTestCase { routeController.route = route for (index, location) in trace.enumerated() { - service.locationManager!(service.locationManager, didUpdateLocations: [location]) if index < 33 { @@ -489,4 +481,35 @@ class NavigationServiceTests: XCTestCase { service.locationManager(locationManager, didUpdateLocations: [location]) } } + + func testUnimplementedLogging() { + unimplementedTestLogs = [] + + let route = Fixture.route(from: "DCA-Arboretum") + let directions = Directions(accessToken: "foo") + let locationManager = DummyLocationManager() + let trace = Fixture.generateTrace(for: route, speedMultiplier: 4).shiftedToPresent() + + let service = MapboxNavigationService(route: route, directions: directions, locationSource: locationManager, eventsManagerType: nil) + + let spy = EmptyNavigationServiceDelegate() + service.delegate = spy + service.start() + + for location in trace { + service.locationManager(locationManager, didUpdateLocations: [location]) + } + + guard let logs = unimplementedTestLogs else { + XCTFail("Unable to fetch logs") + return + } + + let ourLogs = logs.filter { $0.0 == "EmptyNavigationServiceDelegate" } + + XCTAssertEqual(ourLogs.count, 4, "Expected logs to be populated and expected number of messages sent") + unimplementedTestLogs = nil + } } + +class EmptyNavigationServiceDelegate: NavigationServiceDelegate {} diff --git a/MapboxCoreNavigationTests/OfflineRoutingTests.swift b/MapboxCoreNavigationTests/OfflineRoutingTests.swift index 15620222976..38f3cea69c3 100644 --- a/MapboxCoreNavigationTests/OfflineRoutingTests.swift +++ b/MapboxCoreNavigationTests/OfflineRoutingTests.swift @@ -3,11 +3,8 @@ import MapboxDirections import TestHelper @testable import MapboxCoreNavigation - class OfflineRoutingTests: XCTestCase { - func testOfflineDirections() { - let bundle = Bundle(for: Fixture.self) let tilesURL = URL(fileURLWithPath: bundle.bundlePath.appending("/tiles/liechtenstein")) @@ -45,7 +42,6 @@ class OfflineRoutingTests: XCTestCase { } func testOfflineDirectionsError() { - let bundle = Bundle(for: Fixture.self) let tilesURL = URL(fileURLWithPath: bundle.bundlePath).appendingPathComponent("/tiles/liechtenstein") @@ -80,7 +76,6 @@ class OfflineRoutingTests: XCTestCase { } func testUnpackTilePack() { - let bundle = Bundle(for: Fixture.self) let readonlyPackURL = URL(fileURLWithPath: bundle.path(forResource: "li", ofType: "tar")!) @@ -99,9 +94,7 @@ class OfflineRoutingTests: XCTestCase { progressExpectation.expectedFulfillmentCount = 2 NavigationDirections.unpackTilePack(at: packURL, outputDirectoryURL: outputDirectoryURL, progressHandler: { (totalBytes, unpackedBytes) in - progressExpectation.fulfill() - }) { (result, error) in XCTAssertEqual(result, 5) XCTAssertNil(error) diff --git a/MapboxCoreNavigationTests/OptionsTests.swift b/MapboxCoreNavigationTests/OptionsTests.swift index 9983bc0d2bf..2f8ffbc7543 100644 --- a/MapboxCoreNavigationTests/OptionsTests.swift +++ b/MapboxCoreNavigationTests/OptionsTests.swift @@ -4,7 +4,6 @@ import MapboxDirections @testable import MapboxCoreNavigation class OptionsTests: XCTestCase { - let coordinates = [CLLocationCoordinate2D(latitude: 0, longitude: 1), CLLocationCoordinate2D(latitude: 2, longitude: 3)] func testNavigationRouteOptions() { diff --git a/MapboxCoreNavigationTests/RouteProgressTests.swift b/MapboxCoreNavigationTests/RouteProgressTests.swift index c3155bc6db1..82bcda4024d 100644 --- a/MapboxCoreNavigationTests/RouteProgressTests.swift +++ b/MapboxCoreNavigationTests/RouteProgressTests.swift @@ -31,7 +31,7 @@ class RouteProgressTests: XCTestCase { XCTAssertEqual(routeProgress.currentLegProgress.currentStepProgress.distanceTraveled, 0) XCTAssertEqual(routeProgress.currentLegProgress.currentStepProgress.durationRemaining, 86.6, accuracy: 0.001) XCTAssertEqual(routeProgress.currentLegProgress.currentStepProgress.fractionTraveled, 0) - XCTAssertEqual(routeProgress.currentLegProgress.currentStepProgress.userDistanceToManeuverLocation, Double.infinity) + XCTAssertEqual(routeProgress.currentLegProgress.currentStepProgress.userDistanceToManeuverLocation, 384.1) XCTAssertEqual(routeProgress.currentLegProgress.currentStepProgress.step.description, "Head south on Taylor Street") } @@ -43,7 +43,7 @@ class RouteProgressTests: XCTestCase { XCTAssertEqual(routeProgress.currentLegProgress.currentStepProgress.distanceTraveled, 0) XCTAssertEqual(round(routeProgress.currentLegProgress.currentStepProgress.durationRemaining), 73) XCTAssertEqual(routeProgress.currentLegProgress.currentStepProgress.fractionTraveled, 0) - XCTAssertEqual(routeProgress.currentLegProgress.currentStepProgress.userDistanceToManeuverLocation, Double.infinity) + XCTAssertEqual(routeProgress.currentLegProgress.currentStepProgress.userDistanceToManeuverLocation, 439.1) XCTAssertEqual(routeProgress.currentLegProgress.currentStepProgress.step.description, "Turn right onto California Street") } diff --git a/MapboxCoreNavigationTests/TunnelAuthorityTests.swift b/MapboxCoreNavigationTests/TunnelAuthorityTests.swift index 3873e58ca80..3c984e76f24 100644 --- a/MapboxCoreNavigationTests/TunnelAuthorityTests.swift +++ b/MapboxCoreNavigationTests/TunnelAuthorityTests.swift @@ -6,7 +6,6 @@ import MapboxDirections import TestHelper @testable import MapboxCoreNavigation - let tunnelRoute = Fixture.route(from: "routeWithTunnels_9thStreetDC") class TunnelAuthorityTests: XCTestCase { diff --git a/MapboxNavigation.xcodeproj/project.pbxproj b/MapboxNavigation.xcodeproj/project.pbxproj index c516bb9cd16..3d73064ca92 100644 --- a/MapboxNavigation.xcodeproj/project.pbxproj +++ b/MapboxNavigation.xcodeproj/project.pbxproj @@ -111,7 +111,6 @@ 358E856421DD23E200E415C2 /* DummyLocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 358E856321DD23E200E415C2 /* DummyLocationManager.swift */; }; 359574A81F28CC5A00838209 /* CLLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 359574A71F28CC3800838209 /* CLLocation.swift */; }; 359574AA1F28CCBB00838209 /* LocationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 359574A91F28CCBB00838209 /* LocationTests.swift */; }; - 3595FE472190F78C0035B765 /* MBViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3595FE462190F78C0035B765 /* MBViewController.m */; }; 3595FE48219190400035B765 /* TestHelper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 35CDA85E2190F2A30072B675 /* TestHelper.framework */; }; 3595FE49219190420035B765 /* TestHelper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 35CDA85E2190F2A30072B675 /* TestHelper.framework */; }; 3597ABD021553B6F00C12785 /* SimulatedLocationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3597ABCF21553B6F00C12785 /* SimulatedLocationManagerTests.swift */; }; @@ -124,7 +123,6 @@ 359D283C1F9DC14F00FDE9C9 /* UICollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 359D283B1F9DC14F00FDE9C9 /* UICollectionView.swift */; }; 35A262B92050A5CD00AEFF6D /* InstructionsBannerViewSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35A262B82050A5CD00AEFF6D /* InstructionsBannerViewSnapshotTests.swift */; }; 35A43F77223BD632000CB367 /* RouteLeg.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35A43F76223BD632000CB367 /* RouteLeg.swift */; }; - 35A43F79223BD836000CB367 /* BridgingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 35A43F78223BD836000CB367 /* BridgingTests.m */; }; 35A5413B1EFC052700E49846 /* RouteOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35A5413A1EFC052700E49846 /* RouteOptions.swift */; }; 35B1AEBC20AD9B3C00C8544E /* LeaksSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35B1AEBB20AD9B3C00C8544E /* LeaksSpec.swift */; }; 35B1AEBE20AD9C7800C8544E /* LeakTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35B1AEBD20AD9C7800C8544E /* LeakTest.swift */; }; @@ -132,10 +130,8 @@ 35B5A47E1FFFDCE5000A3C8D /* NavigationViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35B5A47D1FFFDCE5000A3C8D /* NavigationViewLayout.swift */; }; 35B711D41E5E7AD2001EDA8D /* MapboxNavigation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 351BEBD71E5BCC28006FE110 /* MapboxNavigation.framework */; }; 35B7837E1F9547B300291F9A /* Transitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35B7837D1F9547B300291F9A /* Transitioning.swift */; }; - 35B839491E2E3D5D0045A868 /* MBRouteController.m in Sources */ = {isa = PBXBuildFile; fileRef = 35B839481E2E3D5D0045A868 /* MBRouteController.m */; }; 35BC7178226F6667003BB5F1 /* CompassViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35BC7177226F6667003BB5F1 /* CompassViewTests.swift */; }; 35C11359215BADF900CC2929 /* sthlm-double-back-replay.json in Resources */ = {isa = PBXBuildFile; fileRef = 35C11358215BADF800CC2929 /* sthlm-double-back-replay.json */; }; - 35C57D6A208DD4A200BDD2A6 /* BridgingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 35C57D69208DD4A200BDD2A6 /* BridgingTests.m */; }; 35C714B0203B251F00F0C2AE /* MapboxSpeech.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C5A9DDBD202E12EE007D52DA /* MapboxSpeech.framework */; }; 35C714B1203B251F00F0C2AE /* MapboxSpeech.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C5A9DDBD202E12EE007D52DA /* MapboxSpeech.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 35C77F621FE8219900338416 /* NavigationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35375EC01F31FA86004CE727 /* NavigationSettings.swift */; }; @@ -205,9 +201,7 @@ 35CDA8982190F6980072B675 /* turn_left.data in Resources */ = {isa = PBXBuildFile; fileRef = 35CDA8812190F5080072B675 /* turn_left.data */; }; 35CDA8992190F6980072B675 /* UnionSquare-to-GGPark.route in Resources */ = {isa = PBXBuildFile; fileRef = 35CDA88B2190F5130072B675 /* UnionSquare-to-GGPark.route */; }; 35CF34B11F0A733200C2692E /* UIFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35CF34B01F0A733200C2692E /* UIFont.swift */; }; - 35D428291FA0B61F00176028 /* InstructionsBannerViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35D428281FA0B61F00176028 /* InstructionsBannerViewLayout.swift */; }; 35D4282B1FA0DF1D00176028 /* MapboxNavigation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 351BEBD71E5BCC28006FE110 /* MapboxNavigation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 35D457A71E2D253100A89946 /* MBRouteController.h in Headers */ = {isa = PBXBuildFile; fileRef = 35D457A61E2D253100A89946 /* MBRouteController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 35D825FC1E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 35D825FA1E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.m */; }; 35D825FE1E6A2EC60088F83B /* MapboxNavigation.h in Headers */ = {isa = PBXBuildFile; fileRef = 35D825FD1E6A2EC60088F83B /* MapboxNavigation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 35DA85791FC45787004092EC /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35DA85781FC45787004092EC /* StatusView.swift */; }; @@ -231,6 +225,7 @@ 3EA9371104016CD402547F1A /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA938479CF48D7AD1B6369B /* ImageCache.swift */; }; 3EA937B1F4DF73EB004BA6BE /* InstructionPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA93230997B8D59E3B76C8C /* InstructionPresenter.swift */; }; 3EA93A1FEFDDB709DE84BED9 /* ImageRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA938BE5468824787100228 /* ImageRepository.swift */; }; + 4303A3992332CD6200B5737D /* UnimplementedLogging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4303A3982332CD6200B5737D /* UnimplementedLogging.swift */; }; 4341758223060A17004264A9 /* route-with-tertiary.json in Resources */ = {isa = PBXBuildFile; fileRef = 439FFC222304BC23004C20AA /* route-with-tertiary.json */; }; 4341758423061666004264A9 /* SnapshotTest+Mapbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4341758323061666004264A9 /* SnapshotTest+Mapbox.swift */; }; 439FFC252304BF54004C20AA /* GuidanceCardsSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 439FFC242304BF54004C20AA /* GuidanceCardsSnapshotTests.swift */; }; @@ -285,7 +280,6 @@ AE47A32E22B1F6AE0096458C /* InstructionsCardStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE47A32422B1F6AD0096458C /* InstructionsCardStyle.swift */; }; AE47A32F22B1F6AE0096458C /* DayInstructionsCardStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE47A32522B1F6AD0096458C /* DayInstructionsCardStyle.swift */; }; AE47A33022B1F6AE0096458C /* InstructionsCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE47A32622B1F6AE0096458C /* InstructionsCardView.swift */; }; - AE47A33122B1F6AE0096458C /* InstructionsCardViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE47A32722B1F6AE0096458C /* InstructionsCardViewLayout.swift */; }; AE47A33222B1F6AE0096458C /* UIColor+InstructionsCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE47A32822B1F6AE0096458C /* UIColor+InstructionsCard.swift */; }; AE47A33322B1F6AE0096458C /* Constants+InstructionsCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE47A32922B1F6AE0096458C /* Constants+InstructionsCard.swift */; }; AE47A33422B1F6AE0096458C /* InstructionsCardContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE47A32A22B1F6AE0096458C /* InstructionsCardContainerView.swift */; }; @@ -346,8 +340,6 @@ C58822001FB0F0D7008B0A2D /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = C58821FF1FB0F0D7008B0A2D /* Error.swift */; }; C588C3C21F33882100520EF2 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35BF8CA31F28EBD8003F6125 /* String.swift */; }; C58D6BAD1DDCF2AE00387F53 /* CoreConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C58D6BAC1DDCF2AE00387F53 /* CoreConstants.swift */; }; - C5A60ECC20A25BC900C21178 /* MD5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5A60EC820A2417200C21178 /* MD5Tests.swift */; }; - C5A60ECD20A25DA100C21178 /* md5_crazy_strings.txt in Resources */ = {isa = PBXBuildFile; fileRef = C5A60ECA20A241B600C21178 /* md5_crazy_strings.txt */; }; C5A6B2DD1F4CE8E8004260EA /* StyleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5A6B2DC1F4CE8E8004260EA /* StyleType.swift */; }; C5A7EC5C1FD610A80008B9BA /* VisualInstructionComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5A7EC5B1FD610A80008B9BA /* VisualInstructionComponent.swift */; }; C5ABB50E20408D2C00AFA92C /* NavigationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5ABB50D20408D2C00AFA92C /* NavigationServiceTests.swift */; }; @@ -367,6 +359,8 @@ CFD47D9020FD85EC00BC1E49 /* MGLAccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFD47D8F20FD85EC00BC1E49 /* MGLAccountManager.swift */; }; DA0557232154FFB200A1F2AA /* Route.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0557202154EF4700A1F2AA /* Route.swift */; }; DA0557252155040700A1F2AA /* RouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0557242155040700A1F2AA /* RouteTests.swift */; }; + DA1755F82357B6BD00B06C1D /* StringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5A60EC820A2417200C21178 /* StringTests.swift */; }; + DA1755F92357B7A100B06C1D /* md5_crazy_strings.txt in Resources */ = {isa = PBXBuildFile; fileRef = C5A60ECA20A241B600C21178 /* md5_crazy_strings.txt */; }; DA23C9611F4FC05C00BA9522 /* MGLMapView+MGLNavigationAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35D825F91E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA303C9521B728DD00F921DC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA303C9321B728DD00F921DC /* Localizable.strings */; }; DA303C9621B728DD00F921DC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA303C9321B728DD00F921DC /* Localizable.strings */; }; @@ -386,10 +380,6 @@ DAA96D18215A961D00BEF703 /* route-doubling-back.json in Resources */ = {isa = PBXBuildFile; fileRef = DAA96D17215A961D00BEF703 /* route-doubling-back.json */; }; DAAE5F301EAE4C4700832871 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DAAE5F321EAE4C4700832871 /* Localizable.strings */; }; DAD17202214DB12B009C8161 /* CPMapTemplateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD17201214DB12B009C8161 /* CPMapTemplateTests.swift */; }; - DADAD828203504C6002E25CA /* MBNavigationSettings.h in Headers */ = {isa = PBXBuildFile; fileRef = DADAD826203504C6002E25CA /* MBNavigationSettings.h */; settings = {ATTRIBUTES = (Public, ); }; }; - DADAD829203504C6002E25CA /* MBNavigationSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = DADAD827203504C6002E25CA /* MBNavigationSettings.m */; }; - DADAD82E20350849002E25CA /* MBConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = DADAD82C20350849002E25CA /* MBConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; - DADAD82F20350849002E25CA /* MBConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = DADAD82D20350849002E25CA /* MBConstants.m */; }; DADD82802161EC0300B8B47D /* UIViewAnimationOptionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADD827F2161EC0300B8B47D /* UIViewAnimationOptionsTests.swift */; }; DAE22A2921C9DEDA00CA269D /* MGLVectorTileSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAE22A2821C9DEDA00CA269D /* MGLVectorTileSourceTests.swift */; }; DAFA92071F01735000A7FB09 /* DistanceFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 351BEC0B1E5BCC72006FE110 /* DistanceFormatter.swift */; }; @@ -721,8 +711,6 @@ 358E856321DD23E200E415C2 /* DummyLocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyLocationManager.swift; sourceTree = ""; }; 359574A71F28CC3800838209 /* CLLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CLLocation.swift; path = MapboxCoreNavigation/CLLocation.swift; sourceTree = SOURCE_ROOT; }; 359574A91F28CCBB00838209 /* LocationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationTests.swift; sourceTree = ""; }; - 3595FE452190F78C0035B765 /* MBViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MBViewController.h; path = Example/MBViewController.h; sourceTree = ""; }; - 3595FE462190F78C0035B765 /* MBViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MBViewController.m; path = Example/MBViewController.m; sourceTree = ""; }; 3597ABCF21553B6F00C12785 /* SimulatedLocationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatedLocationManagerTests.swift; sourceTree = ""; }; 3597ABD321553F6800C12785 /* sthlm-double-back.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "sthlm-double-back.json"; sourceTree = ""; }; 359A8AEC1FA78D3000BDB486 /* DistanceFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DistanceFormatterTests.swift; sourceTree = ""; }; @@ -732,7 +720,6 @@ 35A1D3651E6624EF00A48FE8 /* Mapbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Mapbox.framework; path = Carthage/Build/iOS/Mapbox.framework; sourceTree = ""; }; 35A262B82050A5CD00AEFF6D /* InstructionsBannerViewSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstructionsBannerViewSnapshotTests.swift; sourceTree = ""; }; 35A43F76223BD632000CB367 /* RouteLeg.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteLeg.swift; sourceTree = ""; }; - 35A43F78223BD836000CB367 /* BridgingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BridgingTests.m; sourceTree = ""; }; 35A5413A1EFC052700E49846 /* RouteOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouteOptions.swift; sourceTree = ""; }; 35B1AEB920AD9AE600C8544E /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = ""; }; 35B1AEBA20AD9AEC00C8544E /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = ""; }; @@ -743,11 +730,9 @@ 35B711CF1E5E7AD2001EDA8D /* MapboxNavigationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MapboxNavigationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 35B711D31E5E7AD2001EDA8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35B7837D1F9547B300291F9A /* Transitioning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transitioning.swift; sourceTree = ""; }; - 35B839481E2E3D5D0045A868 /* MBRouteController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBRouteController.m; sourceTree = ""; }; 35BC7177226F6667003BB5F1 /* CompassViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompassViewTests.swift; sourceTree = ""; }; 35BF8CA31F28EBD8003F6125 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 35C11358215BADF800CC2929 /* sthlm-double-back-replay.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "sthlm-double-back-replay.json"; sourceTree = ""; }; - 35C57D69208DD4A200BDD2A6 /* BridgingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BridgingTests.m; sourceTree = ""; }; 35C6ED9A1EBB1DE400A27EF8 /* ca */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = ""; }; 35C6ED9C1EBB224A00A27EF8 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Main.strings"; sourceTree = ""; }; 35C6ED9E1EBB224D00A27EF8 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Main.strings; sourceTree = ""; }; @@ -794,8 +779,6 @@ 35CDA88B2190F5130072B675 /* UnionSquare-to-GGPark.route */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "UnionSquare-to-GGPark.route"; sourceTree = ""; }; 35CDA88C2190F5140072B675 /* route-with-banner-instructions.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "route-with-banner-instructions.json"; sourceTree = ""; }; 35CF34B01F0A733200C2692E /* UIFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFont.swift; sourceTree = ""; }; - 35D428281FA0B61F00176028 /* InstructionsBannerViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstructionsBannerViewLayout.swift; sourceTree = ""; }; - 35D457A61E2D253100A89946 /* MBRouteController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBRouteController.h; sourceTree = ""; }; 35D825F91E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLMapView+MGLNavigationAdditions.h"; sourceTree = ""; }; 35D825FA1E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLMapView+MGLNavigationAdditions.m"; sourceTree = ""; }; 35D825FD1E6A2EC60088F83B /* MapboxNavigation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapboxNavigation.h; sourceTree = ""; }; @@ -819,9 +802,11 @@ 3EA938BE5468824787100228 /* ImageRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRepository.swift; sourceTree = ""; }; 3EA93A10227A7DAF1861D9F5 /* Cache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cache.swift; sourceTree = ""; }; 3EA93EBD6E6BEC966BBE51D6 /* NavigationServiceTestDoubles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationServiceTestDoubles.swift; sourceTree = ""; }; + 4303A3982332CD6200B5737D /* UnimplementedLogging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnimplementedLogging.swift; sourceTree = ""; }; 4341758323061666004264A9 /* SnapshotTest+Mapbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SnapshotTest+Mapbox.swift"; sourceTree = ""; }; 439FFC222304BC23004C20AA /* route-with-tertiary.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "route-with-tertiary.json"; sourceTree = ""; }; 439FFC242304BF54004C20AA /* GuidanceCardsSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuidanceCardsSnapshotTests.swift; sourceTree = ""; }; + 43E69517233D297B0019BF6E /* cover.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = cover.md; path = docs/cover.md; sourceTree = ""; }; 6441B1691EFC64E50076499F /* WaypointConfirmationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WaypointConfirmationViewController.swift; path = Example/WaypointConfirmationViewController.swift; sourceTree = ""; }; 64847A031F04629D003F3A69 /* Feedback.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Feedback.swift; sourceTree = ""; }; 7C12F2D7225B7C310010A931 /* DCA-Arboretum-dummy-faster-route.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "DCA-Arboretum-dummy-faster-route.json"; sourceTree = ""; }; @@ -877,7 +862,6 @@ AE47A32422B1F6AD0096458C /* InstructionsCardStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstructionsCardStyle.swift; sourceTree = ""; }; AE47A32522B1F6AD0096458C /* DayInstructionsCardStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DayInstructionsCardStyle.swift; sourceTree = ""; }; AE47A32622B1F6AE0096458C /* InstructionsCardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstructionsCardView.swift; sourceTree = ""; }; - AE47A32722B1F6AE0096458C /* InstructionsCardViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstructionsCardViewLayout.swift; sourceTree = ""; }; AE47A32822B1F6AE0096458C /* UIColor+InstructionsCard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+InstructionsCard.swift"; sourceTree = ""; }; AE47A32922B1F6AE0096458C /* Constants+InstructionsCard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Constants+InstructionsCard.swift"; sourceTree = ""; }; AE47A32A22B1F6AE0096458C /* InstructionsCardContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstructionsCardContainerView.swift; sourceTree = ""; }; @@ -923,7 +907,7 @@ C582FD5E203626E900A9086E /* CLLocationDirection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLLocationDirection.swift; sourceTree = ""; }; C58821FF1FB0F0D7008B0A2D /* Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; C58D6BAC1DDCF2AE00387F53 /* CoreConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreConstants.swift; sourceTree = ""; }; - C5A60EC820A2417200C21178 /* MD5Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MD5Tests.swift; sourceTree = ""; }; + C5A60EC820A2417200C21178 /* StringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringTests.swift; sourceTree = ""; }; C5A60ECA20A241B600C21178 /* md5_crazy_strings.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = md5_crazy_strings.txt; sourceTree = ""; }; C5A6B2DC1F4CE8E8004260EA /* StyleType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StyleType.swift; sourceTree = ""; }; C5A7EC5B1FD610A80008B9BA /* VisualInstructionComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualInstructionComponent.swift; sourceTree = ""; }; @@ -1017,10 +1001,6 @@ DAD88E00202AC7AA00AAA536 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = Resources/uk.lproj/Localizable.stringsdict; sourceTree = ""; }; DAD88E01202AC80100AAA536 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = ""; }; DAD88E02202AC81F00AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = Resources/da.lproj/Localizable.stringsdict; sourceTree = ""; }; - DADAD826203504C6002E25CA /* MBNavigationSettings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBNavigationSettings.h; sourceTree = ""; }; - DADAD827203504C6002E25CA /* MBNavigationSettings.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBNavigationSettings.m; sourceTree = ""; }; - DADAD82C20350849002E25CA /* MBConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBConstants.h; sourceTree = ""; }; - DADAD82D20350849002E25CA /* MBConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBConstants.m; sourceTree = ""; }; DADD827F2161EC0300B8B47D /* UIViewAnimationOptionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewAnimationOptionsTests.swift; sourceTree = ""; }; DAE22A2821C9DEDA00CA269D /* MGLVectorTileSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MGLVectorTileSourceTests.swift; sourceTree = ""; }; DAE26B1A20644047001D6E1F /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Main.strings; sourceTree = ""; }; @@ -1210,6 +1190,7 @@ 16AC9D10212E356200CECE44 /* CPMapTemplate+MBTestable.mm */, DAD17201214DB12B009C8161 /* CPMapTemplateTests.swift */, DA0557242155040700A1F2AA /* RouteTests.swift */, + C5A60EC820A2417200C21178 /* StringTests.swift */, DADD827F2161EC0300B8B47D /* UIViewAnimationOptionsTests.swift */, DAE22A2821C9DEDA00CA269D /* MGLVectorTileSourceTests.swift */, ); @@ -1289,8 +1270,6 @@ 3EA93230997B8D59E3B76C8C /* InstructionPresenter.swift */, 35D825FD1E6A2EC60088F83B /* MapboxNavigation.h */, C565168A1FE1E23E00A0AD18 /* MapboxVoiceController.swift */, - DADAD82C20350849002E25CA /* MBConstants.h */, - DADAD82D20350849002E25CA /* MBConstants.m */, CFD47D8F20FD85EC00BC1E49 /* MGLAccountManager.swift */, C53208AA1E81FFB900910266 /* NavigationMapView.swift */, 8DEB4065220CE596008BAAB4 /* NavigationMapViewDelegate.swift */, @@ -1357,6 +1336,7 @@ 35C11358215BADF800CC2929 /* sthlm-double-back-replay.json */, DAA96D17215A961D00BEF703 /* route-doubling-back.json */, 8D86AE8821C31C640064A304 /* waypoint-after-turn.json */, + C5A60ECA20A241B600C21178 /* md5_crazy_strings.txt */, ); path = Fixtures; sourceTree = ""; @@ -1396,7 +1376,6 @@ 8D9CD7FD20880581004DC4B3 /* XCTestCase.swift */, 3597ABCF21553B6F00C12785 /* SimulatedLocationManagerTests.swift */, 3573EA70215A5A9F009899D7 /* RouteControllerSnapshotTests.swift */, - 35A43F78223BD836000CB367 /* BridgingTests.m */, 35BC7177226F6667003BB5F1 /* CompassViewTests.swift */, 35E5B962227B4B620033A124 /* CarPlayCompassViewTests.swift */, ); @@ -1421,8 +1400,6 @@ 3529FCF521A5C5D900AEA9AA /* UIViewController.swift */, 3529FCF721A5C62400AEA9AA /* SettingsViewController.swift */, 3529FCF921A5C63A00AEA9AA /* OfflineViewController.swift */, - 3595FE452190F78C0035B765 /* MBViewController.h */, - 3595FE462190F78C0035B765 /* MBViewController.m */, ); name = Example; sourceTree = ""; @@ -1505,6 +1482,7 @@ 8DF8E4E02202696800B29FEF /* MapboxNavigation-Documentation.podspec */, 8DF8E4E52202696800B29FEF /* MapboxNavigation.podspec */, 8DF8E4DF2202696800B29FEF /* README.md */, + 43E69517233D297B0019BF6E /* cover.md */, ); name = Documents; sourceTree = ""; @@ -1552,7 +1530,6 @@ AE87207D22CF97B900D7DAB7 /* InstructionsCardCollectionDelegate.swift */, AE47A32422B1F6AD0096458C /* InstructionsCardStyle.swift */, AE47A32622B1F6AE0096458C /* InstructionsCardView.swift */, - AE47A32722B1F6AE0096458C /* InstructionsCardViewLayout.swift */, AE47A32822B1F6AE0096458C /* UIColor+InstructionsCard.swift */, ); name = "Guidance Cards"; @@ -1606,7 +1583,6 @@ 35DC9D901F4323AA001ECD64 /* LanesView.swift */, 35E407671F5625FF00EFC814 /* StyleKitMarker.swift */, 3531C26F1F9E095400D92F9A /* InstructionsBannerView.swift */, - 35D428281FA0B61F00176028 /* InstructionsBannerViewLayout.swift */, 353280A01FA72871005175F3 /* InstructionLabel.swift */, 355ED36F1FAB724F00BCE1B8 /* BottomBannerViewLayout.swift */, 35F520BF1FB482A200FC9C37 /* NextBannerView.swift */, @@ -1620,7 +1596,6 @@ C52D09CF1DEF5E5F00BE3C5C /* Fixtures */ = { isa = PBXGroup; children = ( - C5A60ECA20A241B600C21178 /* md5_crazy_strings.txt */, ); path = Fixtures; sourceTree = ""; @@ -1700,10 +1675,6 @@ 64847A031F04629D003F3A69 /* Feedback.swift */, C5ADFBCD1DDCC7840011824B /* Info.plist */, C5ADFBCC1DDCC7840011824B /* MapboxCoreNavigation.h */, - DADAD826203504C6002E25CA /* MBNavigationSettings.h */, - DADAD827203504C6002E25CA /* MBNavigationSettings.m */, - 35D457A61E2D253100A89946 /* MBRouteController.h */, - 35B839481E2E3D5D0045A868 /* MBRouteController.m */, 353E68FB1EF0B7F8007B2AE5 /* NavigationLocationManager.swift */, C5E7A31B1F4F6828001CB015 /* NavigationRouteOptions.swift */, 35375EC01F31FA86004CE727 /* NavigationSettings.swift */, @@ -1712,6 +1683,7 @@ C5ADFBF91DDCC9580011824B /* LegacyRouteController.swift */, 8D2AA744211CDD4000EB7F72 /* NavigationService.swift */, 8D4CF9C521349FFB009C3FEE /* NavigationServiceDelegate.swift */, + 4303A3982332CD6200B5737D /* UnimplementedLogging.swift */, 8D1A5CD1212DDFCD0059BA4A /* DispatchTimer.swift */, 8D75F990212B5C7F00F99CF3 /* TunnelAuthority.swift */, 3582A25120EFA9680029C5DE /* RouterDelegate.swift */, @@ -1727,14 +1699,12 @@ isa = PBXGroup; children = ( C52D09CF1DEF5E5F00BE3C5C /* Fixtures */, - 35C57D69208DD4A200BDD2A6 /* BridgingTests.m */, C5BF7371206AB0DF00CDBB6D /* CLHeadingPrivate.h */, 359A8AEC1FA78D3000BDB486 /* DistanceFormatterTests.swift */, C5ADFBD91DDCC7840011824B /* Info.plist */, 359574A91F28CCBB00838209 /* LocationTests.swift */, C5BF7370206AB0DE00CDBB6D /* MapboxCoreNavigationTests-Bridging-Header.h */, C5ADFBD71DDCC7840011824B /* MapboxCoreNavigationTests.swift */, - C5A60EC820A2417200C21178 /* MD5Tests.swift */, 162039CE216C348500875F5C /* NavigationEventsManagerTests.swift */, C551B0E520D42222009A986F /* NavigationLocationManagerTests.swift */, C5ABB50D20408D2C00AFA92C /* NavigationServiceTests.swift */, @@ -1758,7 +1728,6 @@ files = ( DA23C9611F4FC05C00BA9522 /* MGLMapView+MGLNavigationAdditions.h in Headers */, 35D825FE1E6A2EC60088F83B /* MapboxNavigation.h in Headers */, - DADAD82E20350849002E25CA /* MBConstants.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1775,8 +1744,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - DADAD828203504C6002E25CA /* MBNavigationSettings.h in Headers */, - 35D457A71E2D253100A89946 /* MBRouteController.h in Headers */, C5C94C1B1DDCD22B0097296A /* MapboxCoreNavigation.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2148,6 +2115,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + DA1755F92357B7A100B06C1D /* md5_crazy_strings.txt in Resources */, 351030111F54B72000E3B7E7 /* route-for-lane-testing.json in Resources */, 3527D2B91EC4619400C07FC9 /* Fixtures.xcassets in Resources */, 355DB5771EFA780E0091BFB7 /* UnionSquare-to-GGPark.route in Resources */, @@ -2245,7 +2213,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - C5A60ECD20A25DA100C21178 /* md5_crazy_strings.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2397,7 +2364,6 @@ C51511D120EAC89D00372A91 /* CPMapTemplate.swift in Sources */, 353280A11FA72871005175F3 /* InstructionLabel.swift in Sources */, 351BEBFF1E5BCC63006FE110 /* ManeuversStyleKit.swift in Sources */, - 35D428291FA0B61F00176028 /* InstructionsBannerViewLayout.swift in Sources */, C54C655220336F2600D338E0 /* Constants.swift in Sources */, 353610CE1FAB6A8F00FB1746 /* BottomBannerViewController.swift in Sources */, AEC3AC9A2106703100A26F34 /* HighwayShield.swift in Sources */, @@ -2422,7 +2388,6 @@ 353EC9D71FB09708002EB0AB /* StepsViewController.swift in Sources */, 35726EE81F0856E900AFA1B6 /* DayStyle.swift in Sources */, AE47A32E22B1F6AE0096458C /* InstructionsCardStyle.swift in Sources */, - AE47A33122B1F6AE0096458C /* InstructionsCardViewLayout.swift in Sources */, AE47A32C22B1F6AE0096458C /* InstructionsCardViewController.swift in Sources */, 358E31D622562698009B3EC2 /* CarPayCompassView.swift in Sources */, AE47A33322B1F6AE0096458C /* Constants+InstructionsCard.swift in Sources */, @@ -2436,7 +2401,6 @@ DA0557232154FFB200A1F2AA /* Route.swift in Sources */, 8D24A2F62040960C0098CBF8 /* UIEdgeInsets.swift in Sources */, 353E3C8F20A3501C00FD1789 /* MGLStyle.swift in Sources */, - DADAD82F20350849002E25CA /* MBConstants.m in Sources */, C57491DF1FACC42F006F97BC /* CGPoint.swift in Sources */, 16C2A421211526EE00FE6E68 /* CarPlayManager.swift in Sources */, 8D53136B20653FA20044891E /* ExitView.swift in Sources */, @@ -2505,7 +2469,6 @@ 3529FCF221A5C5B400AEA9AA /* ResizableView.swift in Sources */, C5D9800D1EFA8BA9006DBF2E /* CustomViewController.swift in Sources */, AED6285622CBE4CE00058A51 /* ViewController+GuidanceCards.swift in Sources */, - 3595FE472190F78C0035B765 /* MBViewController.m in Sources */, C51FC31720F689F800400CE7 /* CustomStyles.swift in Sources */, 6441B16A1EFC64E50076499F /* WaypointConfirmationViewController.swift in Sources */, 358D14661E5E3B7700ADE590 /* AppDelegate.swift in Sources */, @@ -2528,7 +2491,6 @@ 16EF6C1E21193A9600AA580B /* CarPlayManagerTests.swift in Sources */, DADD82802161EC0300B8B47D /* UIViewAnimationOptionsTests.swift in Sources */, 8DFD949E221F66BE00152F45 /* BottomBannerSnapshotTests.swift in Sources */, - 35A43F79223BD836000CB367 /* BridgingTests.m in Sources */, DAD17202214DB12B009C8161 /* CPMapTemplateTests.swift in Sources */, 35EFD009207CA5E800BF3873 /* ManeuverViewTests.swift in Sources */, 16E4F97F205B05FE00531791 /* MapboxVoiceControllerTests.swift in Sources */, @@ -2552,6 +2514,7 @@ 35B1AEBE20AD9C7800C8544E /* LeakTest.swift in Sources */, 16E3625C201265D600DF0592 /* ImageDownloadOperationSpy.swift in Sources */, 1662244B2029059C00EA4824 /* ImageCacheTests.swift in Sources */, + DA1755F82357B6BD00B06C1D /* StringTests.swift in Sources */, 3510300F1F54B67000E3B7E7 /* LaneTests.swift in Sources */, 160A4A712127A46C0028B070 /* CPBarButton+MBTestable.m in Sources */, 169A970A216440820082A6A0 /* NavigationViewControllerTestDoubles.swift in Sources */, @@ -2653,9 +2616,8 @@ 35F3387C2232AEBF0071DB5C /* MinimumEditDistance.swift in Sources */, C5D9800F1EFBCDAD006DBF2E /* Date.swift in Sources */, 35A43F77223BD632000CB367 /* RouteLeg.swift in Sources */, - 35B839491E2E3D5D0045A868 /* MBRouteController.m in Sources */, + 4303A3992332CD6200B5737D /* UnimplementedLogging.swift in Sources */, 8D2AA745211CDD4000EB7F72 /* NavigationService.swift in Sources */, - DADAD829203504C6002E25CA /* MBNavigationSettings.m in Sources */, 35A5413B1EFC052700E49846 /* RouteOptions.swift in Sources */, 353E69041EF0C4E5007B2AE5 /* SimulatedLocationManager.swift in Sources */, DAFA92071F01735000A7FB09 /* DistanceFormatter.swift in Sources */, @@ -2683,8 +2645,6 @@ C52AC1261DF0E48600396B9F /* RouteProgressTests.swift in Sources */, 359574AA1F28CCBB00838209 /* LocationTests.swift in Sources */, C582BA2C2073E77E00647DAA /* StringTests.swift in Sources */, - 35C57D6A208DD4A200BDD2A6 /* BridgingTests.m in Sources */, - C5A60ECC20A25BC900C21178 /* MD5Tests.swift in Sources */, 3557506B21A826C600AEF9B6 /* OfflineRoutingTests.swift in Sources */, 352762A4225B751A0015B632 /* OptionsTests.swift in Sources */, 8DB7EF6A2176674800DA83A3 /* MapboxNavigationServiceSpec.swift in Sources */, diff --git a/MapboxNavigation/BottomBannerViewController.swift b/MapboxNavigation/BottomBannerViewController.swift index d5bb8edf57d..e1415a5fa43 100644 --- a/MapboxNavigation/BottomBannerViewController.swift +++ b/MapboxNavigation/BottomBannerViewController.swift @@ -6,7 +6,6 @@ import MapboxDirections `BottomBannerViewControllerDelegate` provides a method for reacting to the user tapping on the "cancel" button in the `BottomBannerViewController`. */ public protocol BottomBannerViewControllerDelegate: class { - /** A method that is invoked when the user taps on the cancel button. - parameter sender: The button that originated the tap event. @@ -18,10 +17,8 @@ public protocol BottomBannerViewControllerDelegate: class { A user interface element designed to display the estimated arrival time, distance, and time remaining, as well as give the user a control the cancel the navigation session. */ @IBDesignable -@objc(MBBottomBannerViewController) open class BottomBannerViewController: UIViewController, NavigationComponent { - - /* + /** A padded spacer view that covers the bottom safe area of the device, if any. */ lazy open var bottomPaddingView: BottomPaddingView = .forAutoLayout() @@ -38,7 +35,7 @@ open class BottomBannerViewController: UIViewController, NavigationComponent { /** The label that represents the user's remaining distance. - */ + */ open var distanceRemainingLabel: DistanceRemainingLabel! /** @@ -48,29 +45,28 @@ open class BottomBannerViewController: UIViewController, NavigationComponent { /** The button that, by default, allows the user to cancel the navigation session. - */ + */ open var cancelButton: CancelButton! /** A vertical divider that seperates the cancel button and informative labels. - */ + */ open var verticalDividerView: SeparatorView! /** A horizontal divider that adds visual separation between the bottom banner and its superview. - */ + */ open var horizontalDividerView: SeparatorView! /** The delegate for the view controller. - seealso: BottomBannerViewControllerDelegate - */ + */ open weak var delegate: BottomBannerViewControllerDelegate? var previousProgress: RouteProgress? var timer: DispatchTimer? - let dateFormatter = DateFormatter() let dateComponentsFormatter = DateComponentsFormatter() let distanceFormatter = DistanceFormatter() @@ -169,11 +165,11 @@ open class BottomBannerViewController: UIViewController, NavigationComponent { arrivalTimeLabel.text = "10:09" } - @objc public func navigationService(_ service: NavigationService, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) { + public func navigationService(_ service: NavigationService, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) { refreshETA() } - @objc public func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { + public func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { resetETATimer() updateETA(routeProgress: progress) previousProgress = progress @@ -192,7 +188,7 @@ open class BottomBannerViewController: UIViewController, NavigationComponent { timer?.arm() } - @objc func refreshETA() { + func refreshETA() { guard let progress = previousProgress else { return } updateETA(routeProgress: progress) } diff --git a/MapboxNavigation/BottomBannerViewLayout.swift b/MapboxNavigation/BottomBannerViewLayout.swift index 02f03d7abb6..b27b6cf6723 100644 --- a/MapboxNavigation/BottomBannerViewLayout.swift +++ b/MapboxNavigation/BottomBannerViewLayout.swift @@ -1,9 +1,7 @@ import UIKit extension BottomBannerViewController { - func setupRootViews() { - let children = [bottomBannerView, bottomPaddingView] view.addSubviews(children) setupRootViewConstraints() @@ -26,7 +24,6 @@ extension BottomBannerViewController { } func setupBottomBanner() { - let timeRemainingLabel = TimeRemainingLabel() timeRemainingLabel.translatesAutoresizingMaskIntoConstraints = false timeRemainingLabel.font = .systemFont(ofSize: 28, weight: .medium) diff --git a/MapboxNavigation/Bundle.swift b/MapboxNavigation/Bundle.swift index 0b7f89ef081..6c7ab8e1219 100644 --- a/MapboxNavigation/Bundle.swift +++ b/MapboxNavigation/Bundle.swift @@ -1,7 +1,6 @@ import Foundation extension Bundle { - class var mapboxNavigation: Bundle { get { return Bundle(for: NavigationViewController.self) } } diff --git a/MapboxNavigation/Cache.swift b/MapboxNavigation/Cache.swift index 5c20ca6bdb5..75599531f14 100644 --- a/MapboxNavigation/Cache.swift +++ b/MapboxNavigation/Cache.swift @@ -5,7 +5,7 @@ public typealias CompletionHandler = () -> Void /** A cache consists of both in-memory and on-disk components, both of which can be reset. */ -@objc(MBBimodalCache) +@objc(MBBimodalCache) public protocol BimodalCache { func clearMemory() func clearDisk(completion: CompletionHandler?) @@ -14,7 +14,6 @@ public protocol BimodalCache { /** A cache which supports storing images */ -@objc(MBBimodalImageCache) public protocol BimodalImageCache: BimodalCache { func store(_ image: UIImage, forKey key: String, toDisk: Bool, completion completionBlock: CompletionHandler?) func image(forKey: String?) -> UIImage? @@ -23,14 +22,13 @@ public protocol BimodalImageCache: BimodalCache { /** A cache which supports storing data */ -@objc(MBBimodalDataCache) public protocol BimodalDataCache: BimodalCache { func store(_ data: Data, forKey key: String, toDisk: Bool, completion completionBlock: CompletionHandler?) func data(forKey: String?) -> Data? } - /** - A general purpose on-disk cache used by both the ImageCache and DataCache implementations +/** + A general purpose on-disk cache used by both the ImageCache and DataCache implementations */ internal class FileCache { let diskCacheURL: URL = { @@ -49,7 +47,7 @@ internal class FileCache { } } - /* + /** Stores data in the file cache for the given key, and calls the completion handler when finished. */ public func store(_ data: Data, forKey key: String, completion: CompletionHandler?) { @@ -69,10 +67,9 @@ internal class FileCache { } completion?() } - } - /* + /** Returns data from the file cache for the given key, if any. */ public func dataFromFileCache(forKey key: String?) -> Data? { @@ -87,7 +84,7 @@ internal class FileCache { } } - /* + /** Clears the disk cache by removing and recreating the cache directory, and calls the completion handler when finished. */ public func clearDisk(completion: CompletionHandler?) { @@ -115,7 +112,7 @@ internal class FileCache { } func cacheKeyForKey(_ key: String) -> String { - return key.md5() + return key.md5 } private func createCacheDirIfNeeded(_ url: URL, fileManager: FileManager) { diff --git a/MapboxNavigation/CarPayCompassView.swift b/MapboxNavigation/CarPayCompassView.swift index 0b3117a740f..0ad2c12d468 100644 --- a/MapboxNavigation/CarPayCompassView.swift +++ b/MapboxNavigation/CarPayCompassView.swift @@ -6,10 +6,8 @@ import MapboxCoreNavigation /** A control indicating the direction that the vehicle is traveling towards. */ -@objc(MBCarPlayCompassView) open class CarPlayCompassView: StylableView { - - @objc weak var label: StylableLabel! + weak var label: StylableLabel! // Round to closest 45° to only show main winds ["N", "NE", "E", "SE", "S", "SW", "W", "NW"] static let granularity: CLLocationDirection = 360 / 8 @@ -23,7 +21,7 @@ open class CarPlayCompassView: StylableView { /** Sets the course, rounds it to closest 45°, and draws the cardinal direction on the label. */ - @objc open var course: CLLocationDirection = 0 { + open var course: CLLocationDirection = 0 { didSet { if course >= 0 { snappedCourse = course.wrap(min: 0, max: 360) diff --git a/MapboxNavigation/CarPlayManager.swift b/MapboxNavigation/CarPlayManager.swift index 1f2b9206440..877d62e644d 100644 --- a/MapboxNavigation/CarPlayManager.swift +++ b/MapboxNavigation/CarPlayManager.swift @@ -7,7 +7,6 @@ import MapboxDirections The activity during which a `CPTemplate` is displayed. This enumeration is used to distinguish between different templates during different phases of user interaction. */ @available(iOS 12.0, *) -@objc(MBCarPlayActivity) public enum CarPlayActivity: Int { /// The user is browsing the map or searching for a destination. case browsing @@ -27,28 +26,26 @@ public enum CarPlayActivity: Int { - note: It is very important you have a single `CarPlayManager` instance at any given time. This should be managed by your `UIApplicationDelegate` class if you choose to supply your `accessToken` to the `CarPlayManager.eventsManager` via `NavigationEventsManager.init(dataSource:accessToken:mobileEventsManager)`, instead of the Info.plist. */ @available(iOS 12.0, *) -@objc(MBCarPlayManager) public class CarPlayManager: NSObject { - public fileprivate(set) var interfaceController: CPInterfaceController? public fileprivate(set) var carWindow: UIWindow? /** Developers should assign their own object as a delegate implementing the CarPlayManagerDelegate protocol for customization. */ - @objc public weak var delegate: CarPlayManagerDelegate? + public weak var delegate: CarPlayManagerDelegate? /** If set to `true`, turn-by-turn directions will simulate the user traveling along the selected route when initiated from CarPlay. */ - @objc public var simulatesLocations = false + public var simulatesLocations = false private weak var navigationService: NavigationService? /** A multiplier to be applied to the user's speed in simulation mode. */ - @objc public var simulatedSpeedMultiplier = 1.0 { + public var simulatedSpeedMultiplier = 1.0 { didSet { navigationService?.simulationSpeedMultiplier = simulatedSpeedMultiplier } @@ -77,26 +74,26 @@ public class CarPlayManager: NSObject { /** A Boolean value indicating whether the phone is connected to CarPlay. */ - @objc public static var isConnected = false + public static var isConnected = false /** The events manager used during turn-by-turn navigation while connected to CarPlay. */ - @objc public let eventsManager: NavigationEventsManager + public let eventsManager: NavigationEventsManager /** The object that calculates routes when the user interacts with the CarPlay interface. */ - @objc public let directions: Directions + public let directions: Directions - @objc public let navigationViewControllerType: CarPlayNavigationViewController.Type + public let navigationViewControllerType: CarPlayNavigationViewController.Type /** The styles displayed in the CarPlay interface. */ - @objc public var styles: [Style] { + public var styles: [Style] { didSet { if let mapViewController = carPlayMapViewController { mapViewController.styles = styles @@ -108,7 +105,7 @@ public class CarPlayManager: NSObject { /** The view controller for orchestrating the Mapbox map, the interface styles and the map template buttons on CarPlay. */ - @objc public var carPlayMapViewController: CarPlayMapViewController? { + public var carPlayMapViewController: CarPlayMapViewController? { if let mapViewController = carWindow?.rootViewController as? CarPlayMapViewController { return mapViewController } @@ -118,7 +115,7 @@ public class CarPlayManager: NSObject { /** The bar button that exits the navigation session. */ - @objc public lazy var exitButton: CPBarButton = { + public lazy var exitButton: CPBarButton = { let exitButton = CPBarButton(type: .text) { [weak self] (button: CPBarButton) in self?.currentNavigator?.exitNavigation(byCanceling: true) } @@ -129,7 +126,7 @@ public class CarPlayManager: NSObject { /** The bar button that mutes the voice turn-by-turn instruction announcements during navigation. */ - @objc public lazy var muteButton: CPBarButton = { + public lazy var muteButton: CPBarButton = { let muteTitle = NSLocalizedString("CARPLAY_MUTE", bundle: .mapboxNavigation, value: "Mute", comment: "Title for mute button") let unmuteTitle = NSLocalizedString("CARPLAY_UNMUTE", bundle: .mapboxNavigation, value: "Unmute", comment: "Title for unmute button") @@ -144,7 +141,7 @@ public class CarPlayManager: NSObject { /** The bar button that prompts the presented navigation view controller to display the feedback screen. */ - @objc public lazy var showFeedbackButton: CPMapButton = { + public lazy var showFeedbackButton: CPMapButton = { let showFeedbackButton = CPMapButton { button in self.currentNavigator?.showFeedback() } @@ -156,7 +153,7 @@ public class CarPlayManager: NSObject { /** The bar button that shows the selected route overview on the map. */ - @objc public lazy var userTrackingButton: CPMapButton = { + public lazy var userTrackingButton: CPMapButton = { let userTrackingButton = CPMapButton { button in guard let navigationViewController = self.currentNavigator else { return @@ -170,14 +167,14 @@ public class CarPlayManager: NSObject { }() @available(*, deprecated, renamed: "trackingStateButton") - @objc public var overviewButton: CPMapButton { + public var overviewButton: CPMapButton { get { return userTrackingButton } } /** The main map view displayed inside CarPlay. */ - @objc public var mapView: NavigationMapView? { + public var mapView: NavigationMapView? { let mapViewController = carPlayMapViewController return mapViewController?.mapView } @@ -197,21 +194,19 @@ public class CarPlayManager: NSObject { omitted, a standard `NavigationEventsManager` object is used by default. */ - @objc public convenience init(styles: [Style]? = nil, - directions: Directions? = nil, - eventsManager: NavigationEventsManager? = nil) { - + public convenience init(styles: [Style]? = nil, + directions: Directions? = nil, + eventsManager: NavigationEventsManager? = nil) { self.init(styles: styles, - directions: directions, - eventsManager: eventsManager, - navigationViewControllerClass: nil) + directions: directions, + eventsManager: eventsManager, + navigationViewControllerClass: nil) } - - @objc internal init(styles: [Style]? = nil, - directions: Directions? = nil, - eventsManager: NavigationEventsManager? = nil, - navigationViewControllerClass: CarPlayNavigationViewController.Type? = nil) { + internal init(styles: [Style]? = nil, + directions: Directions? = nil, + eventsManager: NavigationEventsManager? = nil, + navigationViewControllerClass: CarPlayNavigationViewController.Type? = nil) { self.styles = styles ?? [DayStyle(), NightStyle()] self.directions = directions ?? .shared self.eventsManager = eventsManager ?? NavigationEventsManager(dataSource: nil) @@ -234,7 +229,7 @@ public class CarPlayManager: NSObject { let route = navigationService.route var trip = CPTrip(routes: [route], routeOptions: route.routeOptions, waypoints: route.routeOptions.waypoints) - trip = delegate?.carPlayManager?(self, willPreview: trip) ?? trip + trip = delegate?.carPlayManager(self, willPreview: trip) ?? trip self.navigationService = navigationService @@ -247,14 +242,12 @@ public class CarPlayManager: NSObject { // MARK: CPApplicationDelegate @available(iOS 12.0, *) extension CarPlayManager: CPApplicationDelegate { - public func application(_ application: UIApplication, didConnectCarInterfaceController interfaceController: CPInterfaceController, to window: CPWindow) { - CarPlayManager.isConnected = true interfaceController.delegate = self self.interfaceController = interfaceController - if let shouldDisableIdleTimer = delegate?.carplayManagerShouldDisableIdleTimer?(self) { + if let shouldDisableIdleTimer = delegate?.carplayManagerShouldDisableIdleTimer(self) { UIApplication.shared.isIdleTimerDisabled = shouldDisableIdleTimer } else { UIApplication.shared.isIdleTimerDisabled = true @@ -279,7 +272,7 @@ extension CarPlayManager: CPApplicationDelegate { eventsManager.sendCarPlayDisconnectEvent() - if let shouldDisableIdleTimer = delegate?.carplayManagerShouldDisableIdleTimer?(self) { + if let shouldDisableIdleTimer = delegate?.carplayManagerShouldDisableIdleTimer(self) { UIApplication.shared.isIdleTimerDisabled = !shouldDisableIdleTimer } else { UIApplication.shared.isIdleTimerDisabled = false @@ -296,15 +289,15 @@ extension CarPlayManager: CPApplicationDelegate { let mapTemplate = CPMapTemplate() mapTemplate.mapDelegate = self - if let leadingButtons = delegate?.carPlayManager?(self, leadingNavigationBarButtonsCompatibleWith: traitCollection, in: mapTemplate, for: .browsing) { + if let leadingButtons = delegate?.carPlayManager(self, leadingNavigationBarButtonsCompatibleWith: traitCollection, in: mapTemplate, for: .browsing) { mapTemplate.leadingNavigationBarButtons = leadingButtons } - if let trailingButtons = delegate?.carPlayManager?(self, trailingNavigationBarButtonsCompatibleWith: traitCollection, in: mapTemplate, for: .browsing) { + if let trailingButtons = delegate?.carPlayManager(self, trailingNavigationBarButtonsCompatibleWith: traitCollection, in: mapTemplate, for: .browsing) { mapTemplate.trailingNavigationBarButtons = trailingButtons } - if let mapButtons = delegate?.carPlayManager?(self, mapButtonsCompatibleWith: traitCollection, in: mapTemplate, for: .browsing) { + if let mapButtons = delegate?.carPlayManager(self, mapButtonsCompatibleWith: traitCollection, in: mapTemplate, for: .browsing) { mapTemplate.mapButtons = mapButtons } else if let mapButtons = self.browsingMapButtons(for: mapTemplate) { mapTemplate.mapButtons = mapButtons @@ -315,8 +308,8 @@ extension CarPlayManager: CPApplicationDelegate { public func resetPanButtons(_ mapTemplate: CPMapTemplate) { if mapTemplate.isPanningInterfaceVisible, let mapViewController = carPlayMapViewController { - if let mapButtons = delegate?.carPlayManager?(self, mapButtonsCompatibleWith: mapViewController.traitCollection, in: mapTemplate, for: .browsing) { - mapTemplate.mapButtons = mapButtons + if let mapButtons = delegate?.carPlayManager(self, mapButtonsCompatibleWith: mapViewController.traitCollection, in: mapTemplate, for: .browsing) { + mapTemplate.mapButtons = mapButtons } else if let mapButtons = self.browsingMapButtons(for: mapTemplate) { mapTemplate.mapButtons = mapButtons } @@ -352,7 +345,6 @@ extension CarPlayManager: CPInterfaceControllerDelegate { public func templateDidAppear(_ template: CPTemplate, animated: Bool) { guard interfaceController?.topTemplate == mainMapTemplate else { return } if template == interfaceController?.rootTemplate, let mapViewController = carPlayMapViewController { - let mapView = mapViewController.mapView mapView.removeRoutes() mapView.removeWaypoints() @@ -372,14 +364,12 @@ extension CarPlayManager: CPInterfaceControllerDelegate { } carPlayMapViewController?.resetCamera(animated: false) - } } @available(iOS 12.0, *) extension CarPlayManager { public func previewRoutes(to destination: Waypoint, completionHandler: @escaping CompletionHandler) { - guard let rootViewController = carPlayMapViewController, let userLocation = rootViewController.mapView.userLocation, let location = userLocation.location else { @@ -412,18 +402,15 @@ extension CarPlayManager { directions.calculate(options, completionHandler: completionHandler) } - internal func didCalculate(_ routes: [Route]?, for routeOptions: RouteOptions, between waypoints: [Waypoint]?, error: NSError?, completionHandler: CompletionHandler) { defer { completionHandler() } - - if let error = error { guard let delegate = delegate, - let alert = delegate.carPlayManager?(self, didFailToFetchRouteBetween: waypoints, options: routeOptions, error: error) else { - return + let alert = delegate.carPlayManager(self, didFailToFetchRouteBetween: waypoints, options: routeOptions, error: error) else { + return } let mapTemplate = interfaceController?.rootTemplate as? CPMapTemplate @@ -437,11 +424,11 @@ extension CarPlayManager { } var trip = CPTrip(routes: routes, routeOptions: routeOptions, waypoints: waypoints) - trip = delegate?.carPlayManager?(self, willPreview: trip) ?? trip + trip = delegate?.carPlayManager(self, willPreview: trip) ?? trip var previewText = defaultTripPreviewTextConfiguration() - if let customPreviewText = delegate?.carPlayManager?(self, willPreview: trip, with: previewText) { + if let customPreviewText = delegate?.carPlayManager(self, willPreview: trip, with: previewText) { previewText = customPreviewText } @@ -469,7 +456,6 @@ extension CarPlayManager { // MARK: CPMapTemplateDelegate @available(iOS 12.0, *) extension CarPlayManager: CPMapTemplateDelegate { - public func mapTemplate(_ mapTemplate: CPMapTemplate, startedTrip trip: CPTrip, using routeChoice: CPRouteChoice) { guard let interfaceController = interfaceController, let carPlayMapViewController = carPlayMapViewController else { @@ -520,21 +506,21 @@ extension CarPlayManager: CPMapTemplateDelegate { mapTemplate.mapDelegate = self if let carPlayMapViewController = carPlayMapViewController, - let mapButtons = delegate?.carPlayManager?(self, mapButtonsCompatibleWith: carPlayMapViewController.traitCollection, in: mapTemplate, for: .navigating) { + let mapButtons = delegate?.carPlayManager(self, mapButtonsCompatibleWith: carPlayMapViewController.traitCollection, in: mapTemplate, for: .navigating) { mapTemplate.mapButtons = mapButtons } else { mapTemplate.mapButtons = [userTrackingButton, showFeedbackButton] } if let rootViewController = carPlayMapViewController, - let leadingButtons = delegate?.carPlayManager?(self, leadingNavigationBarButtonsCompatibleWith: rootViewController.traitCollection, in: mapTemplate, for: .navigating) { + let leadingButtons = delegate?.carPlayManager(self, leadingNavigationBarButtonsCompatibleWith: rootViewController.traitCollection, in: mapTemplate, for: .navigating) { mapTemplate.leadingNavigationBarButtons = leadingButtons } else { mapTemplate.leadingNavigationBarButtons.insert(muteButton, at: 0) } if let rootViewController = carPlayMapViewController, - let trailingButtons = delegate?.carPlayManager?(self, trailingNavigationBarButtonsCompatibleWith: rootViewController.traitCollection, in: mapTemplate, for: .navigating) { + let trailingButtons = delegate?.carPlayManager(self, trailingNavigationBarButtonsCompatibleWith: rootViewController.traitCollection, in: mapTemplate, for: .navigating) { mapTemplate.trailingNavigationBarButtons = trailingButtons } else { mapTemplate.trailingNavigationBarButtons.append(exitButton) @@ -562,7 +548,7 @@ extension CarPlayManager: CPMapTemplateDelegate { mapView.showcase([route]) - delegate?.carPlayManager?(self, selectedPreviewFor: trip, using: routeChoice) + delegate?.carPlayManager(self, selectedPreviewFor: trip, using: routeChoice) } public func mapTemplateDidCancelNavigation(_ mapTemplate: CPMapTemplate) { @@ -601,7 +587,7 @@ extension CarPlayManager: CPMapTemplateDelegate { return } - if let mapButtons = delegate?.carPlayManager?(self, mapButtonsCompatibleWith: carPlayMapViewController.traitCollection, in: mapTemplate, for: .panningInBrowsingMode) { + if let mapButtons = delegate?.carPlayManager(self, mapButtonsCompatibleWith: carPlayMapViewController.traitCollection, in: mapTemplate, for: .panningInBrowsingMode) { mapTemplate.mapButtons = mapButtons } else { let closeButton = carPlayMapViewController.dismissPanningButton ?? carPlayMapViewController.panningInterfaceDismissalButton(for: mapTemplate) @@ -668,7 +654,6 @@ extension CarPlayManager: CPMapTemplateDelegate { let shiftedCenterCoordinate = mapView.centerCoordinate.coordinate(at: distance, facing: shiftedDirection) mapView.setCenter(shiftedCenterCoordinate, animated: true) } - } // MARK: CarPlayNavigationDelegate @@ -686,37 +671,34 @@ extension CarPlayManager: CarPlayNavigationDelegate { @available(iOS 12.0, *) extension CarPlayManager: MapTemplateProviderDelegate { func mapTemplateProvider(_ provider: MapTemplateProvider, mapTemplate: CPMapTemplate, leadingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, for activity: CarPlayActivity) -> [CPBarButton]? { - return delegate?.carPlayManager?(self, leadingNavigationBarButtonsCompatibleWith: traitCollection, in: mapTemplate, for: activity) + return delegate?.carPlayManager(self, leadingNavigationBarButtonsCompatibleWith: traitCollection, in: mapTemplate, for: activity) } func mapTemplateProvider(_ provider: MapTemplateProvider, mapTemplate: CPMapTemplate, trailingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, for activity: CarPlayActivity) -> [CPBarButton]? { - return delegate?.carPlayManager?(self, trailingNavigationBarButtonsCompatibleWith: traitCollection, in: mapTemplate, for: activity) + return delegate?.carPlayManager(self, trailingNavigationBarButtonsCompatibleWith: traitCollection, in: mapTemplate, for: activity) } } @available(iOS 12.0, *) -@objc(MBMapTemplateProviderDelegate) -internal protocol MapTemplateProviderDelegate { - @objc optional func mapTemplateProvider(_ provider: MapTemplateProvider, mapTemplate: CPMapTemplate, leadingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, for activity: CarPlayActivity) -> [CPBarButton]? +internal protocol MapTemplateProviderDelegate: class { + func mapTemplateProvider(_ provider: MapTemplateProvider, mapTemplate: CPMapTemplate, leadingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, for activity: CarPlayActivity) -> [CPBarButton]? - @objc optional func mapTemplateProvider(_ provider: MapTemplateProvider, mapTemplate: CPMapTemplate, trailingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, for activity: CarPlayActivity) -> [CPBarButton]? + func mapTemplateProvider(_ provider: MapTemplateProvider, mapTemplate: CPMapTemplate, trailingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, for activity: CarPlayActivity) -> [CPBarButton]? } @available(iOS 12.0, *) internal class MapTemplateProvider: NSObject { - weak var delegate: MapTemplateProviderDelegate? func mapTemplate(forPreviewing trip: CPTrip, traitCollection: UITraitCollection, mapDelegate: CPMapTemplateDelegate) -> CPMapTemplate { - let mapTemplate = createMapTemplate() mapTemplate.mapDelegate = mapDelegate - if let leadingButtons = delegate?.mapTemplateProvider?(self, mapTemplate: mapTemplate, leadingNavigationBarButtonsCompatibleWith: traitCollection, for: .previewing) { + if let leadingButtons = delegate?.mapTemplateProvider(self, mapTemplate: mapTemplate, leadingNavigationBarButtonsCompatibleWith: traitCollection, for: .previewing) { mapTemplate.leadingNavigationBarButtons = leadingButtons } - if let trailingButtons = delegate?.mapTemplateProvider?(self, mapTemplate: mapTemplate, trailingNavigationBarButtonsCompatibleWith: traitCollection, for: .previewing) { + if let trailingButtons = delegate?.mapTemplateProvider(self, mapTemplate: mapTemplate, trailingNavigationBarButtonsCompatibleWith: traitCollection, for: .previewing) { mapTemplate.trailingNavigationBarButtons = trailingButtons } @@ -732,11 +714,10 @@ internal class MapTemplateProvider: NSObject { /** CarPlay support requires iOS 12.0 or above and the CarPlay framework. */ -@objc(MBCarPlayManager) public class CarPlayManager: NSObject { /** A Boolean value indicating whether the phone is connected to CarPlay. */ - @objc public static var isConnected = false + public static var isConnected = false } #endif diff --git a/MapboxNavigation/CarPlayManagerDelegate.swift b/MapboxNavigation/CarPlayManagerDelegate.swift index 826fef2d6d6..b62c54d0361 100644 --- a/MapboxNavigation/CarPlayManagerDelegate.swift +++ b/MapboxNavigation/CarPlayManagerDelegate.swift @@ -12,9 +12,7 @@ import MapboxDirections If no delegate is set, a default built-in MapboxNavigationService will be created and used when a trip begins. */ @available(iOS 12.0, *) -@objc(MBCarPlayManagerDelegate) -public protocol CarPlayManagerDelegate { - +public protocol CarPlayManagerDelegate: class, UnimplementedLogging { /** Offers the delegate an opportunity to provide a customized list of leading bar buttons at the root of the template stack for the given activity. @@ -26,8 +24,7 @@ public protocol CarPlayManagerDelegate { - parameter activity: What the user is currently doing on the CarPlay screen. Use this parameter to distinguish between multiple templates of the same kind, such as multiple `CPMapTemplate`s. - returns: An array of bar buttons to display on the leading side of the navigation bar while `template` is visible. */ - @objc(carPlayManager:leadingNavigationBarButtonsWithTraitCollection:inTemplate:forActivity:) - optional func carPlayManager(_ carPlayManager: CarPlayManager, leadingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, in carPlayTemplate: CPTemplate, for activity: CarPlayActivity) -> [CPBarButton]? + func carPlayManager(_ carPlayManager: CarPlayManager, leadingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, in carPlayTemplate: CPTemplate, for activity: CarPlayActivity) -> [CPBarButton]? /** Offers the delegate an opportunity to provide a customized list of trailing bar buttons at the root of the template stack for the given activity. @@ -39,9 +36,9 @@ public protocol CarPlayManagerDelegate { - parameter carPlayTemplate: The template into which the returned bar buttons will be inserted. - parameter activity: What the user is currently doing on the CarPlay screen. Use this parameter to distinguish between multiple templates of the same kind, such as multiple `CPMapTemplate`s. - returns: An array of bar buttons to display on the trailing side of the navigation bar while `template` is visible. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(carPlayManager:trailingNavigationBarButtonsWithTraitCollection:inTemplate:forActivity:) - optional func carPlayManager(_ carPlayManager: CarPlayManager, trailingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, in carPlayTemplate: CPTemplate, for activity: CarPlayActivity) -> [CPBarButton]? + func carPlayManager(_ carPlayManager: CarPlayManager, trailingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, in carPlayTemplate: CPTemplate, for activity: CarPlayActivity) -> [CPBarButton]? /** Offers the delegate an opportunity to provide a customized list of buttons displayed on the map. @@ -54,9 +51,9 @@ public protocol CarPlayManagerDelegate { - parameter carPlayTemplate: The template into which the returned map buttons will be inserted. - parameter activity: What the user is currently doing on the CarPlay screen. Use this parameter to distinguish between multiple templates of the same kind, such as multiple `CPMapTemplate`s. - returns: An array of map buttons to display on the map while `template` is visible. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(carPlayManager:mapButtonsCompatibleWithTraitCollection:inTemplate:forActivity:) - optional func carPlayManager(_ carPlayManager: CarPlayManager, mapButtonsCompatibleWith traitCollection: UITraitCollection, in carPlayTemplate: CPTemplate, for activity: CarPlayActivity) -> [CPMapButton]? + func carPlayManager(_ carPlayManager: CarPlayManager, mapButtonsCompatibleWith traitCollection: UITraitCollection, in carPlayTemplate: CPTemplate, for activity: CarPlayActivity) -> [CPMapButton]? /** Asks the delegate to provide a navigation service. In multi-screen applications this should be the same instance used to guide the user along the route on the phone. @@ -65,9 +62,8 @@ public protocol CarPlayManagerDelegate { - parameter route: The route for which the returned route controller will manage location updates. - parameter desiredSimulationMode: The desired simulation mode to use. - returns: A navigation service that manages location updates along `route`. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - - @objc(carPlayManager:navigationServiceAlongRoute:desiredSimulationMode:) func carPlayManager(_ carPlayManager: CarPlayManager, navigationServiceAlong route: Route, desiredSimulationMode: SimulationMode) -> NavigationService /** @@ -77,11 +73,11 @@ public protocol CarPlayManagerDelegate { - parameter searchTemplate: The search template currently accepting user input. - parameter searchText: The updated search text in `searchTemplate`. - parameter completionHandler: Called when the search is complete. Accepts a list of search results. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. - postcondition: You must call `completionHandler` within this method. */ - @objc(carPlayManager:searchTemplate:updatedSearchText:completionHandler:) - optional func carPlayManager(_ carPlayManager: CarPlayManager, searchTemplate: CPSearchTemplate, updatedSearchText searchText: String, completionHandler: @escaping ([CPListItem]) -> Void) + func carPlayManager(_ carPlayManager: CarPlayManager, searchTemplate: CPSearchTemplate, updatedSearchText searchText: String, completionHandler: @escaping ([CPListItem]) -> Void) /** Offers the delegate an opportunity to react to selection of a search result. @@ -90,11 +86,11 @@ public protocol CarPlayManagerDelegate { - parameter searchTemplate: The search template currently accepting user input. - parameter item: The search result the user has selected. - parameter completionHandler: Called when the delegate is done responding to the selection. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. - postcondition: You must call `completionHandler` within this method. */ - @objc(carPlayManager:searchTemplate:selectedResult:completionHandler:) - optional func carPlayManager(_ carPlayManager: CarPlayManager, searchTemplate: CPSearchTemplate, selectedResult item: CPListItem, completionHandler: @escaping () -> Void) + func carPlayManager(_ carPlayManager: CarPlayManager, searchTemplate: CPSearchTemplate, selectedResult item: CPListItem, completionHandler: @escaping () -> Void) /** Called when the CarPlay manager fails to fetch a route. @@ -102,11 +98,10 @@ public protocol CarPlayManagerDelegate { - parameter waypoints: the waypoints for which a route could not be retrieved. - parameter options: The route options that were attached to the route request. - parameter error: The error returned from the directions API. - - - returns Optionally, a CPNavigationAlert to present to the user. If an alert is returned, Carplay will transition back to the map template and display the alert. If `nil` is returned, nothing is done. + - returns: Optionally, a `CPNavigationAlert` to present to the user. If this method returns an alert, the CarPlay manager will transition back to the map template and display the alert. If it returns `nil`, the CarPlay manager will do nothing. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(carPlayManager:didFailToFetchRouteBetweenWaypoints:withOptions:becauseOfError:) - optional func carPlayManager(_ carPlayManager: CarPlayManager, didFailToFetchRouteBetween waypoints: [Waypoint]?, options: RouteOptions, error: NSError) -> CPNavigationAlert? + func carPlayManager(_ carPlayManager: CarPlayManager, didFailToFetchRouteBetween waypoints: [Waypoint]?, options: RouteOptions, error: NSError) -> CPNavigationAlert? /** Offers the delegate the opportunity to customize a trip before it is presented to the user to preview. @@ -116,9 +111,9 @@ public protocol CarPlayManagerDelegate { - parameter carPlayManager: The CarPlay manager instance. - parameter trip: The trip that will be previewed. - returns: The actual trip to be previewed. This can be the same trip or a new/alternate trip if desired. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(carPlayManager:willPreviewTrip:) - optional func carPlayManager(_ carPlayManager: CarPlayManager, willPreview trip: CPTrip) -> (CPTrip) + func carPlayManager(_ carPlayManager: CarPlayManager, willPreview trip: CPTrip) -> (CPTrip) /** Offers the delegate the opportunity to customize a trip preview text configuration for a given trip. @@ -127,9 +122,9 @@ public protocol CarPlayManagerDelegate { - parameter trip: The trip that will be previewed. - parameter previewTextConfiguration: The trip preview text configuration that will be presented alongside the trip. - returns: The actual preview text configuration to be presented alongside the trip. - */ - @objc(carPlayManager:willPreviewTrip:withPreviewTextConfiguration:) - optional func carPlayManager(_ carPlayManager: CarPlayManager, willPreview trip: CPTrip, with previewTextConfiguration: CPTripPreviewTextConfiguration) -> (CPTripPreviewTextConfiguration) + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. + */ + func carPlayManager(_ carPlayManager: CarPlayManager, willPreview trip: CPTrip, with previewTextConfiguration: CPTripPreviewTextConfiguration) -> (CPTripPreviewTextConfiguration) /** Offers the delegate the opportunity to react to selection of a trip. Certain trips may have alternate route(s). @@ -137,25 +132,26 @@ public protocol CarPlayManagerDelegate { - parameter carPlayManager: The CarPlay manager instance. - parameter trip: The trip to begin navigating along. - parameter routeChoice: The possible route for the chosen trip. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(carPlayManager:selectedPreviewForTrip:usingRouteChoice:) - optional func carPlayManager(_ carPlayManager: CarPlayManager, selectedPreviewFor trip: CPTrip, using routeChoice: CPRouteChoice) -> () + func carPlayManager(_ carPlayManager: CarPlayManager, selectedPreviewFor trip: CPTrip, using routeChoice: CPRouteChoice) -> () /** Called when navigation begins so that the containing app can update accordingly. - parameter carPlayManager: The CarPlay manager instance. - parameter service: The navigation service that has begun managing location updates for a navigation session. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(carPlayManager:didBeginNavigationWithNavigationService:) func carPlayManager(_ carPlayManager: CarPlayManager, didBeginNavigationWith service: NavigationService) -> () /** Called when navigation ends so that the containing app can update accordingly. - parameter carPlayManager: The CarPlay manager instance. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc func carPlayManagerDidEndNavigation(_ carPlayManager: CarPlayManager) -> () + func carPlayManagerDidEndNavigation(_ carPlayManager: CarPlayManager) -> () /** Called when the carplay manager will disable the idle timer. @@ -164,7 +160,66 @@ public protocol CarPlayManagerDelegate { - parameter carPlayManager: The CarPlay manager instance. - returns: A Boolean value indicating whether to disable idle timer when carplay is connected and enable when disconnected. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func carplayManagerShouldDisableIdleTimer(_ carPlayManager: CarPlayManager) -> Bool + func carplayManagerShouldDisableIdleTimer(_ carPlayManager: CarPlayManager) -> Bool +} + +@available(iOS 12.0, *) +public extension CarPlayManagerDelegate { + func carPlayManager(_ carPlayManager: CarPlayManager, leadingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, in carPlayTemplate: CPTemplate, for activity: CarPlayActivity) -> [CPBarButton]? { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + return nil + } + + func carPlayManager(_ carPlayManager: CarPlayManager, trailingNavigationBarButtonsCompatibleWith traitCollection: UITraitCollection, in carPlayTemplate: CPTemplate, for activity: CarPlayActivity) -> [CPBarButton]? { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + return nil + } + + func carPlayManager(_ carPlayManager: CarPlayManager, mapButtonsCompatibleWith traitCollection: UITraitCollection, in carPlayTemplate: CPTemplate, for activity: CarPlayActivity) -> [CPMapButton]? { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + return nil + } + + func carPlayManager(_ carPlayManager: CarPlayManager, searchTemplate: CPSearchTemplate, updatedSearchText searchText: String, completionHandler: @escaping ([CPListItem]) -> Void) { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + } + + func carPlayManager(_ carPlayManager: CarPlayManager, searchTemplate: CPSearchTemplate, selectedResult item: CPListItem, completionHandler: @escaping () -> Void) { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + } + + func carPlayManager(_ carPlayManager: CarPlayManager, didFailToFetchRouteBetween waypoints: [Waypoint]?, options: RouteOptions, error: NSError) -> CPNavigationAlert? { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + return nil + } + + func carPlayManager(_ carPlayManager: CarPlayManager, willPreview trip: CPTrip) -> (CPTrip) { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + return trip + } + + func carPlayManager(_ carPlayManager: CarPlayManager, willPreview trip: CPTrip, with previewTextConfiguration: CPTripPreviewTextConfiguration) -> (CPTripPreviewTextConfiguration) { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + return previewTextConfiguration + } + + func carPlayManager(_ carPlayManager: CarPlayManager, selectedPreviewFor trip: CPTrip, using routeChoice: CPRouteChoice) { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + } + + func carPlayManager(_ carPlayManager: CarPlayManager, didBeginNavigationWith service: NavigationService) { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + } + + func carPlayManagerDidEndNavigation(_ carPlayManager: CarPlayManager) { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + } + + func carplayManagerShouldDisableIdleTimer(_ carPlayManager: CarPlayManager) -> Bool { + logUnimplemented(protocolType: CarPlayManagerDelegate.self, level: .debug) + return false + } } #endif diff --git a/MapboxNavigation/CarPlayMapViewController.swift b/MapboxNavigation/CarPlayMapViewController.swift index 5ffe837db74..3526db98a4c 100644 --- a/MapboxNavigation/CarPlayMapViewController.swift +++ b/MapboxNavigation/CarPlayMapViewController.swift @@ -6,9 +6,7 @@ import CarPlay `CarPlayMapViewController` is responsible for administering the Mapbox map, the interface styles and the map template buttons to display on CarPlay. */ @available(iOS 12.0, *) -@objc(MBCarPlayMapViewController) public class CarPlayMapViewController: UIViewController { - static let defaultAltitude: CLLocationDistance = 850 var styleManager: StyleManager? @@ -46,9 +44,8 @@ public class CarPlayMapViewController: UIViewController { /** The map button for recentering the map view if a user action causes it to stop following the user. */ - @objc public lazy var recenterButton: CPMapButton = { + public lazy var recenterButton: CPMapButton = { let recenter = CPMapButton { [weak self] button in - self?.mapView.setUserTrackingMode(.followWithCourse, animated: true, completionHandler: nil) button.isHidden = true } @@ -60,7 +57,7 @@ public class CarPlayMapViewController: UIViewController { /** The map button for zooming in the current map view. */ - @objc public lazy var zoomInButton: CPMapButton = { + public lazy var zoomInButton: CPMapButton = { let zoomInButton = CPMapButton { [weak self] (button) in let zoomLevel = self?.mapView.zoomLevel ?? 0 self?.mapView.setZoomLevel(zoomLevel + 1, animated: true) @@ -73,7 +70,7 @@ public class CarPlayMapViewController: UIViewController { /** The map button for zooming out the current map view. */ - @objc public lazy var zoomOutButton: CPMapButton = { + public lazy var zoomOutButton: CPMapButton = { let zoomOutButton = CPMapButton { [weak self] (button) in guard let strongSelf = self else { return @@ -88,12 +85,12 @@ public class CarPlayMapViewController: UIViewController { /** The map button property for hiding or showing the pan map button. */ - @objc internal(set) public var panMapButton: CPMapButton? + internal(set) public var panMapButton: CPMapButton? /** The map button property for exiting the pan map mode. */ - @objc internal(set) public var dismissPanningButton: CPMapButton? + internal(set) public var dismissPanningButton: CPMapButton? var styleObservation: NSKeyValueObservation? @@ -218,12 +215,10 @@ public class CarPlayMapViewController: UIViewController { @available(iOS 12.0, *) extension CarPlayMapViewController: StyleManagerDelegate { - @objc(locationForStyleManager:) public func location(for styleManager: StyleManager) -> CLLocation? { return mapView.userLocationForCourseTracking ?? mapView.userLocation?.location ?? coarseLocationManager.location } - @objc(styleManager:didApplyStyle:) public func styleManager(_ styleManager: StyleManager, didApply style: Style) { let styleURL = style.previewMapStyleURL if mapView.styleURL != styleURL { @@ -232,7 +227,7 @@ extension CarPlayMapViewController: StyleManagerDelegate { } } - @objc public func styleManagerDidRefreshAppearance(_ styleManager: StyleManager) { + public func styleManagerDidRefreshAppearance(_ styleManager: StyleManager) { mapView.reloadStyle(self) } } diff --git a/MapboxNavigation/CarPlayNavigationViewController.swift b/MapboxNavigation/CarPlayNavigationViewController.swift index 38e433a8f8c..2ffd4415e58 100644 --- a/MapboxNavigation/CarPlayNavigationViewController.swift +++ b/MapboxNavigation/CarPlayNavigationViewController.swift @@ -10,37 +10,35 @@ import CarPlay - seealso: NavigationViewController */ @available(iOS 12.0, *) -@objc(MBCarPlayNavigationViewController) public class CarPlayNavigationViewController: UIViewController, NavigationMapViewDelegate { /** The view controller’s delegate. */ - @objc public weak var carPlayNavigationDelegate: CarPlayNavigationDelegate? { + public weak var carPlayNavigationDelegate: CarPlayNavigationDelegate? { didSet { if let carPlayNavigationDelegate = carPlayNavigationDelegate as? NSObjectProtocol { // This rigamarole avoids a compiler error when using a #selector literal, as well as a compiler warning when calling the Selector(_:) initializer with a string literal. let carPlayNavigationViewControllerDidArrive = Selector(("carPlayNavigationViewControllerDidArrive:" as NSString) as String) - assert(!carPlayNavigationDelegate.responds(to: carPlayNavigationViewControllerDidArrive), "CarPlayNavigationDelegate.carPlayNavigationViewControllerDidArrive(_:) has been removed. Use NavigationViewControllerDelegate.navigationViewController(_:didArriveAt:) or NavigationServiceDelegate.navigationService(_:didArriveAt:) instead.") + assert(!carPlayNavigationDelegate.responds(to: carPlayNavigationViewControllerDidArrive), "CarPlayNavigationDelegate.carPlayNavigationViewControllerDidArrive(_:) has been removed. Use NavigationViewControllerDelegate.navigationViewController(_:didArriveAt:) or NavigationServiceDelegate.navigationService(_:didArriveAt:) instead.") } } } public var carPlayManager: CarPlayManager - @objc public var drivingSide: DrivingSide = .right - + public var drivingSide: DrivingSide = .right /** Provides all routing logic for the user. See `NavigationService` for more information. */ - @objc public var navigationService: NavigationService + public var navigationService: NavigationService /** The map view showing the route and the user’s location. */ - @objc public fileprivate(set) var mapView: NavigationMapView? + public fileprivate(set) var mapView: NavigationMapView? let shieldHeight: CGFloat = 16 @@ -52,21 +50,20 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie var carInterfaceController: CPInterfaceController var styleManager: StyleManager? - /** A view indicating what direction the vehicle is traveling towards, snapped to eight cardinal directions in steps of 45°. This view is hidden by default. */ - @objc weak public var compassView: CarPlayCompassView! + weak public var compassView: CarPlayCompassView! /** The interface styles available for display. These are the styles available to the view controller’s internal `StyleManager` object. In CarPlay, `Style` objects primarily affect the appearance of the map, not guidance-related overlay views. */ - @objc public var styles: [Style] { + public var styles: [Style] { didSet { styleManager?.styles = styles } @@ -93,11 +90,11 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie - postcondition: Call `startNavigationSession(for:)` after initializing this object to begin navigation. */ - @objc required public init(navigationService: NavigationService, - mapTemplate: CPMapTemplate, - interfaceController: CPInterfaceController, - manager: CarPlayManager, - styles: [Style]? = nil) { + required public init(navigationService: NavigationService, + mapTemplate: CPMapTemplate, + interfaceController: CPInterfaceController, + manager: CarPlayManager, + styles: [Style]? = nil) { self.navigationService = navigationService self.mapTemplate = mapTemplate self.carInterfaceController = interfaceController @@ -201,7 +198,7 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie var contentFrame = mapView.bounds.inset(by: insets) // Avoid letting the puck go partially off-screen, and add a comfortable padding beyond that. - let courseViewBounds = mapView.userCourseView?.bounds ?? .zero + let courseViewBounds = mapView.userCourseView.bounds // If it is not possible to position it right above the content area, center it at the remaining space. contentFrame = contentFrame.insetBy(dx: min(NavigationMapView.courseViewMinimumInsets.left + courseViewBounds.width / 2.0, contentFrame.width / 2.0), dy: min(NavigationMapView.courseViewMinimumInsets.top + courseViewBounds.height / 2.0, contentFrame.height / 2.0)) @@ -219,7 +216,6 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie - parameter trip: The trip to begin navigating along. */ - @objc(startNavigationSessionForTrip:) public func startNavigationSession(for trip: CPTrip) { carSession = mapTemplate.startNavigationSession(for: trip) } @@ -229,18 +225,17 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie - parameter canceled: A Boolean value indicating whether this method is being called because the user intends to cancel the trip, as opposed to letting it run to completion. */ - @objc(exitNavigationByCanceling:) public func exitNavigation(byCanceling canceled: Bool = false) { carSession.finishTrip() dismiss(animated: true) { - self.carPlayNavigationDelegate?.carPlayNavigationViewControllerDidDismiss?(self, byCanceling: canceled) + self.carPlayNavigationDelegate?.carPlayNavigationViewControllerDidDismiss(self, byCanceling: canceled) } } /** Shows the interface for providing feedback about the route. */ - @objc public func showFeedback() { + public func showFeedback() { carInterfaceController.pushTemplate(self.carFeedbackTemplate, animated: true) } @@ -259,8 +254,8 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie isOverviewingRoutes = false mapView?.recenterMap() mapView?.addArrow(route: progress.route, - legIndex: progress.legIndex, - stepIndex: progress.currentLegProgress.stepIndex + 1) + legIndex: progress.legIndex, + stepIndex: progress.currentLegProgress.stepIndex + 1) mapView?.setContentInset(contentInset(forOverviewing: false), animated: true, completionHandler: nil) } else if tracksUserCourse && !newValue { isOverviewingRoutes = !isPanningAway @@ -293,7 +288,7 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie @objc func visualInstructionDidChange(_ notification: NSNotification) { let routeProgress = notification.userInfo![RouteControllerNotificationUserInfoKey.routeProgressKey] as! RouteProgress updateManeuvers(for: routeProgress) - mapView?.showWaypoints(routeProgress.route) + mapView?.showWaypoints(on: routeProgress.route) mapView?.addArrow(route: routeProgress.route, legIndex: routeProgress.legIndex, stepIndex: routeProgress.currentLegProgress.stepIndex + 1) } @@ -347,8 +342,8 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie let nextStep = progress.currentLegProgress.stepIndex + 1 // look forward twoards the next step map.addArrow(route: progress.route, legIndex: legIndex, stepIndex: nextStep) - map.showRoutes([progress.route], legIndex: legIndex) - map.showWaypoints(progress.route, legIndex: legIndex) + map.show([progress.route], legIndex: legIndex) + map.showWaypoints(on: progress.route, legIndex: legIndex) } func updateManeuvers(for routeProgress: RouteProgress) { @@ -456,7 +451,6 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie func endOfRouteFeedbackTemplate() -> CPGridTemplate { let buttonHandler: (_: CPGridButton) -> Void = { [weak self] (button) in - let title: String? = button.titleVariants.first ?? nil let rating: Int? = title != nil ? Int(title!.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()) : nil let feedback: EndOfRouteFeedback? = rating != nil ? EndOfRouteFeedback(rating: rating, comment: nil) : nil @@ -496,7 +490,6 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie @available(iOS 12.0, *) extension CarPlayNavigationViewController: StyleManagerDelegate { - @objc(locationForStyleManager:) public func location(for styleManager: StyleManager) -> CLLocation? { if let location = navigationService.router.location { return location @@ -507,7 +500,6 @@ extension CarPlayNavigationViewController: StyleManagerDelegate { } } - @objc(styleManager:didApplyStyle:) public func styleManager(_ styleManager: StyleManager, didApply style: Style) { if mapView?.styleURL != style.mapStyleURL { mapView?.style?.transition = MGLTransition(duration: 0.5, delay: 0) @@ -515,7 +507,7 @@ extension CarPlayNavigationViewController: StyleManagerDelegate { } } - @objc public func styleManagerDidRefreshAppearance(_ styleManager: StyleManager) { + public func styleManagerDidRefreshAppearance(_ styleManager: StyleManager) { mapView?.reloadStyle(self) } } @@ -524,20 +516,30 @@ extension CarPlayNavigationViewController: StyleManagerDelegate { The `CarPlayNavigationDelegate` protocol provides methods for reacting to significant events during turn-by-turn navigation with `CarPlayNavigationViewController`. */ @available(iOS 12.0, *) -@objc(MBNavigationCarPlayDelegate) -public protocol CarPlayNavigationDelegate { +public protocol CarPlayNavigationDelegate: class, UnimplementedLogging { /** Called when the CarPlay navigation view controller is dismissed, such as when the user ends a trip. - parameter carPlayNavigationViewController: The CarPlay navigation view controller that was dismissed. - parameter canceled: True if the user dismissed the CarPlay navigation view controller by tapping the Cancel button; false if the navigation view controller dismissed by some other means. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(carPlayNavigationViewControllerDidDismiss:byCanceling:) - optional func carPlayNavigationViewControllerDidDismiss(_ carPlayNavigationViewController: CarPlayNavigationViewController, byCanceling canceled: Bool) + func carPlayNavigationViewControllerDidDismiss(_ carPlayNavigationViewController: CarPlayNavigationViewController, byCanceling canceled: Bool) //MARK: - Deprecated. - @available(*, deprecated, message: "Use NavigationViewControllerDelegate.navigationViewController(_:didArriveAt:) or NavigationServiceDelegate.navigationService(_:didArriveAt:) instead.") - @objc optional func carPlayNavigationViewControllerDidArrive(_ carPlayNavigationViewController: CarPlayNavigationViewController) + @available(*, deprecated, message: "Use NavigationViewControllerDelegate.navigationViewController(_:didArriveAt:) or NavigationServiceDelegate.navigationService(_:didArriveAt:) instead.") + func carPlayNavigationViewControllerDidArrive(_ carPlayNavigationViewController: CarPlayNavigationViewController) +} + +@available(iOS 12.0, *) +public extension CarPlayNavigationDelegate { + func carPlayNavigationViewControllerDidDismiss(_ carPlayNavigationViewController: CarPlayNavigationViewController, byCanceling canceled: Bool) { + logUnimplemented(protocolType: CarPlayNavigationDelegate.self, level: .debug) + } + + func carPlayNavigationViewControllerDidArrive(_ carPlayNavigationViewController: CarPlayNavigationViewController) { + //no-op, deprecated method + } } #endif diff --git a/MapboxNavigation/CarPlaySearchController+ CPSearchTemplateDelegate.swift b/MapboxNavigation/CarPlaySearchController+ CPSearchTemplateDelegate.swift index 3bfed85bdd3..2196ec6c5a1 100644 --- a/MapboxNavigation/CarPlaySearchController+ CPSearchTemplateDelegate.swift +++ b/MapboxNavigation/CarPlaySearchController+ CPSearchTemplateDelegate.swift @@ -6,7 +6,6 @@ import MapboxDirections @available(iOS 12.0, *) extension CarPlaySearchController: CPSearchTemplateDelegate { - static var recentItems = RecentItem.loadDefaults() public static let CarPlayGeocodedPlacemarkKey: String = "MBGecodedPlacemark" @@ -20,7 +19,6 @@ extension CarPlaySearchController: CPSearchTemplateDelegate { }() public func searchTemplate(_ searchTemplate: CPSearchTemplate, updatedSearchText searchText: String, completionHandler: @escaping ([CPListItem]) -> Void) { - recentSearchText = searchText // Append recent searches @@ -29,7 +27,6 @@ extension CarPlaySearchController: CPSearchTemplateDelegate { // Search for placemarks using MapboxGeocoder.swift let shouldSearch = searchText.count > 2 if shouldSearch { - let options = CarPlaySearchController.forwardGeocodeOptions(searchText) Geocoder.shared.geocode(options, completionHandler: { [weak self] (placemarks, attribution, error) in guard let strongSelf = self else { return } @@ -42,7 +39,6 @@ extension CarPlaySearchController: CPSearchTemplateDelegate { items.append(contentsOf: results) completionHandler(strongSelf.resultsOrNoResults(results, limit: CarPlaySearchController.MaximumInitialSearchResults)) }) - } else { completionHandler(resultsOrNoResults(items, limit: CarPlaySearchController.MaximumInitialSearchResults)) } @@ -74,7 +70,6 @@ extension CarPlaySearchController: CPSearchTemplateDelegate { } public func searchTemplateButton(searchTemplate: CPSearchTemplate, interfaceController: CPInterfaceController, traitCollection: UITraitCollection) -> CPBarButton { - let searchTemplateButton = CPBarButton(type: .image) { [weak self] button in guard let strongSelf = self else { return @@ -84,7 +79,6 @@ extension CarPlaySearchController: CPSearchTemplateDelegate { strongSelf.delegate?.resetPanButtons(mapTemplate) } - self?.delegate?.pushTemplate(searchTemplate, animated: false) } @@ -109,7 +103,6 @@ extension CarPlaySearchController: CPSearchTemplateDelegate { @available(iOS 12.0, *) public func selectResult(item: CPListItem, completionHandler: @escaping () -> Void) { - guard let userInfo = item.userInfo as? [String: Any], let placemark = userInfo[CarPlaySearchController.CarPlayGeocodedPlacemarkKey] as? GeocodedPlacemark, let location = placemark.routableLocations?.first ?? placemark.location else { @@ -166,7 +159,6 @@ extension CarPlaySearchController: CPListTemplateDelegate { } extension GeocodedPlacemark { - @available(iOS 12.0, *) func listItem() -> CPListItem { let item = CPListItem(text: formattedName, detailText: subtitle, image: nil, showsDisclosureIndicator: true) diff --git a/MapboxNavigation/CarPlaySearchController.swift b/MapboxNavigation/CarPlaySearchController.swift index ab239ca5e29..c869e18f58d 100644 --- a/MapboxNavigation/CarPlaySearchController.swift +++ b/MapboxNavigation/CarPlaySearchController.swift @@ -18,9 +18,7 @@ public protocol CarPlaySearchControllerDelegate: class { - note: It is very important you have a single `CarPlaySearchController` instance at any given time. */ @available(iOS 12.0, *) -@objc(MBCarPlaySearchController) public class CarPlaySearchController: NSObject { - /** The completion handler that will process the list of search results initiated on CarPlay. */ @@ -40,14 +38,10 @@ public class CarPlaySearchController: NSObject { The `CarPlaySearchController` delegate. */ public weak var delegate: CarPlaySearchControllerDelegate? - } #else /** CarPlay support requires iOS 12.0 or above and the CarPlay framework. */ -@objc(MBCarPlaySearchController) -public class CarPlaySearchController: NSObject { - -} +public class CarPlaySearchController: NSObject {} #endif diff --git a/MapboxNavigation/Constants.swift b/MapboxNavigation/Constants.swift index 9c4c77b7021..f96a5d176df 100644 --- a/MapboxNavigation/Constants.swift +++ b/MapboxNavigation/Constants.swift @@ -37,12 +37,39 @@ public let MBCongestionAttribute = "congestion" public let NavigationViewMinimumVolumeForWarning: Float = 0.3 extension Notification.Name { - /** Posted when `StyleManager` applies a style that was triggered by change of time of day, or when entering or exiting a tunnel. This notification is the equivalent of `StyleManagerDelegate.styleManager(_:didApply:)`. The user info dictionary contains the key `StyleManagerNotificationUserInfoKey.style` and `StyleManagerNotificationUserInfoKey.styleManager`. */ - public static let styleManagerDidApplyStyle = NSNotification.Name.MBStyleManagerDidApplyStyle + public static let styleManagerDidApplyStyle: Notification.Name = .init(rawValue: "MBStyleManagerDidApplyStyle") +} + +/** + Keys in the user info dictionaries of various notifications posted by instances of `StyleManager`. + */ +public struct StyleManagerNotificationUserInfoKey: Hashable, Equatable, RawRepresentable { + public typealias RawValue = String + + public var rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } + + /** + A key in the user info dictionary of `StyleManagerDidApplyStyleNotification` notification. The corresponding value is an `Style` instance that was applied. + */ + static let styleKey: StyleManagerNotificationUserInfoKey = .init(rawValue: "style") + + /** + A key in the user info dictionary of `StyleManagerDidApplyStyleNotification` notification. The corresponding value is an `StyleManager` instance that applied the style. + */ + static let styleManagerKey: StyleManagerNotificationUserInfoKey = .init(rawValue: "styleManager") } + +/** + Key used for constructing errors when spoken instructions fail. + */ +let SpokenInstructionErrorCodeKey: String = "MBSpokenInstructionErrorCode" diff --git a/MapboxNavigation/DashedLineView.swift b/MapboxNavigation/DashedLineView.swift index 0525b2dba6f..aeae558355c 100644 --- a/MapboxNavigation/DashedLineView.swift +++ b/MapboxNavigation/DashedLineView.swift @@ -2,9 +2,7 @@ import UIKit /// :nodoc: @IBDesignable -@objc(MBDashedLineView) public class DashedLineView: LineView { - @IBInspectable public var dashedLength: CGFloat = 4 { didSet { updateProperties() } } @IBInspectable public var dashedGap: CGFloat = 4 { didSet { updateProperties() } } diff --git a/MapboxNavigation/DataCache.swift b/MapboxNavigation/DataCache.swift index 9584fa6a92a..a824eed0efb 100644 --- a/MapboxNavigation/DataCache.swift +++ b/MapboxNavigation/DataCache.swift @@ -1,6 +1,5 @@ import Foundation -@objc(MBDataCache) public class DataCache: NSObject, BimodalDataCache { let memoryCache: NSCache let fileCache = FileCache() @@ -14,19 +13,18 @@ public class DataCache: NSObject, BimodalDataCache { NotificationCenter.default.addObserver(self, selector: #selector(DataCache.clearMemory), name: UIApplication.didReceiveMemoryWarningNotification, object: nil) } - // MARK: Data cache - /* + /** Stores data in the cache for the given key. If `toDisk` is set to `true`, the completion handler is called following writing the data to disk, otherwise it is called immediately upon storing the data in the memory cache. - */ + */ public func store(_ data: Data, forKey key: String, toDisk: Bool, completion: CompletionHandler?) { storeDataInMemoryCache(data, forKey: key) toDisk == true ? fileCache.store(data, forKey: key, completion: completion) : completion?() } - /* + /** Returns data from the cache for the given key, if any. The memory cache is consulted first, followed by the disk cache. If data is found on disk which isn't in memory, it is added to the memory cache. */ public func data(forKey key: String?) -> Data? { @@ -46,14 +44,14 @@ public class DataCache: NSObject, BimodalDataCache { return nil } - /* + /** Clears out the memory cache. */ - public func clearMemory() { + @objc public func clearMemory() { memoryCache.removeAllObjects() } - /* + /** Clears the disk cache and calls the completion handler when finished. */ public func clearDisk(completion: CompletionHandler?) { @@ -70,5 +68,4 @@ public class DataCache: NSObject, BimodalDataCache { } return nil } - } diff --git a/MapboxNavigation/DayInstructionsCardStyle.swift b/MapboxNavigation/DayInstructionsCardStyle.swift index c90acb83479..d95d81f4a8e 100644 --- a/MapboxNavigation/DayInstructionsCardStyle.swift +++ b/MapboxNavigation/DayInstructionsCardStyle.swift @@ -79,7 +79,7 @@ public class DayInstructionsCardStyle: InstructionsCardStyle { } public lazy var nextBannerInstructionLabelNormalFont: UIFont = { - return CardFont.create(.regular, with: 14.0) + return CardFont.create(.regular, with: 14.0) }() public var nextBannerInstructionHighlightedColor: UIColor { diff --git a/MapboxNavigation/DayStyle.swift b/MapboxNavigation/DayStyle.swift index 41052e1efdf..102bc60c8b2 100644 --- a/MapboxNavigation/DayStyle.swift +++ b/MapboxNavigation/DayStyle.swift @@ -1,7 +1,6 @@ import Foundation import Mapbox - extension UIColor { class var defaultRouteCasing: UIColor { get { return .defaultTintStroke } } class var defaultRouteLayer: UIColor { get { return #colorLiteral(red: 0.337254902, green: 0.6588235294, blue: 0.9843137255, alpha: 1) } } @@ -40,9 +39,7 @@ extension UIFont { /** `DefaultStyle` is default style for Mapbox Navigation SDK. */ -@objc(MBDayStyle) open class DayStyle: Style { - public required init() { super.init() mapStyleURL = MGLStyle.navigationGuidanceDayStyleURL @@ -169,9 +166,7 @@ open class DayStyle: Style { /** `NightStyle` is the default night style for Mapbox Navigation SDK. Only will be applied when necessary and if `automaticallyAdjustStyleForSunPosition`. */ -@objc(MBNightStyle) open class NightStyle: DayStyle { - public required init() { super.init() mapStyleURL = MGLStyle.navigationGuidanceNightStyleURL diff --git a/MapboxNavigation/EndOfRouteViewController.swift b/MapboxNavigation/EndOfRouteViewController.swift index 47911792a03..0631c58c045 100644 --- a/MapboxNavigation/EndOfRouteViewController.swift +++ b/MapboxNavigation/EndOfRouteViewController.swift @@ -12,28 +12,21 @@ fileprivate enum ContainerHeight: CGFloat { } /// :nodoc: -@objc(MBEndOfRouteContentView) open class EndOfRouteContentView: UIView {} /// :nodoc: -@objc(MBEndOfRouteTitleLabel) open class EndOfRouteTitleLabel: StylableLabel {} /// :nodoc: -@objc(MBEndOfRouteStaticLabel) open class EndOfRouteStaticLabel: StylableLabel {} /// :nodoc: -@objc(MBEndOfRouteCommentView) open class EndOfRouteCommentView: StylableTextView {} /// :nodoc: -@objc(MBEndOfRouteButton) open class EndOfRouteButton: StylableButton {} -@objc(MBEndOfRouteViewController) class EndOfRouteViewController: UIViewController { - // MARK: - IBOutlets @IBOutlet weak var labelContainer: UIView! @IBOutlet weak var staticYouHaveArrived: EndOfRouteStaticLabel! diff --git a/MapboxNavigation/Error.swift b/MapboxNavigation/Error.swift index 4bd2a1bc4e9..90d98e48855 100644 --- a/MapboxNavigation/Error.swift +++ b/MapboxNavigation/Error.swift @@ -1,17 +1,73 @@ import Foundation import MapboxCoreNavigation +import MapboxDirections +import MapboxSpeech +import AVKit -extension NSError { - /** - Creates a custom `Error` object. - */ - convenience init(code: MBErrorCode, localizedFailureReason: String, spokenInstructionCode: SpokenInstructionErrorCode? = nil) { - var userInfo = [ - NSLocalizedFailureReasonErrorKey: localizedFailureReason - ] - if let spokenInstructionCode = spokenInstructionCode { - userInfo[MBSpokenInstructionErrorCodeKey] = String(spokenInstructionCode.rawValue) - } - self.init(domain: MBErrorDomain, code: code.rawValue, userInfo: userInfo) - } +/** + The speech-related action that failed. + - seealso: SpeechError + */ +public enum SpeechFailureAction: String { + /** + A failure occurred while attempting to mix audio. + */ + case mix + /** + A failure occurred while attempting to duck audio. + */ + case duck + /** + A failure occurred while attempting to unduck audio. + */ + case unduck + /** + A failure occurred while attempting to play audio. + */ + case play +} + +/** + A error type returned when encountering errors in the speech engine. + */ +public enum SpeechError: LocalizedError { + /** + The Speech API Did not successfully return a response. + - parameter instruction: the instruction that failed. + - parameter options: the SpeechOptions that were used to make the API request. + - parameter underlying: the underlying `Error` returned by the API. + */ + case apiError(instruction: SpokenInstruction, options: SpeechOptions, underlying: Error?) + + /** + The Speech API Did not return any data. + - parameter instruction: the instruction that failed. + - parameter options: the SpeechOptions that were used to make the API request. + */ + case noData(instruction: SpokenInstruction, options: SpeechOptions) + + /** + The speech engine was unable to perform an action on the system audio service. + - parameter instruction: The instruction that failed. + - parameter action: a `SpeechFailureAction` that describes the action attempted + - parameter synthesizer: the speech engine that tried to perform the action. + - parameter underlying: the `Error` that was optrionally returned by the audio service. + */ + case unableToControlAudio(instruction: SpokenInstruction?, action: SpeechFailureAction, synthesizer: Any?, underlying: Error?) + + /** + The speech engine was unable to initalize an audio player. + - parameter playerType: the type of `AVAudioPlayer` that failed to initalize. + - parameter instruction: The instruction that failed. + - parameter synthesizer: The speech engine that attempted the initalization. + - parameter underlying: the `Error` that was returned by the system audio service. + */ + case unableToInitializePlayer(playerType: AVAudioPlayer.Type, instruction: SpokenInstruction, synthesizer: Any?, underlying: Error) + + /** + The active `RouteProgress` did not contain a speech locale. + - parameter instruction: The instruction that failed. + - parameter progress: the offending `RouteProgress` that omits the expected `SpeechLocale`. + */ + case undefinedSpeechLocale(instruction: SpokenInstruction, progress: RouteProgress) } diff --git a/MapboxNavigation/ExitView.swift b/MapboxNavigation/ExitView.swift index 5489a19133f..07dd38427b0 100644 --- a/MapboxNavigation/ExitView.swift +++ b/MapboxNavigation/ExitView.swift @@ -61,14 +61,12 @@ public class ExitView: StylableView { } } - func spacing(for side: ExitSide, direction: UIUserInterfaceLayoutDirection = UIApplication.shared.userInterfaceLayoutDirection) -> CGFloat { let space: (less: CGFloat, more: CGFloat) = (4.0, 6.0) let lessSide: ExitSide = (direction == .rightToLeft) ? .left : .right return side == lessSide ? space.less : space.more } - convenience init(pointSize: CGFloat, side: ExitSide = .right, text: String) { self.init(frame: .zero) self.pointSize = pointSize diff --git a/MapboxNavigation/FeedbackCollectionViewCell.swift b/MapboxNavigation/FeedbackCollectionViewCell.swift index 0500968c194..f519c7a2a6e 100644 --- a/MapboxNavigation/FeedbackCollectionViewCell.swift +++ b/MapboxNavigation/FeedbackCollectionViewCell.swift @@ -1,7 +1,6 @@ import Foundation import UIKit - class FeedbackCollectionViewCell: UICollectionViewCell { static let defaultIdentifier = "MapboxFeedbackCell" diff --git a/MapboxNavigation/FeedbackItem.swift b/MapboxNavigation/FeedbackItem.swift index 932acc7b9ea..2397e26402c 100644 --- a/MapboxNavigation/FeedbackItem.swift +++ b/MapboxNavigation/FeedbackItem.swift @@ -10,27 +10,26 @@ extension UIImage { /** A single feedback item displayed on an instance of `FeedbackViewController`. */ -@objc(MBFeedbackItem) public class FeedbackItem: NSObject { /** The title of feedback item. This will be rendered directly below the image. */ - @objc public var title: String + public var title: String /** An image representation of the feedback. */ - @objc public var image: UIImage + public var image: UIImage /** The type of feedback that best describes the event. */ - @objc public var feedbackType: FeedbackType + public var feedbackType: FeedbackType /** Creates a new `FeedbackItem`. */ - @objc public init(title: String, image: UIImage, feedbackType: FeedbackType) { + public init(title: String, image: UIImage, feedbackType: FeedbackType) { self.title = title self.image = image self.feedbackType = feedbackType diff --git a/MapboxNavigation/FeedbackViewController.swift b/MapboxNavigation/FeedbackViewController.swift index faa1845568c..cf44d5270a5 100644 --- a/MapboxNavigation/FeedbackViewController.swift +++ b/MapboxNavigation/FeedbackViewController.swift @@ -3,16 +3,16 @@ import MapboxCoreNavigation import AVFoundation extension FeedbackViewController: UIViewControllerTransitioningDelegate { - @objc public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { abortAutodismiss() return DismissAnimator() } - @objc public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { + public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return PresentAnimator() } - @objc public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { + public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { return interactor.hasStarted ? interactor : nil } } @@ -20,29 +20,43 @@ extension FeedbackViewController: UIViewControllerTransitioningDelegate { /** The `FeedbackViewControllerDelegate` protocol provides methods for responding to feedback events. */ -@objc public protocol FeedbackViewControllerDelegate { - +public protocol FeedbackViewControllerDelegate: class, UnimplementedLogging { /** Called when the user opens the feedback form. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func feedbackViewControllerDidOpen(_ feedbackViewController: FeedbackViewController) + func feedbackViewControllerDidOpen(_ feedbackViewController: FeedbackViewController) /** Called when the user submits a feedback event. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(feedbackViewController:didSendFeedbackItem:UUID:) - optional func feedbackViewController(_ feedbackViewController: FeedbackViewController, didSend feedbackItem: FeedbackItem, uuid: UUID) + func feedbackViewController(_ feedbackViewController: FeedbackViewController, didSend feedbackItem: FeedbackItem, uuid: UUID) /** Called when a `FeedbackViewController` is dismissed for any reason without giving explicit feedback. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func feedbackViewControllerDidCancel(_ feedbackViewController: FeedbackViewController) + func feedbackViewControllerDidCancel(_ feedbackViewController: FeedbackViewController) +} + +public extension FeedbackViewControllerDelegate { + func feedbackViewControllerDidOpen(_ feedbackViewController: FeedbackViewController) { + logUnimplemented(protocolType: FeedbackViewControllerDelegate.self, level: .debug) + } + + func feedbackViewController(_ feedbackViewController: FeedbackViewController, didSend feedbackItem: FeedbackItem, uuid: UUID) { + logUnimplemented(protocolType: FeedbackViewControllerDelegate.self, level: .debug) + } + + func feedbackViewControllerDidCancel(_ feedbackViewController: FeedbackViewController) { + logUnimplemented(protocolType: FeedbackViewControllerDelegate.self, level: .debug) + } } /** A view controller containing a grid of buttons the user can use to denote an issue their current navigation experience. */ -@objc(MBFeedbackViewController) public class FeedbackViewController: UIViewController, DismissDraggable, UIGestureRecognizerDelegate { var activeFeedbackItem: FeedbackItem? @@ -58,7 +72,7 @@ public class FeedbackViewController: UIViewController, DismissDraggable, UIGestu */ public var sections: [FeedbackItem] = [.turnNotAllowed, .closure, .reportTraffic, .confusingInstructions, .generalMapError, .badRoute] - @objc public weak var delegate: FeedbackViewControllerDelegate? + public weak var delegate: FeedbackViewControllerDelegate? lazy var collectionView: UICollectionView = { let view: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) @@ -108,7 +122,7 @@ public class FeedbackViewController: UIViewController, DismissDraggable, UIGestu /** Initialize a new FeedbackViewController from a `NavigationEventsManager`. */ - @objc public init(eventsManager: NavigationEventsManager) { + public init(eventsManager: NavigationEventsManager) { self.eventsManager = eventsManager super.init(nibName: nil, bundle: nil) commonInit() @@ -147,7 +161,7 @@ public class FeedbackViewController: UIViewController, DismissDraggable, UIGestu override public func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - delegate?.feedbackViewControllerDidOpen?(self) + delegate?.feedbackViewControllerDidOpen(self) UIView.animate(withDuration: FeedbackViewController.autoDismissInterval) { self.progressBar.progress = 0 @@ -231,7 +245,7 @@ public class FeedbackViewController: UIViewController, DismissDraggable, UIGestu func send(_ item: FeedbackItem) { if let uuid = self.uuid { - delegate?.feedbackViewController?(self, didSend: item, uuid: uuid) + delegate?.feedbackViewController(self, didSend: item, uuid: uuid) eventsManager?.updateFeedback(uuid: uuid, type: item.feedbackType, source: .user, description: nil) } @@ -246,7 +260,7 @@ public class FeedbackViewController: UIViewController, DismissDraggable, UIGestu } func dismissFeedbackItem() { - delegate?.feedbackViewControllerDidCancel?(self) + delegate?.feedbackViewControllerDidCancel(self) if let uuid = self.uuid { eventsManager?.cancelFeedback(uuid: uuid) } @@ -255,7 +269,7 @@ public class FeedbackViewController: UIViewController, DismissDraggable, UIGestu } extension FeedbackViewController: UICollectionViewDataSource { - @objc public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: FeedbackCollectionViewCell.defaultIdentifier, for: indexPath) as! FeedbackCollectionViewCell let item = sections[indexPath.row] @@ -266,15 +280,15 @@ extension FeedbackViewController: UICollectionViewDataSource { return cell } - @objc public func numberOfSections(in collectionView: UICollectionView) -> Int { + public func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } - @objc public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections.count } - @objc public func scrollViewDidScroll(_ scrollView: UIScrollView) { + public func scrollViewDidScroll(_ scrollView: UIScrollView) { // In case the view is scrolled, dismiss the feedback window immediately // and reset the `progressBar` back to a full progress. abortAutodismiss() @@ -283,7 +297,7 @@ extension FeedbackViewController: UICollectionViewDataSource { } extension FeedbackViewController: UICollectionViewDelegate { - @objc public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { abortAutodismiss() let item = sections[indexPath.row] send(item) @@ -291,7 +305,7 @@ extension FeedbackViewController: UICollectionViewDelegate { } extension FeedbackViewController: UICollectionViewDelegateFlowLayout { - @objc public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let availableWidth = collectionView.bounds.width // 3 columns and 2 rows in portrait mode. // 6 columns and 1 row in landscape mode. @@ -301,9 +315,9 @@ extension FeedbackViewController: UICollectionViewDelegateFlowLayout { let item = sections[indexPath.row] let titleHeight = item.title.height(constrainedTo: width, font: FeedbackCollectionViewCell.Constants.titleFont) let cellHeight: CGFloat = FeedbackCollectionViewCell.Constants.imageSize.height - + FeedbackCollectionViewCell.Constants.padding - + titleHeight - + FeedbackViewController.verticalCellPadding + + FeedbackCollectionViewCell.Constants.padding + + titleHeight + + FeedbackViewController.verticalCellPadding return CGSize(width: width, height: cellHeight ) } } diff --git a/MapboxNavigation/GenericRouteShield.swift b/MapboxNavigation/GenericRouteShield.swift index 34098b9bca0..dd1c25f313e 100644 --- a/MapboxNavigation/GenericRouteShield.swift +++ b/MapboxNavigation/GenericRouteShield.swift @@ -15,7 +15,7 @@ public class GenericRouteShield: StylableView { setNeedsDisplay() } } - + //The label that contains the route code. lazy var routeLabel: UILabel = { let label: UILabel = .forAutoLayout() diff --git a/MapboxNavigation/HighwayShield.swift b/MapboxNavigation/HighwayShield.swift index a2da3aa392a..7db0f65d504 100644 --- a/MapboxNavigation/HighwayShield.swift +++ b/MapboxNavigation/HighwayShield.swift @@ -1,5 +1,4 @@ struct HighwayShield { - enum RoadClass: String { case alternate, duplex, business, truck, bypass, b case oneB = "1b", twoA = "2a", twoB = "2b" @@ -69,7 +68,7 @@ struct HighwayShield { self = roadType(locale, nil) case 3: guard let roadType = RoadType.type(for: fields[1]), - let locale = Locale(rawValue: fields[0]), let roadClass = RoadClass(rawValue: fields[2]) else { + let locale = Locale(rawValue: fields[0]), let roadClass = RoadClass(rawValue: fields[2]) else { return nil } self = roadType(locale, roadClass) diff --git a/MapboxNavigation/ImageCache.swift b/MapboxNavigation/ImageCache.swift index 8be1d1c2633..7672d37af10 100644 --- a/MapboxNavigation/ImageCache.swift +++ b/MapboxNavigation/ImageCache.swift @@ -15,7 +15,7 @@ internal class ImageCache: BimodalImageCache { // MARK: Image cache - /* + /** Stores an image in the cache for the given key. If `toDisk` is set to `true`, the completion handler is called following writing the image to disk, otherwise it is called immediately upon storing the image in the memory cache. */ public func store(_ image: UIImage, forKey key: String, toDisk: Bool, completion: CompletionHandler?) { @@ -29,7 +29,7 @@ internal class ImageCache: BimodalImageCache { fileCache.store(data, forKey: key, completion: completion) } - /* + /** Returns an image from the cache for the given key, if any. The memory cache is consulted first, followed by the disk cache. If an image is found on disk which isn't in memory, it is added to the memory cache. */ public func image(forKey key: String?) -> UIImage? { @@ -49,14 +49,14 @@ internal class ImageCache: BimodalImageCache { return nil } - /* + /** Clears out the memory cache. */ public func clearMemory() { memoryCache.removeAllObjects() } - /* + /** Clears the disk cache and calls the completion handler when finished. */ public func clearDisk(completion: CompletionHandler?) { diff --git a/MapboxNavigation/ImageDownload.swift b/MapboxNavigation/ImageDownload.swift index 99dde5cd6ae..07c1845456d 100644 --- a/MapboxNavigation/ImageDownload.swift +++ b/MapboxNavigation/ImageDownload.swift @@ -106,7 +106,6 @@ class ImageDownloadOperation: Operation, ImageDownload { } else { //fail and bail; connection failed or bad URL (client-side error) } - } // MARK: URLSessionDataDelegate diff --git a/MapboxNavigation/ImageDownloader.swift b/MapboxNavigation/ImageDownloader.swift index 3869d4c3e2b..c608ff28f0e 100644 --- a/MapboxNavigation/ImageDownloader.swift +++ b/MapboxNavigation/ImageDownloader.swift @@ -115,5 +115,4 @@ class ImageDownloader: NSObject, ReentrantImageDownloader, URLSessionDataDelegat self.operations[url] = nil } } - } diff --git a/MapboxNavigation/ImageRepository.swift b/MapboxNavigation/ImageRepository.swift index abf3355ee87..3505c36ef57 100644 --- a/MapboxNavigation/ImageRepository.swift +++ b/MapboxNavigation/ImageRepository.swift @@ -1,7 +1,6 @@ import Foundation class ImageRepository { - public var sessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.default { didSet { imageDownloader = ImageDownloader(sessionConfiguration: sessionConfiguration) diff --git a/MapboxNavigation/InstructionLabel.swift b/MapboxNavigation/InstructionLabel.swift index 15b0a933464..d28ef159377 100644 --- a/MapboxNavigation/InstructionLabel.swift +++ b/MapboxNavigation/InstructionLabel.swift @@ -3,7 +3,6 @@ import MapboxCoreNavigation import MapboxDirections /// :nodoc: -@objc(MBInstructionLabel) open class InstructionLabel: StylableLabel, InstructionPresenterDataSource { typealias AvailableBoundsHandler = () -> (CGRect) var availableBounds: AvailableBoundsHandler! @@ -26,11 +25,10 @@ open class InstructionLabel: StylableLabel, InstructionPresenterDataSource { self?.imageDownloadCompletion?() } - let presenter = InstructionPresenter(instruction, dataSource: self, imageRepository: imageRepository, downloadCompletion: update) let attributed = presenter.attributedText() - attributedText = instructionDelegate?.label?(self, willPresent: instruction, as: attributed) ?? attributed + attributedText = instructionDelegate?.label(self, willPresent: instruction, as: attributed) ?? attributed instructionPresenter = presenter } } @@ -41,9 +39,7 @@ open class InstructionLabel: StylableLabel, InstructionPresenterDataSource { /** The `VisualInstructionDelegate` protocol defines a method that allows an object to customize presented visual instructions. */ -@objc(MBVisualInstructionDelegate) -public protocol VisualInstructionDelegate: class { - +public protocol VisualInstructionDelegate: class, UnimplementedLogging { /** Called when an InstructionLabel will present a visual instruction. @@ -51,7 +47,14 @@ public protocol VisualInstructionDelegate: class { - parameter instruction: the `VisualInstruction` that will be presented. - parameter presented: the formatted string that is provided by the instruction presenter - returns: optionally, a customized NSAttributedString that will be presented instead of the default, or if nil, the default behavior will be used. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(label:willPresentVisualInstruction:asAttributedString:) - optional func label(_ label: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? + func label(_ label: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? +} + +public extension VisualInstructionDelegate { + func label(_ label: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { + logUnimplemented(protocolType: InstructionLabel.self, level: .debug) + return nil + } } diff --git a/MapboxNavigation/InstructionPresenter.swift b/MapboxNavigation/InstructionPresenter.swift index 20a4f4474db..4d603c5fbb0 100644 --- a/MapboxNavigation/InstructionPresenter.swift +++ b/MapboxNavigation/InstructionPresenter.swift @@ -11,7 +11,6 @@ protocol InstructionPresenterDataSource: class { typealias DataSource = InstructionPresenterDataSource class InstructionPresenter { - private let instruction: VisualInstruction private weak var dataSource: DataSource? @@ -71,7 +70,6 @@ class InstructionPresenter { var strings: [NSAttributedString] = [] var processedComponents: [VisualInstructionComponent] = [] - for (index, component) in components.enumerated() { let isFirst = index == 0 let joinChar = isFirst ? "" : " " @@ -201,7 +199,6 @@ class InstructionPresenter { private func exitShield(side: ExitSide = .right, text: String, component: VisualInstructionComponent, dataSource: DataSource) -> NSAttributedString? { guard let cacheKey = component.cacheKey else { return nil } - let additionalKey = ExitView.criticalHash(side: side, dataSource: dataSource) let attachment = ExitAttachment() @@ -242,7 +239,6 @@ class InstructionPresenter { view.removeFromSuperview() return image } - } protocol ImagePresenter: TextPresenter { @@ -300,7 +296,6 @@ class RoadNameLabelAttachment: TextInstruction { } extension CGSize { - fileprivate static func +(lhs: CGSize, rhs: CGSize) -> CGSize { return CGSize(width: lhs.width + rhs.width, height: lhs.height + rhs.height) } diff --git a/MapboxNavigation/InstructionsBannerView.swift b/MapboxNavigation/InstructionsBannerView.swift index e19c411574a..a3cbb74f3da 100644 --- a/MapboxNavigation/InstructionsBannerView.swift +++ b/MapboxNavigation/InstructionsBannerView.swift @@ -5,46 +5,56 @@ import MapboxDirections /** `InstructionsBannerViewDelegate` provides methods for reacting to user interactions in `InstructionsBannerView`. */ -@objc(MBInstructionsBannerViewDelegate) -public protocol InstructionsBannerViewDelegate: class { - +public protocol InstructionsBannerViewDelegate: class, UnimplementedLogging { /** Called when the user taps the `InstructionsBannerView`. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(didTapInstructionsBanner:) - optional func didTapInstructionsBanner(_ sender: BaseInstructionsBannerView) - + func didTapInstructionsBanner(_ sender: BaseInstructionsBannerView) /** Called when the user drags either up or down on the `InstructionsBannerView`. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ @available(*, deprecated, message: "Please use didSwipeInstructionsBanner instead.") - @objc(didDragInstructionsBanner:) - optional func didDragInstructionsBanner(_ sender: BaseInstructionsBannerView) + + func didDragInstructionsBanner(_ sender: BaseInstructionsBannerView) /** Called when the user swipes either left, right, or down on the `InstructionsBannerView` + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func didSwipeInstructionsBanner(_ sender: BaseInstructionsBannerView, swipeDirection direction: UISwipeGestureRecognizer.Direction) + func didSwipeInstructionsBanner(_ sender: BaseInstructionsBannerView, swipeDirection direction: UISwipeGestureRecognizer.Direction) +} + +public extension InstructionsBannerViewDelegate { + func didTapInstructionsBanner(_ sender: BaseInstructionsBannerView) { + logUnimplemented(protocolType: InstructionsBannerViewDelegate.self, level: .debug) + } + + func didDragInstructionsBanner(_ sender: BaseInstructionsBannerView) { + //no-op, deprecated. + } + + func didSwipeInstructionsBanner(_ sender: BaseInstructionsBannerView, swipeDirection direction: UISwipeGestureRecognizer.Direction) { + logUnimplemented(protocolType: InstructionsBannerViewDelegate.self, level: .debug) + } } -@objc private protocol InstructionsBannerViewDelegateDeprecations { - @objc(didDragInstructionsBanner:) - optional func didDragInstructionsBanner(_ sender: BaseInstructionsBannerView) +private protocol InstructionsBannerViewDelegateDeprecations { + func didDragInstructionsBanner(_ sender: BaseInstructionsBannerView) } /// :nodoc: @IBDesignable -@objc(MBInstructionsBannerView) open class InstructionsBannerView: BaseInstructionsBannerView, NavigationComponent { - @objc public func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { + public func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { update(for: instruction) } } /// :nodoc: open class BaseInstructionsBannerView: UIControl { - public weak var maneuverView: ManeuverView! public weak var primaryLabel: PrimaryLabel! public weak var secondaryLabel: SecondaryLabel! @@ -67,7 +77,7 @@ open class BaseInstructionsBannerView: UIControl { public weak var delegate: InstructionsBannerViewDelegate? { didSet { if showStepIndicator { - stepListIndicatorView.isHidden = false + stepListIndicatorView.isHidden = false } } } @@ -121,11 +131,10 @@ open class BaseInstructionsBannerView: UIControl { if let gestureRecognizer = sender as? UISwipeGestureRecognizer, gestureRecognizer.state == .ended { if let delegate = delegate { - delegate.didSwipeInstructionsBanner?(self, swipeDirection: .left) + delegate.didSwipeInstructionsBanner(self, swipeDirection: .left) } } } - @objc func swipedInstructionBannerRight(_ sender: Any) { if !swipeable { return @@ -133,7 +142,7 @@ open class BaseInstructionsBannerView: UIControl { if let gestureRecognizer = sender as? UISwipeGestureRecognizer, gestureRecognizer.state == .ended { if let delegate = delegate { - delegate.didSwipeInstructionsBanner?(self, swipeDirection: .right) + delegate.didSwipeInstructionsBanner(self, swipeDirection: .right) } } } @@ -141,12 +150,12 @@ open class BaseInstructionsBannerView: UIControl { @objc func swipedInstructionBannerDown(_ sender: Any) { if let gestureRecognizer = sender as? UISwipeGestureRecognizer, gestureRecognizer.state == .ended { if showStepIndicator { - stepListIndicatorView.isHidden = !stepListIndicatorView.isHidden + stepListIndicatorView.isHidden = !stepListIndicatorView.isHidden } if let delegate = delegate { - delegate.didSwipeInstructionsBanner?(self, swipeDirection: .down) - (delegate as? InstructionsBannerViewDelegateDeprecations)?.didDragInstructionsBanner?(self) + delegate.didSwipeInstructionsBanner(self, swipeDirection: .down) + (delegate as? InstructionsBannerViewDelegateDeprecations)?.didDragInstructionsBanner(self) } } } @@ -156,14 +165,13 @@ open class BaseInstructionsBannerView: UIControl { if showStepIndicator { stepListIndicatorView.isHidden = !stepListIndicatorView.isHidden } - delegate.didTapInstructionsBanner?(self) + delegate.didTapInstructionsBanner(self) } } /** Updates the instructions banner info with a given `VisualInstructionBanner`. */ - @objc(updateForVisualInstructionBanner:) public func update(for instruction: VisualInstructionBanner?) { let secondaryInstruction = instruction?.secondaryInstruction primaryLabel.numberOfLines = secondaryInstruction == nil ? 2 : 1 @@ -197,4 +205,165 @@ open class BaseInstructionsBannerView: UIControl { let distanceRemaining = currentStepProgress.distanceRemaining distance = distanceRemaining > 5 ? distanceRemaining : 0 } + + // MARK: - Layout + static let padding: CGFloat = 16 + static let maneuverViewSize = CGSize(width: 38, height: 38) + static let stepListIndicatorViewSize = CGSize(width: 30, height: 5) + + func setupViews() { + let maneuverView = ManeuverView() + maneuverView.backgroundColor = .clear + maneuverView.translatesAutoresizingMaskIntoConstraints = false + addSubview(maneuverView) + self.maneuverView = maneuverView + + let distanceLabel = DistanceLabel() + distanceLabel.translatesAutoresizingMaskIntoConstraints = false + distanceLabel.adjustsFontSizeToFitWidth = true + distanceLabel.minimumScaleFactor = 16.0 / 22.0 + addSubview(distanceLabel) + self.distanceLabel = distanceLabel + + let primaryLabel = PrimaryLabel() + primaryLabel.instructionDelegate = instructionDelegate + primaryLabel.translatesAutoresizingMaskIntoConstraints = false + primaryLabel.allowsDefaultTighteningForTruncation = true + primaryLabel.adjustsFontSizeToFitWidth = true + primaryLabel.numberOfLines = 1 + primaryLabel.minimumScaleFactor = 20.0 / 30.0 + primaryLabel.lineBreakMode = .byTruncatingTail + addSubview(primaryLabel) + self.primaryLabel = primaryLabel + + let secondaryLabel = SecondaryLabel() + secondaryLabel.instructionDelegate = instructionDelegate + secondaryLabel.translatesAutoresizingMaskIntoConstraints = false + secondaryLabel.allowsDefaultTighteningForTruncation = true + secondaryLabel.numberOfLines = 1 + secondaryLabel.minimumScaleFactor = 20.0 / 26.0 + secondaryLabel.lineBreakMode = .byTruncatingTail + addSubview(secondaryLabel) + self.secondaryLabel = secondaryLabel + + let dividerView = UIView() + dividerView.translatesAutoresizingMaskIntoConstraints = false + addSubview(dividerView) + self.dividerView = dividerView + + let _separatorView = UIView() + _separatorView.translatesAutoresizingMaskIntoConstraints = false + addSubview(_separatorView) + self._separatorView = _separatorView + + let separatorView = SeparatorView() + separatorView.translatesAutoresizingMaskIntoConstraints = false + addSubview(separatorView) + self.separatorView = separatorView + + let stepListIndicatorView = StepListIndicatorView() + stepListIndicatorView.translatesAutoresizingMaskIntoConstraints = false + addSubview(stepListIndicatorView) + self.stepListIndicatorView = stepListIndicatorView + + addTarget(self, action: #selector(BaseInstructionsBannerView.tappedInstructionsBanner(_:)), for: .touchUpInside) + + let swipeLeftGesture = UISwipeGestureRecognizer(target: self, action: #selector(BaseInstructionsBannerView.swipedInstructionBannerLeft(_:))) + swipeLeftGesture.direction = .left + addGestureRecognizer(swipeLeftGesture) + + let swipeRightGesture = UISwipeGestureRecognizer(target: self, action: #selector(BaseInstructionsBannerView.swipedInstructionBannerRight(_:))) + swipeRightGesture.direction = .right + addGestureRecognizer(swipeRightGesture) + + let swipeDownGesture = UISwipeGestureRecognizer(target: self, action: #selector(BaseInstructionsBannerView.swipedInstructionBannerDown(_:))) + swipeDownGesture.direction = .down + addGestureRecognizer(swipeDownGesture) + } + + func setupLayout() { + // firstColumnWidth is the width of the left side of the banner containing the maneuver view and distance label + let firstColumnWidth = BaseInstructionsBannerView.maneuverViewSize.width + BaseInstructionsBannerView.padding * 3 + + // Distance label + distanceLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: BaseInstructionsBannerView.padding / 2).isActive = true + distanceLabel.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -BaseInstructionsBannerView.padding / 2).isActive = true + distanceLabel.centerXAnchor.constraint(equalTo: maneuverView.centerXAnchor, constant: 0).isActive = true + distanceLabel.lastBaselineAnchor.constraint(equalTo: bottomAnchor, constant: -BaseInstructionsBannerView.padding).isActive = true + distanceLabel.topAnchor.constraint(greaterThanOrEqualTo: maneuverView.bottomAnchor).isActive = true + + // Turn arrow view + maneuverView.heightAnchor.constraint(equalToConstant: BaseInstructionsBannerView.maneuverViewSize.height).isActive = true + maneuverView.widthAnchor.constraint(equalToConstant: BaseInstructionsBannerView.maneuverViewSize.width).isActive = true + maneuverView.topAnchor.constraint(equalTo: topAnchor, constant: BaseInstructionsBannerView.padding).isActive = true + maneuverView.centerXAnchor.constraint(equalTo: leadingAnchor, constant: firstColumnWidth / 2).isActive = true + + // Primary Label + primaryLabel.leadingAnchor.constraint(equalTo: dividerView.trailingAnchor).isActive = true + primaryLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -18).isActive = true + baselineConstraints.append(primaryLabel.topAnchor.constraint(equalTo: maneuverView.topAnchor, constant: -BaseInstructionsBannerView.padding/2)) + centerYConstraints.append(primaryLabel.centerYAnchor.constraint(equalTo: centerYAnchor)) + + // Secondary Label + secondaryLabel.leadingAnchor.constraint(equalTo: dividerView.trailingAnchor).isActive = true + secondaryLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -18).isActive = true + baselineConstraints.append(secondaryLabel.lastBaselineAnchor.constraint(equalTo: distanceLabel.lastBaselineAnchor, constant: -BaseInstructionsBannerView.padding / 2)) + baselineConstraints.append(secondaryLabel.topAnchor.constraint(greaterThanOrEqualTo: primaryLabel.bottomAnchor, constant: 0)) + centerYConstraints.append(secondaryLabel.topAnchor.constraint(greaterThanOrEqualTo: primaryLabel.bottomAnchor, constant: 0)) + + // Drag Indicator View + stepListIndicatorView.heightAnchor.constraint(equalToConstant: BaseInstructionsBannerView.stepListIndicatorViewSize.height).isActive = true + stepListIndicatorView.widthAnchor.constraint(equalToConstant: BaseInstructionsBannerView.stepListIndicatorViewSize.width).isActive = true + stepListIndicatorView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -BaseInstructionsBannerView.padding / 2).isActive = true + stepListIndicatorView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true + baselineConstraints.append(stepListIndicatorView.topAnchor.constraint(greaterThanOrEqualTo: secondaryLabel.bottomAnchor)) + centerYConstraints.append(stepListIndicatorView.topAnchor.constraint(greaterThanOrEqualTo: secondaryLabel.bottomAnchor, constant: 0)) + + // Divider view (vertical divider between maneuver/distance to primary/secondary instruction + dividerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: firstColumnWidth).isActive = true + dividerView.widthAnchor.constraint(equalToConstant: 1).isActive = true + dividerView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true + dividerView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + + // Separator view (invisible helper view for visualizing the result of the constraints) + _separatorView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + _separatorView.heightAnchor.constraint(equalToConstant: 1).isActive = true + _separatorView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true + _separatorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + + // Visible separator docked to the bottom + separatorView.heightAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale).isActive = true + separatorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + separatorView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + separatorView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + } + + // Aligns the instruction to the center Y (used for single line primary and/or secondary instructions) + func centerYAlignInstructions() { + _separatorView.isHidden = false + baselineConstraints.forEach { $0.isActive = false } + centerYConstraints.forEach { $0.isActive = true } + } + + // Aligns primary top to the top of the maneuver view and the secondary baseline to the distance baseline (used for multiline) + func baselineAlignInstructions() { + _separatorView.isHidden = true + centerYConstraints.forEach { $0.isActive = false } + baselineConstraints.forEach { $0.isActive = true } + } + + func setupAvailableBounds() { + // Abbreviate if the instructions do not fit on one line + primaryLabel.availableBounds = { [unowned self] in + // Available width H:|-padding-maneuverView-padding-availableWidth-padding-| + let availableWidth = self.primaryLabel.viewForAvailableBoundsCalculation?.bounds.width ?? self.bounds.width - BaseInstructionsBannerView.maneuverViewSize.width - BaseInstructionsBannerView.padding * 3 + return CGRect(x: 0, y: 0, width: availableWidth, height: self.primaryLabel.font.lineHeight) + } + + secondaryLabel.availableBounds = { [unowned self] in + // Available width H:|-padding-maneuverView-padding-availableWidth-padding-| + let availableWidth = self.secondaryLabel.viewForAvailableBoundsCalculation?.bounds.width ?? self.bounds.width - BaseInstructionsBannerView.maneuverViewSize.width - BaseInstructionsBannerView.padding * 3 + return CGRect(x: 0, y: 0, width: availableWidth, height: self.secondaryLabel.font.lineHeight) + } + } } diff --git a/MapboxNavigation/InstructionsBannerViewLayout.swift b/MapboxNavigation/InstructionsBannerViewLayout.swift deleted file mode 100644 index 899e56dc56a..00000000000 --- a/MapboxNavigation/InstructionsBannerViewLayout.swift +++ /dev/null @@ -1,165 +0,0 @@ -import UIKit - -extension BaseInstructionsBannerView { - - static let padding: CGFloat = 16 - static let maneuverViewSize = CGSize(width: 38, height: 38) - static let stepListIndicatorViewSize = CGSize(width: 30, height: 5) - - func setupViews() { - - let maneuverView = ManeuverView() - maneuverView.backgroundColor = .clear - maneuverView.translatesAutoresizingMaskIntoConstraints = false - addSubview(maneuverView) - self.maneuverView = maneuverView - - let distanceLabel = DistanceLabel() - distanceLabel.translatesAutoresizingMaskIntoConstraints = false - distanceLabel.adjustsFontSizeToFitWidth = true - distanceLabel.minimumScaleFactor = 16.0 / 22.0 - addSubview(distanceLabel) - self.distanceLabel = distanceLabel - - let primaryLabel = PrimaryLabel() - primaryLabel.instructionDelegate = instructionDelegate - primaryLabel.translatesAutoresizingMaskIntoConstraints = false - primaryLabel.allowsDefaultTighteningForTruncation = true - primaryLabel.adjustsFontSizeToFitWidth = true - primaryLabel.numberOfLines = 1 - primaryLabel.minimumScaleFactor = 20.0 / 30.0 - primaryLabel.lineBreakMode = .byTruncatingTail - addSubview(primaryLabel) - self.primaryLabel = primaryLabel - - let secondaryLabel = SecondaryLabel() - secondaryLabel.instructionDelegate = instructionDelegate - secondaryLabel.translatesAutoresizingMaskIntoConstraints = false - secondaryLabel.allowsDefaultTighteningForTruncation = true - secondaryLabel.numberOfLines = 1 - secondaryLabel.minimumScaleFactor = 20.0 / 26.0 - secondaryLabel.lineBreakMode = .byTruncatingTail - addSubview(secondaryLabel) - self.secondaryLabel = secondaryLabel - - let dividerView = UIView() - dividerView.translatesAutoresizingMaskIntoConstraints = false - addSubview(dividerView) - self.dividerView = dividerView - - let _separatorView = UIView() - _separatorView.translatesAutoresizingMaskIntoConstraints = false - addSubview(_separatorView) - self._separatorView = _separatorView - - let separatorView = SeparatorView() - separatorView.translatesAutoresizingMaskIntoConstraints = false - addSubview(separatorView) - self.separatorView = separatorView - - let stepListIndicatorView = StepListIndicatorView() - stepListIndicatorView.translatesAutoresizingMaskIntoConstraints = false - addSubview(stepListIndicatorView) - self.stepListIndicatorView = stepListIndicatorView - - addTarget(self, action: #selector(BaseInstructionsBannerView.tappedInstructionsBanner(_:)), for: .touchUpInside) - - let swipeLeftGesture = UISwipeGestureRecognizer(target: self, action: #selector(BaseInstructionsBannerView.swipedInstructionBannerLeft(_:))) - swipeLeftGesture.direction = .left - addGestureRecognizer(swipeLeftGesture) - - let swipeRightGesture = UISwipeGestureRecognizer(target: self, action: #selector(BaseInstructionsBannerView.swipedInstructionBannerRight(_:))) - swipeRightGesture.direction = .right - addGestureRecognizer(swipeRightGesture) - - let swipeDownGesture = UISwipeGestureRecognizer(target: self, action: #selector(BaseInstructionsBannerView.swipedInstructionBannerDown(_:))) - swipeDownGesture.direction = .down - addGestureRecognizer(swipeDownGesture) - } - - @objc func setupLayout() { - // firstColumnWidth is the width of the left side of the banner containing the maneuver view and distance label - let firstColumnWidth = BaseInstructionsBannerView.maneuverViewSize.width + BaseInstructionsBannerView.padding * 3 - - // Distance label - distanceLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: BaseInstructionsBannerView.padding / 2).isActive = true - distanceLabel.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -BaseInstructionsBannerView.padding / 2).isActive = true - distanceLabel.centerXAnchor.constraint(equalTo: maneuverView.centerXAnchor, constant: 0).isActive = true - distanceLabel.lastBaselineAnchor.constraint(equalTo: bottomAnchor, constant: -BaseInstructionsBannerView.padding).isActive = true - distanceLabel.topAnchor.constraint(greaterThanOrEqualTo: maneuverView.bottomAnchor).isActive = true - - // Turn arrow view - maneuverView.heightAnchor.constraint(equalToConstant: BaseInstructionsBannerView.maneuverViewSize.height).isActive = true - maneuverView.widthAnchor.constraint(equalToConstant: BaseInstructionsBannerView.maneuverViewSize.width).isActive = true - maneuverView.topAnchor.constraint(equalTo: topAnchor, constant: BaseInstructionsBannerView.padding).isActive = true - maneuverView.centerXAnchor.constraint(equalTo: leadingAnchor, constant: firstColumnWidth / 2).isActive = true - - // Primary Label - primaryLabel.leadingAnchor.constraint(equalTo: dividerView.trailingAnchor).isActive = true - primaryLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -18).isActive = true - baselineConstraints.append(primaryLabel.topAnchor.constraint(equalTo: maneuverView.topAnchor, constant: -BaseInstructionsBannerView.padding/2)) - centerYConstraints.append(primaryLabel.centerYAnchor.constraint(equalTo: centerYAnchor)) - - // Secondary Label - secondaryLabel.leadingAnchor.constraint(equalTo: dividerView.trailingAnchor).isActive = true - secondaryLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -18).isActive = true - baselineConstraints.append(secondaryLabel.lastBaselineAnchor.constraint(equalTo: distanceLabel.lastBaselineAnchor, constant: -BaseInstructionsBannerView.padding / 2)) - baselineConstraints.append(secondaryLabel.topAnchor.constraint(greaterThanOrEqualTo: primaryLabel.bottomAnchor, constant: 0)) - centerYConstraints.append(secondaryLabel.topAnchor.constraint(greaterThanOrEqualTo: primaryLabel.bottomAnchor, constant: 0)) - - // Drag Indicator View - stepListIndicatorView.heightAnchor.constraint(equalToConstant: BaseInstructionsBannerView.stepListIndicatorViewSize.height).isActive = true - stepListIndicatorView.widthAnchor.constraint(equalToConstant: BaseInstructionsBannerView.stepListIndicatorViewSize.width).isActive = true - stepListIndicatorView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -BaseInstructionsBannerView.padding / 2).isActive = true - stepListIndicatorView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true - baselineConstraints.append(stepListIndicatorView.topAnchor.constraint(greaterThanOrEqualTo: secondaryLabel.bottomAnchor)) - centerYConstraints.append(stepListIndicatorView.topAnchor.constraint(greaterThanOrEqualTo: secondaryLabel.bottomAnchor, constant: 0)) - - // Divider view (vertical divider between maneuver/distance to primary/secondary instruction - dividerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: firstColumnWidth).isActive = true - dividerView.widthAnchor.constraint(equalToConstant: 1).isActive = true - dividerView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true - dividerView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - - // Separator view (invisible helper view for visualizing the result of the constraints) - _separatorView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true - _separatorView.heightAnchor.constraint(equalToConstant: 1).isActive = true - _separatorView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true - _separatorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - - // Visible separator docked to the bottom - separatorView.heightAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale).isActive = true - separatorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - separatorView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - separatorView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true - } - - // Aligns the instruction to the center Y (used for single line primary and/or secondary instructions) - @objc func centerYAlignInstructions() { - _separatorView.isHidden = false - baselineConstraints.forEach { $0.isActive = false } - centerYConstraints.forEach { $0.isActive = true } - } - - // Aligns primary top to the top of the maneuver view and the secondary baseline to the distance baseline (used for multiline) - @objc func baselineAlignInstructions() { - _separatorView.isHidden = true - centerYConstraints.forEach { $0.isActive = false } - baselineConstraints.forEach { $0.isActive = true } - } - - @objc func setupAvailableBounds() { - // Abbreviate if the instructions do not fit on one line - primaryLabel.availableBounds = { [unowned self] in - // Available width H:|-padding-maneuverView-padding-availableWidth-padding-| - let availableWidth = self.primaryLabel.viewForAvailableBoundsCalculation?.bounds.width ?? self.bounds.width - BaseInstructionsBannerView.maneuverViewSize.width - BaseInstructionsBannerView.padding * 3 - return CGRect(x: 0, y: 0, width: availableWidth, height: self.primaryLabel.font.lineHeight) - } - - secondaryLabel.availableBounds = { [unowned self] in - // Available width H:|-padding-maneuverView-padding-availableWidth-padding-| - let availableWidth = self.secondaryLabel.viewForAvailableBoundsCalculation?.bounds.width ?? self.bounds.width - BaseInstructionsBannerView.maneuverViewSize.width - BaseInstructionsBannerView.padding * 3 - return CGRect(x: 0, y: 0, width: availableWidth, height: self.secondaryLabel.font.lineHeight) - } - } -} diff --git a/MapboxNavigation/InstructionsCardCell.swift b/MapboxNavigation/InstructionsCardCell.swift index 0d72a5a2c4a..725a1a14f94 100644 --- a/MapboxNavigation/InstructionsCardCell.swift +++ b/MapboxNavigation/InstructionsCardCell.swift @@ -4,7 +4,6 @@ import MapboxCoreNavigation /// :nodoc: public class InstructionsCardCell: UICollectionViewCell { - public var container: InstructionsCardContainerView! public var style: InstructionsCardStyle = DayInstructionsCardStyle() diff --git a/MapboxNavigation/InstructionsCardCollectionDelegate.swift b/MapboxNavigation/InstructionsCardCollectionDelegate.swift index 9805eb3a90d..9a24dbc344c 100644 --- a/MapboxNavigation/InstructionsCardCollectionDelegate.swift +++ b/MapboxNavigation/InstructionsCardCollectionDelegate.swift @@ -2,7 +2,7 @@ import MapboxDirections import MapboxCoreNavigation /// :nodoc: -@objc public protocol InstructionsCardCollectionDelegate: InstructionsCardContainerViewDelegate { +public protocol InstructionsCardCollectionDelegate: InstructionsCardContainerViewDelegate { /** Called when previewing the steps on the current route. @@ -10,7 +10,6 @@ import MapboxCoreNavigation - parameter instructionsCardCollection: The instructions card collection instance. - parameter step: The step for the maneuver instruction in preview. */ - @objc(instructionsCardCollection:didPreviewStep:) func instructionsCardCollection(_ instructionsCardCollection: InstructionsCardViewController, didPreview step: RouteStep) /** @@ -20,6 +19,16 @@ import MapboxCoreNavigation - parameter traitCollection: The traitCollection associated to the current container view controller. - returns: The preferred size of the cards for each cell in the instructions card collection. */ - @objc(instructionsCardCollection:cardSizeForTraitCollection:) - optional func instructionsCardCollection(_ instructionsCardCollection: InstructionsCardViewController, cardSizeFor traitCollection: UITraitCollection) -> CGSize + func instructionsCardCollection(_ instructionsCardCollection: InstructionsCardViewController, cardSizeFor traitCollection: UITraitCollection) -> CGSize? +} + +public extension InstructionsCardCollectionDelegate { + func instructionsCardCollection(_ instructionsCardCollection: InstructionsCardViewController, didPreview step: RouteStep) { + logUnimplemented(protocolType: InstructionsCardCollectionDelegate.self, level: .debug) + } + + func instructionsCardCollection(_ instructionsCardCollection: InstructionsCardViewController, cardSizeFor traitCollection: UITraitCollection) -> CGSize? { + logUnimplemented(protocolType: InstructionsCardCollectionDelegate.self, level: .info) + return nil + } } diff --git a/MapboxNavigation/InstructionsCardContainerView.swift b/MapboxNavigation/InstructionsCardContainerView.swift index c9a242b22cf..843a8fd5498 100644 --- a/MapboxNavigation/InstructionsCardContainerView.swift +++ b/MapboxNavigation/InstructionsCardContainerView.swift @@ -3,10 +3,9 @@ import MapboxDirections import MapboxCoreNavigation /** + :nodoc: The `InstructionsCardContainerViewDelegate` protocol defines a method that allows an object to customize presented visual instructions within the instructions container view. */ -/// :nodoc: -@objc(MBInstructionsCardContainerViewDelegate) public protocol InstructionsCardContainerViewDelegate: VisualInstructionDelegate { /** Called when the Primary Label will present a visual instruction. @@ -15,9 +14,9 @@ public protocol InstructionsCardContainerViewDelegate: VisualInstructionDelegate - parameter instruction: the `VisualInstruction` that will be presented. - parameter presented: the formatted string that is provided by the instruction presenter - returns: optionally, a customized NSAttributedString that will be presented instead of the default, or if nil, the default behavior will be used. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(primaryLabel:willPresentVisualInstruction:asAttributedString:) - optional func primaryLabel(_ primaryLabel: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? + func primaryLabel(_ primaryLabel: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? /** Called when the Secondary Label will present a visual instruction. @@ -26,15 +25,25 @@ public protocol InstructionsCardContainerViewDelegate: VisualInstructionDelegate - parameter instruction: the `VisualInstruction` that will be presented. - parameter presented: the formatted string that is provided by the instruction presenter - returns: optionally, a customized NSAttributedString that will be presented instead of the default, or if nil, the default behavior will be used. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(secondaryLabel:willPresentVisualInstruction:asAttributedString:) - optional func secondaryLabel(_ secondaryLabel: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? + func secondaryLabel(_ secondaryLabel: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? +} + +public extension InstructionsCardContainerViewDelegate { + func primaryLabel(_ primaryLabel: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { + logUnimplemented(protocolType: InstructionsCardContainerViewDelegate.self, level: .debug) + return nil + } + + func secondaryLabel(_ secondaryLabel: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { + logUnimplemented(protocolType: InstructionsCardContainerViewDelegate.self,level: .debug) + return nil + } } /// :nodoc: -@objc(MBInstructionsCardContainerView) public class InstructionsCardContainerView: UIView { - lazy var informationStackView = UIStackView(orientation: .vertical, autoLayout: true) lazy var instructionsCardView: InstructionsCardView = { @@ -166,8 +175,8 @@ public class InstructionsCardContainerView: UIView { private func gradientLayer(for view: UIView, with colors:[CGColor]? = nil) -> CAGradientLayer? { guard !view.isHidden, let sublayers = view.layer.sublayers, - let firstLayer = sublayers.first as? CAGradientLayer, - let layerColors = firstLayer.colors as? [CGColor], layerColors.count == 2 else { + let firstLayer = sublayers.first as? CAGradientLayer, + let layerColors = firstLayer.colors as? [CGColor], layerColors.count == 2 else { return nil } @@ -192,7 +201,6 @@ public class InstructionsCardContainerView: UIView { lanesView.update(for: instruction) nextBannerView.instructionDelegate = self nextBannerView.update(for: instruction) - } public func updateInstructionCard(distance: CLLocationDistance) { @@ -251,7 +259,7 @@ public class InstructionsCardContainerView: UIView { fileprivate func highlightLanesView(_ gradientLayer: CAGradientLayer, colors: [CGColor]) { gradientLayer.colors = colors - guard let stackView = lanesView.subviews.first as? UIStackView else { + guard let stackView = lanesView.subviews.first as? UIStackView else { return } let laneViews: [LaneView] = stackView.subviews.compactMap { $0 as? LaneView } @@ -285,12 +293,11 @@ public class InstructionsCardContainerView: UIView { /// :nodoc: extension InstructionsCardContainerView: InstructionsCardContainerViewDelegate { public func label(_ label: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { - if let primaryLabel = label as? PrimaryLabel, - let presented = delegate?.primaryLabel?(primaryLabel, willPresent: instruction, as: presented) { + let presented = delegate?.primaryLabel(primaryLabel, willPresent: instruction, as: presented) { return presented } else if let secondaryLabel = label as? SecondaryLabel, - let presented = delegate?.secondaryLabel?(secondaryLabel, willPresent: instruction, as: presented) { + let presented = delegate?.secondaryLabel(secondaryLabel, willPresent: instruction, as: presented) { return presented } else { let highlighted = instructionsCardView.distanceFromCurrentLocation < InstructionsCardConstants.highlightDistance diff --git a/MapboxNavigation/InstructionsCardStyle.swift b/MapboxNavigation/InstructionsCardStyle.swift index 3aba78213de..b39df932a93 100644 --- a/MapboxNavigation/InstructionsCardStyle.swift +++ b/MapboxNavigation/InstructionsCardStyle.swift @@ -1,5 +1,5 @@ /// :nodoc: -@objc public protocol InstructionsCardStyle: class { +public protocol InstructionsCardStyle: class { var cornerRadius: CGFloat { get set } var backgroundColor: UIColor { get set } var highlightedBackgroundColor: UIColor { get set } diff --git a/MapboxNavigation/InstructionsCardView.swift b/MapboxNavigation/InstructionsCardView.swift index 06ed86182e5..ad267720439 100644 --- a/MapboxNavigation/InstructionsCardView.swift +++ b/MapboxNavigation/InstructionsCardView.swift @@ -4,7 +4,6 @@ import MapboxCoreNavigation /// :nodoc: public class InstructionsCardView: BaseInstructionsBannerView { - @objc dynamic var cardWidthFactor: CGFloat = 0.82 @objc dynamic var cardHeight: CGFloat = 100.0 @@ -74,4 +73,71 @@ public class InstructionsCardView: BaseInstructionsBannerView { secondaryLabel.normalFont = style.secondaryLabelNormalFont secondaryLabel.normalTextColor = style.secondaryLabelTextColor } + + // MARK: - Layout + override func setupLayout() { + // firstColumnWidth is the width of the left side of the banner containing the maneuver view and distance label + let firstColumnWidth = BaseInstructionsBannerView.maneuverViewSize.width + BaseInstructionsBannerView.padding * 3 + + // Distance label + distanceLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: BaseInstructionsBannerView.padding / 2).isActive = true + distanceLabel.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -BaseInstructionsBannerView.padding / 2).isActive = true + distanceLabel.centerXAnchor.constraint(equalTo: maneuverView.centerXAnchor, constant: 0).isActive = true + distanceLabel.lastBaselineAnchor.constraint(equalTo: bottomAnchor, constant: -BaseInstructionsBannerView.padding).isActive = true + distanceLabel.topAnchor.constraint(greaterThanOrEqualTo: maneuverView.bottomAnchor).isActive = true + + // Turn arrow view + maneuverView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + maneuverView.heightAnchor.constraint(equalToConstant: BaseInstructionsBannerView.maneuverViewSize.height).isActive = true + maneuverView.topAnchor.constraint(equalTo: topAnchor, constant: BaseInstructionsBannerView.padding).isActive = true + maneuverView.centerXAnchor.constraint(equalTo: leadingAnchor, constant: firstColumnWidth / 2).isActive = true + + // Primary Label + primaryLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: InstructionsCardConstants.primaryLabelWidth).isActive = true + primaryLabel.leadingAnchor.constraint(equalTo: maneuverView.trailingAnchor, + constant: InstructionsCardConstants.primaryLabelLeadingPadding).isActive = true + primaryLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -18).isActive = true + baselineConstraints.append(primaryLabel.topAnchor.constraint(equalTo: maneuverView.topAnchor, constant: -BaseInstructionsBannerView.padding/2)) + centerYConstraints.append(primaryLabel.centerYAnchor.constraint(equalTo: centerYAnchor)) + + // Secondary Label + secondaryLabel.widthAnchor.constraint(lessThanOrEqualToConstant: InstructionsCardConstants.secondaryLabelWidth).isActive = true + secondaryLabel.leadingAnchor.constraint(equalTo: maneuverView.trailingAnchor).isActive = true + baselineConstraints.append(secondaryLabel.lastBaselineAnchor.constraint(equalTo: distanceLabel.lastBaselineAnchor, constant: -BaseInstructionsBannerView.padding / 2)) + baselineConstraints.append(secondaryLabel.topAnchor.constraint(greaterThanOrEqualTo: primaryLabel.bottomAnchor, constant: 0)) + centerYConstraints.append(secondaryLabel.topAnchor.constraint(greaterThanOrEqualTo: primaryLabel.bottomAnchor, constant: 0)) + + // Visible separator docked to the bottom + separatorView.heightAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale).isActive = true + separatorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + separatorView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + separatorView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + } + + // Aligns the instruction to the center Y (used for single line primary and/or secondary instructions) + override func centerYAlignInstructions() { + baselineConstraints.forEach { $0.isActive = false } + centerYConstraints.forEach { $0.isActive = true } + } + + // Aligns primary top to the top of the maneuver view and the secondary baseline to the distance baseline (used for multiline) + override func baselineAlignInstructions() { + centerYConstraints.forEach { $0.isActive = false } + baselineConstraints.forEach { $0.isActive = true } + } + + override func setupAvailableBounds() { + // Abbreviate if the instructions do not fit on one line + primaryLabel.availableBounds = { [unowned self] in + // Available width H:|-padding-maneuverView-padding-availableWidth-padding-| + let availableWidth = self.bounds.width - BaseInstructionsBannerView.maneuverViewSize.width - BaseInstructionsBannerView.padding * 3 + return CGRect(x: 0, y: 0, width: availableWidth, height: self.primaryLabel.font.lineHeight) + } + + secondaryLabel.availableBounds = { [unowned self] in + // Available width H:|-padding-maneuverView-padding-availableWidth-padding-| + let availableWidth = self.bounds.width - BaseInstructionsBannerView.maneuverViewSize.width - BaseInstructionsBannerView.padding * 3 + return CGRect(x: 0, y: 0, width: availableWidth, height: self.secondaryLabel.font.lineHeight) + } + } } diff --git a/MapboxNavigation/InstructionsCardViewController.swift b/MapboxNavigation/InstructionsCardViewController.swift index c3118228400..4ae58311caa 100644 --- a/MapboxNavigation/InstructionsCardViewController.swift +++ b/MapboxNavigation/InstructionsCardViewController.swift @@ -52,7 +52,7 @@ open class InstructionsCardViewController: UIViewController { /** The InstructionsCardCollection delegate. */ - @objc public weak var cardCollectionDelegate: InstructionsCardCollectionDelegate? + public weak var cardCollectionDelegate: InstructionsCardCollectionDelegate? fileprivate var contentOffsetBeforeSwipe = CGPoint(x: 0, y: 0) fileprivate var indexBeforeSwipe = IndexPath(row: 0, section: 0) @@ -70,7 +70,7 @@ open class InstructionsCardViewController: UIViewController { super.viewDidLoad() /* TODO: Identify the traitCollections to define the width of the cards */ - if let customSize = cardCollectionDelegate?.instructionsCardCollection?(self, cardSizeFor: traitCollection) { + if let customSize = cardCollectionDelegate?.instructionsCardCollection(self, cardSizeFor: traitCollection) { cardSize = customSize } else { cardSize = CGSize(width: Int(floor(view.frame.size.width * 0.82)), height: 200) @@ -142,7 +142,7 @@ open class InstructionsCardViewController: UIViewController { } } - @objc open func updateVisibleInstructionCards(at indexPaths: [IndexPath]) { + open func updateVisibleInstructionCards(at indexPaths: [IndexPath]) { guard let legProgress = routeProgress?.currentLegProgress else { return } let remainingSteps = legProgress.remainingSteps guard let currentCardStep = remainingSteps.first else { return } @@ -239,7 +239,8 @@ extension InstructionsCardViewController: UICollectionViewDelegate { isInPreview = true let previewIndex = indexPath.row - if isInPreview, let steps = steps, previewIndex < steps.endIndex { + assert(previewIndex >= 0, "Preview Index should not be negative") + if isInPreview, let steps = steps, previewIndex >= 0, previewIndex < steps.endIndex { let step = steps[previewIndex] cardCollectionDelegate?.instructionsCardCollection(self, didPreview: step) } @@ -299,13 +300,12 @@ extension InstructionsCardViewController: NavigationComponent { /// :nodoc: extension InstructionsCardViewController: InstructionsCardContainerViewDelegate { - public func primaryLabel(_ primaryLabel: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { - return cardCollectionDelegate?.primaryLabel?(primaryLabel, willPresent: instruction, as: presented) + return cardCollectionDelegate?.primaryLabel(primaryLabel, willPresent: instruction, as: presented) } public func secondaryLabel(_ secondaryLabel: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { - return cardCollectionDelegate?.secondaryLabel?(secondaryLabel, willPresent: instruction, as: presented) + return cardCollectionDelegate?.secondaryLabel(secondaryLabel, willPresent: instruction, as: presented) } } diff --git a/MapboxNavigation/InstructionsCardViewLayout.swift b/MapboxNavigation/InstructionsCardViewLayout.swift deleted file mode 100644 index 6881091af55..00000000000 --- a/MapboxNavigation/InstructionsCardViewLayout.swift +++ /dev/null @@ -1,70 +0,0 @@ -/// :nodoc: -extension InstructionsCardView { - - override func setupLayout() { - // firstColumnWidth is the width of the left side of the banner containing the maneuver view and distance label - let firstColumnWidth = BaseInstructionsBannerView.maneuverViewSize.width + BaseInstructionsBannerView.padding * 3 - - // Distance label - distanceLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: BaseInstructionsBannerView.padding / 2).isActive = true - distanceLabel.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -BaseInstructionsBannerView.padding / 2).isActive = true - distanceLabel.centerXAnchor.constraint(equalTo: maneuverView.centerXAnchor, constant: 0).isActive = true - distanceLabel.lastBaselineAnchor.constraint(equalTo: bottomAnchor, constant: -BaseInstructionsBannerView.padding).isActive = true - distanceLabel.topAnchor.constraint(greaterThanOrEqualTo: maneuverView.bottomAnchor).isActive = true - - // Turn arrow view - maneuverView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - maneuverView.heightAnchor.constraint(equalToConstant: BaseInstructionsBannerView.maneuverViewSize.height).isActive = true - maneuverView.topAnchor.constraint(equalTo: topAnchor, constant: BaseInstructionsBannerView.padding).isActive = true - maneuverView.centerXAnchor.constraint(equalTo: leadingAnchor, constant: firstColumnWidth / 2).isActive = true - - // Primary Label - primaryLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: InstructionsCardConstants.primaryLabelWidth).isActive = true - primaryLabel.leadingAnchor.constraint(equalTo: maneuverView.trailingAnchor, - constant: InstructionsCardConstants.primaryLabelLeadingPadding).isActive = true - primaryLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -18).isActive = true - baselineConstraints.append(primaryLabel.topAnchor.constraint(equalTo: maneuverView.topAnchor, constant: -BaseInstructionsBannerView.padding/2)) - centerYConstraints.append(primaryLabel.centerYAnchor.constraint(equalTo: centerYAnchor)) - - // Secondary Label - secondaryLabel.widthAnchor.constraint(lessThanOrEqualToConstant: InstructionsCardConstants.secondaryLabelWidth).isActive = true - secondaryLabel.leadingAnchor.constraint(equalTo: maneuverView.trailingAnchor).isActive = true - baselineConstraints.append(secondaryLabel.lastBaselineAnchor.constraint(equalTo: distanceLabel.lastBaselineAnchor, constant: -BaseInstructionsBannerView.padding / 2)) - baselineConstraints.append(secondaryLabel.topAnchor.constraint(greaterThanOrEqualTo: primaryLabel.bottomAnchor, constant: 0)) - centerYConstraints.append(secondaryLabel.topAnchor.constraint(greaterThanOrEqualTo: primaryLabel.bottomAnchor, constant: 0)) - - // Visible separator docked to the bottom - separatorView.heightAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale).isActive = true - separatorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - separatorView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - separatorView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true - - } - - // Aligns the instruction to the center Y (used for single line primary and/or secondary instructions) - override func centerYAlignInstructions() { - baselineConstraints.forEach { $0.isActive = false } - centerYConstraints.forEach { $0.isActive = true } - } - - // Aligns primary top to the top of the maneuver view and the secondary baseline to the distance baseline (used for multiline) - override func baselineAlignInstructions() { - centerYConstraints.forEach { $0.isActive = false } - baselineConstraints.forEach { $0.isActive = true } - } - - override func setupAvailableBounds() { - // Abbreviate if the instructions do not fit on one line - primaryLabel.availableBounds = { [unowned self] in - // Available width H:|-padding-maneuverView-padding-availableWidth-padding-| - let availableWidth = self.bounds.width - BaseInstructionsBannerView.maneuverViewSize.width - BaseInstructionsBannerView.padding * 3 - return CGRect(x: 0, y: 0, width: availableWidth, height: self.primaryLabel.font.lineHeight) - } - - secondaryLabel.availableBounds = { [unowned self] in - // Available width H:|-padding-maneuverView-padding-availableWidth-padding-| - let availableWidth = self.bounds.width - BaseInstructionsBannerView.maneuverViewSize.width - BaseInstructionsBannerView.padding * 3 - return CGRect(x: 0, y: 0, width: availableWidth, height: self.secondaryLabel.font.lineHeight) - } - } -} diff --git a/MapboxNavigation/LaneView.swift b/MapboxNavigation/LaneView.swift index 74226f5f152..79947311bff 100644 --- a/MapboxNavigation/LaneView.swift +++ b/MapboxNavigation/LaneView.swift @@ -2,9 +2,7 @@ import UIKit import MapboxDirections /// :nodoc: -@objc(MBLaneView) open class LaneView: UIView { - let invalidAlpha: CGFloat = 0.4 var lane: Lane? { @@ -68,7 +66,6 @@ open class LaneView: UIView { if let lane = lane { if lane.indications.isSuperset(of: [.straightAhead, .sharpRight]) || lane.indications.isSuperset(of: [.straightAhead, .right]) || lane.indications.isSuperset(of: [.straightAhead, .slightRight]) { - if !isValid { if lane.indications == .slightRight { LanesStyleKit.drawLaneSlightRight(frame: bounds, resizing: resizing, primaryColor: appropriatePrimaryColor) diff --git a/MapboxNavigation/LanesStyleKit.swift b/MapboxNavigation/LanesStyleKit.swift index baca38c8b60..e93a306647b 100644 --- a/MapboxNavigation/LanesStyleKit.swift +++ b/MapboxNavigation/LanesStyleKit.swift @@ -13,7 +13,7 @@ import UIKit -@objc(MBLanesStyleKit) + public class LanesStyleKit : NSObject { //// Drawing Methods @@ -641,7 +641,8 @@ public class LanesStyleKit : NSObject { - @objc(LanesStyleKitResizingBehavior) + + @objc(MBLanesStyleKitResizingBehavior) public enum ResizingBehavior: Int { case aspectFit /// The content is proportionally resized to fit into the target rectangle. case aspectFill /// The content is proportionally resized to completely fill the target rectangle. diff --git a/MapboxNavigation/LanesView.swift b/MapboxNavigation/LanesView.swift index 960d9ba4cec..796fb36f10a 100644 --- a/MapboxNavigation/LanesView.swift +++ b/MapboxNavigation/LanesView.swift @@ -4,7 +4,6 @@ import MapboxDirections /// :nodoc: @IBDesignable -@objc(MBLanesView) open class LanesView: UIView, NavigationComponent { weak var stackView: UIStackView! weak var separatorView: SeparatorView! @@ -67,21 +66,19 @@ open class LanesView: UIView, NavigationComponent { separatorView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true } - @objc public func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { + public func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { update(for: instruction) } /** Updates the tertiary instructions banner info with a given `VisualInstructionBanner`. */ - @objc(updateForVisualInstructionBanner:) public func update(for visualInstruction: VisualInstructionBanner?) { clearLaneViews() - guard let tertiaryInstruction = visualInstruction?.tertiaryInstruction, - tertiaryInstruction.containsLaneIndications else { - hide() - return + guard let tertiaryInstruction = visualInstruction?.tertiaryInstruction, tertiaryInstruction.containsLaneIndications else { + hide() + return } let laneIndications: [LaneIndicationComponent]? = tertiaryInstruction.components.compactMap({ $0 as? LaneIndicationComponent }) @@ -125,5 +122,4 @@ open class LanesView: UIView, NavigationComponent { $0.removeFromSuperview() } } - } diff --git a/MapboxNavigation/MBConstants.h b/MapboxNavigation/MBConstants.h deleted file mode 100644 index db695162a9f..00000000000 --- a/MapboxNavigation/MBConstants.h +++ /dev/null @@ -1,26 +0,0 @@ -#import - -/** - Key used for constructing errors when spoken instructions fail. - */ -extern const _Nonnull NSErrorUserInfoKey MBSpokenInstructionErrorCodeKey; - -/** - Posted when `MBStyleManager` applies a new style triggered by change of day of time, or when entering or exiting a tunnel. - */ -extern const _Nonnull NSNotificationName MBStyleManagerDidApplyStyleNotification; - -/** - Keys in the user info dictionaries of various notifications posted by instances of `MBStyleManager`. - */ -typedef NSString *MBStyleManagerNotificationUserInfoKey NS_EXTENSIBLE_STRING_ENUM; - -/** - A key in the user info dictionary of `MBStyleManagerDidApplyStyleNotification` notification. The corresponding value is an `MBStyle` instance that was applied. - */ -extern const _Nonnull MBStyleManagerNotificationUserInfoKey MBStyleManagerStyleKey; - -/** - A key in the user info dictionary of `MBStyleManagerDidApplyStyleNotification` notification. The corresponding value is an `MBStyleManager` instance that applied the style. - */ -extern const _Nonnull MBStyleManagerNotificationUserInfoKey MBStyleManagerStyleManagerKey; diff --git a/MapboxNavigation/MBConstants.m b/MapboxNavigation/MBConstants.m deleted file mode 100644 index b79c881d403..00000000000 --- a/MapboxNavigation/MBConstants.m +++ /dev/null @@ -1,9 +0,0 @@ -#import "MBConstants.h" - -const NSErrorUserInfoKey MBSpokenInstructionErrorCodeKey = @"MBSpokenInstructionErrorCode"; - -const NSNotificationName MBStyleManagerDidApplyStyleNotification = @"MBStyleManagerDidApplyStyle"; - -const MBStyleManagerNotificationUserInfoKey MBStyleManagerStyleKey = @"style"; - -const MBStyleManagerNotificationUserInfoKey MBStyleManagerStyleManagerKey = @"styleManager"; diff --git a/MapboxNavigation/MGLAccountManager.swift b/MapboxNavigation/MGLAccountManager.swift index 91480155d87..2950fa1c7a0 100644 --- a/MapboxNavigation/MGLAccountManager.swift +++ b/MapboxNavigation/MGLAccountManager.swift @@ -1,18 +1,15 @@ import Foundation import Mapbox - extension MGLAccountManager{ - // Mapbox China base API URL static let mapboxChinaBaseAPIURL = "https://api.mapbox.cn" /** Returns true if the map's endpoint is China. */ - @objc public class var hasChinaBaseURL : Bool{ + public class var hasChinaBaseURL : Bool{ let apiBaseURL = Bundle.main.object(forInfoDictionaryKey:"MGLMapboxAPIBaseURL") as? String return apiBaseURL == mapboxChinaBaseAPIURL } - } diff --git a/MapboxNavigation/MGLStyle.swift b/MapboxNavigation/MGLStyle.swift index d4a8418fe5a..d2c2a206d00 100644 --- a/MapboxNavigation/MGLStyle.swift +++ b/MapboxNavigation/MGLStyle.swift @@ -1,9 +1,7 @@ import Foundation import Mapbox - extension MGLStyle { - // The Mapbox China Day Style URL. static let mapboxChinaDayStyleURL = URL(string:"mapbox://styles/mapbox/streets-zh-v1")! @@ -13,7 +11,7 @@ extension MGLStyle { /** Returns the URL to the current version of the Mapbox Navigation Guidance Day style. */ - @objc public class var navigationGuidanceDayStyleURL: URL { + public class var navigationGuidanceDayStyleURL: URL { get { if(MGLAccountManager.hasChinaBaseURL){ return mapboxChinaDayStyleURL @@ -25,7 +23,7 @@ extension MGLStyle { /** Returns the URL to the current version of the Mapbox Navigation Guidance Night style. */ - @objc public class var navigationGuidanceNightStyleURL: URL { + public class var navigationGuidanceNightStyleURL: URL { get { if(MGLAccountManager.hasChinaBaseURL){ return mapboxChinaDayStyleURL @@ -39,20 +37,19 @@ extension MGLStyle { We only have one version of navigation guidance style in China, so if you switch your endpoint to .cn, it will return the default day style. */ - @objc public class func navigationGuidanceDayStyleURL(version: Int) -> URL { + public class func navigationGuidanceDayStyleURL(version: Int) -> URL { if(MGLAccountManager.hasChinaBaseURL){ return mapboxChinaDayStyleURL } return URL(string:"mapbox://styles/mapbox/navigation-guidance-day-v\(version)")! } - /** Returns the URL to the given version of the navigation guidance style. Available version are 2, 3, and 4. We only have one version of navigation guidance style in China, so if you switch your endpoint to .cn, it will return the default night style. */ - @objc public class func navigationGuidanceNightStyleURL(version: Int) -> URL { + public class func navigationGuidanceNightStyleURL(version: Int) -> URL { if(MGLAccountManager.hasChinaBaseURL){ return mapboxChinaNightStyleURL } @@ -62,7 +59,7 @@ extension MGLStyle { /** Returns the URL to the current version of the Mapbox Navigation Preview Day style. */ - @objc public class var navigationPreviewDayStyleURL: URL { + public class var navigationPreviewDayStyleURL: URL { get { if MGLAccountManager.hasChinaBaseURL { return mapboxChinaDayStyleURL @@ -74,7 +71,7 @@ extension MGLStyle { /** Returns the URL to the current version of the Mapbox Navigation Preview Night style. */ - @objc public class var navigationPreviewNightStyleURL: URL { + public class var navigationPreviewNightStyleURL: URL { get { if MGLAccountManager.hasChinaBaseURL { return mapboxChinaDayStyleURL @@ -88,20 +85,19 @@ extension MGLStyle { We only have one version of Navigation Preview style in China, so if you switch your endpoint to .cn, it will return the default day style. */ - @objc public class func navigationPreviewDayStyleURL(version: Int) -> URL { + public class func navigationPreviewDayStyleURL(version: Int) -> URL { if MGLAccountManager.hasChinaBaseURL { return mapboxChinaDayStyleURL } return URL(string:"mapbox://styles/mapbox/navigation-guidance-day-v\(version)")! } - /** Returns the URL to the given version of the Mapbox Navigation Preview Night style. Available versions are 2, 3, and 4. We only have one version of Navigation Preview style in China, so if you switch your endpoint to .cn, it will return the default night style. */ - @objc public class func navigationPreviewNightStyleURL(version: Int) -> URL { + public class func navigationPreviewNightStyleURL(version: Int) -> URL { if MGLAccountManager.hasChinaBaseURL { return mapboxChinaNightStyleURL } diff --git a/MapboxNavigation/ManeuverView.swift b/MapboxNavigation/ManeuverView.swift index 6079b4f0683..fafe8984844 100644 --- a/MapboxNavigation/ManeuverView.swift +++ b/MapboxNavigation/ManeuverView.swift @@ -5,9 +5,7 @@ import Turf /// :nodoc: @IBDesignable -@objc(MBManeuverView) open class ManeuverView: UIView { - @objc public dynamic var primaryColor: UIColor = .defaultTurnArrowPrimary { didSet { setNeedsDisplay() @@ -20,13 +18,13 @@ open class ManeuverView: UIView { } } - @objc public var isStart = false { + public var isStart = false { didSet { setNeedsDisplay() } } - @objc public var isEnd = false { + public var isEnd = false { didSet { setNeedsDisplay() } @@ -42,7 +40,7 @@ open class ManeuverView: UIView { /** The current instruction displayed in the maneuver view. */ - @objc public var visualInstruction: VisualInstruction? { + public var visualInstruction: VisualInstruction? { didSet { setNeedsDisplay() } @@ -51,7 +49,7 @@ open class ManeuverView: UIView { /** This indicates the side of the road currently driven on. */ - @objc public var drivingSide: DrivingSide = .right { + public var drivingSide: DrivingSide = .right { didSet { setNeedsDisplay() } diff --git a/MapboxNavigation/ManeuversStyleKit.swift b/MapboxNavigation/ManeuversStyleKit.swift index 5302bb1fc40..a5ddad0f1a0 100644 --- a/MapboxNavigation/ManeuversStyleKit.swift +++ b/MapboxNavigation/ManeuversStyleKit.swift @@ -1,6 +1,6 @@ import UIKit -@objc(MBManeuversStyleKit) + public class ManeuversStyleKit: NSObject { //// Drawing Methods @@ -933,7 +933,7 @@ public class ManeuversStyleKit: NSObject { } - @objc(ManeuversStyleKitResizingBehavior) + @objc(MBManeuversStyleKitResizingBehavior) public enum ResizingBehavior: Int { case aspectFit /// The content is proportionally resized to fit into the target rectangle. case aspectFill /// The content is proportionally resized to completely fill the target rectangle. diff --git a/MapboxNavigation/MapboxNavigation.h b/MapboxNavigation/MapboxNavigation.h index 70de67180df..03185c67bfe 100644 --- a/MapboxNavigation/MapboxNavigation.h +++ b/MapboxNavigation/MapboxNavigation.h @@ -6,5 +6,4 @@ FOUNDATION_EXPORT double MapboxNavigationVersionNumber; //! Project version string for MapboxNavigation. FOUNDATION_EXPORT const unsigned char MapboxNavigationVersionString[]; -#import "MBConstants.h" #import "MGLMapView+MGLNavigationAdditions.h" diff --git a/MapboxNavigation/MapboxVoiceController.swift b/MapboxNavigation/MapboxVoiceController.swift index bf557e5199d..a4141a9f249 100644 --- a/MapboxNavigation/MapboxVoiceController.swift +++ b/MapboxNavigation/MapboxVoiceController.swift @@ -16,23 +16,21 @@ import MapboxDirections The Mapbox Voice API is optimized for spoken instructions provided by the Mapbox Directions API via the MapboxDirections.swift framework. If you need text-to-speech functionality outside the context of a navigation service, use the Speech Synthesis framework’s `AVSpeechSynthesizer` class directly. */ -@objc(MBMapboxVoiceController) open class MapboxVoiceController: RouteVoiceController, AVAudioPlayerDelegate { - /** Number of seconds a request can wait before it is canceled and the default speech synthesizer speaks the instruction. */ - @objc public var timeoutIntervalForRequest: TimeInterval = 5 + public var timeoutIntervalForRequest: TimeInterval = 5 /** Number of steps ahead of the current step to cache spoken instructions. */ - @objc public var stepsAheadToCache: Int = 3 + public var stepsAheadToCache: Int = 3 /** An `AVAudioPlayer` through which spoken instructions are played. */ - @objc public var audioPlayer: AVAudioPlayer? + public var audioPlayer: AVAudioPlayer? var audioTask: URLSessionDataTask? var cache: BimodalDataCache @@ -42,8 +40,8 @@ open class MapboxVoiceController: RouteVoiceController, AVAudioPlayerDelegate { var locale: Locale? let localizedErrorMessage = NSLocalizedString("FAILED_INSTRUCTION", bundle: .mapboxNavigation, value: "Unable to read instruction aloud.", comment: "Error message when the SDK is unable to read a spoken instruction.") - - @objc public init(navigationService: NavigationService, speechClient: SpeechSynthesizer = SpeechSynthesizer(accessToken: nil), dataCache: BimodalDataCache = DataCache(), audioPlayerType: AVAudioPlayer.Type? = nil) { + + public init(navigationService: NavigationService, speechClient: SpeechSynthesizer = SpeechSynthesizer(accessToken: nil), dataCache: BimodalDataCache = DataCache(), audioPlayerType: AVAudioPlayer.Type? = nil) { speech = speechClient cache = dataCache self.audioPlayerType = audioPlayerType ?? AVAudioPlayer.self @@ -58,12 +56,10 @@ open class MapboxVoiceController: RouteVoiceController, AVAudioPlayerDelegate { muteToken = NavigationSettings.shared.observe(\.voiceMuted) { [weak self] (settings, change) in if settings.voiceMuted { self?.audioPlayer?.stop() - + guard let strongSelf = self else { return } - do { - try strongSelf.unDuckAudio() - } catch { - strongSelf.voiceControllerDelegate?.voiceController?(strongSelf, spokenInstructionsDidFailWith: error) + strongSelf.safeUnduckAudio(instruction: nil, engine: self?.speech) { + strongSelf.voiceControllerDelegate?.voiceController(strongSelf, spokenInstructionsDidFailWith: $0) } } } @@ -71,27 +67,25 @@ open class MapboxVoiceController: RouteVoiceController, AVAudioPlayerDelegate { deinit { audioPlayer?.stop() - do { - try unDuckAudio() - } catch { - voiceControllerDelegate?.voiceController?(self, spokenInstructionsDidFailWith: error) + + safeUnduckAudio(instruction: nil, engine: speech) { + voiceControllerDelegate?.voiceController(self, spokenInstructionsDidFailWith: $0) } + audioPlayer?.delegate = nil } - @objc public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { - do { - try unDuckAudio() - } catch { - voiceControllerDelegate?.voiceController?(self, spokenInstructionsDidFailWith: error) + public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { + safeUnduckAudio(instruction: nil, engine: speech) { + voiceControllerDelegate?.voiceController(self, spokenInstructionsDidFailWith: $0) } } - - @objc open override func didPassSpokenInstructionPoint(notification: NSNotification) { + + open override func didPassSpokenInstructionPoint(notification: NSNotification) { let routeProgresss = notification.userInfo![RouteControllerNotificationUserInfoKey.routeProgressKey] as! RouteProgress locale = routeProgresss.route.routeOptions.locale let currentLegProgress: RouteLegProgress = routeProgresss.currentLegProgress - + let instructionSets = currentLegProgress.remainingSteps.prefix(stepsAheadToCache).compactMap { $0.instructionsSpokenAlongStep } let instructions = instructionSets.flatMap { $0 } let unfetchedInstructions = instructions.filter { !hasCachedSpokenInstructionForKey($0.ssmlText) } @@ -100,64 +94,65 @@ open class MapboxVoiceController: RouteVoiceController, AVAudioPlayerDelegate { super.didPassSpokenInstructionPoint(notification: notification) } - + /** Speaks an instruction. The cache is first checked to see if we have already downloaded the speech file. If not, the instruction is fetched and played. If there is an error anywhere along the way, the instruction will be spoken with the default speech synthesizer. */ - @objc open override func speak(_ instruction: SpokenInstruction) { + open override func speak(_ instruction: SpokenInstruction) { if let audioPlayer = audioPlayer, audioPlayer.isPlaying, let lastSpokenInstruction = lastSpokenInstruction { - voiceControllerDelegate?.voiceController?(self, didInterrupt: lastSpokenInstruction, with: instruction) + voiceControllerDelegate?.voiceController(self, didInterrupt: lastSpokenInstruction, with: instruction) } audioTask?.cancel() audioPlayer?.stop() - assert(routeProgress != nil, "routeProgress should not be nil.") + guard let progress = routeProgress else { + assertionFailure("routeProgress should not be nil.") + return + } - guard let _ = routeProgress!.route.speechLocale else { - speakWithDefaultSpeechSynthesizer(instruction, error: nil) + guard progress.route.speechLocale != nil else { + let wrapped = SpeechError.undefinedSpeechLocale(instruction: instruction, progress: progress) + speakWithDefaultSpeechSynthesizer(instruction, error: wrapped) return } - let modifiedInstruction = voiceControllerDelegate?.voiceController?(self, willSpeak: instruction, routeProgress: routeProgress!) ?? instruction + let modifiedInstruction = voiceControllerDelegate?.voiceController(self, willSpeak: instruction, routeProgress: routeProgress!) ?? instruction lastSpokenInstruction = modifiedInstruction - + if let data = cachedDataForKey(modifiedInstruction.ssmlText) { - play(data) + play(instruction: instruction, data: data) return } fetchAndSpeak(instruction: modifiedInstruction) } - + /** Speaks an instruction with the built in speech synthesizer. This method should be used in cases where `fetch(instruction:)` or `play(_:)` fails. */ - @objc open func speakWithDefaultSpeechSynthesizer(_ instruction: SpokenInstruction, error: Error?) { + open func speakWithDefaultSpeechSynthesizer(_ instruction: SpokenInstruction, error: SpeechError?) { audioTask?.cancel() if let error = error { - voiceControllerDelegate?.voiceController?(self, spokenInstructionsDidFailWith: error) + voiceControllerDelegate?.voiceController(self, didFallBackTo: speechSynth, error: error) } - guard let audioPlayer = audioPlayer else { - super.speak(instruction) + guard !(audioPlayer?.isPlaying ?? false) else { return } - guard !audioPlayer.isPlaying else { return } - super.speak(instruction) } /** Fetches and plays an instruction. */ - @objc open func fetchAndSpeak(instruction: SpokenInstruction) { + open func fetchAndSpeak(instruction: SpokenInstruction) { audioTask?.cancel() let ssmlText = instruction.ssmlText let options = SpeechOptions(ssml: ssmlText) @@ -170,25 +165,27 @@ open class MapboxVoiceController: RouteVoiceController, AVAudioPlayerDelegate { if let error = error as? URLError, error.code == .cancelled { return } else if let error = error { - strongSelf.speakWithDefaultSpeechSynthesizer(instruction, error: error) + let wrapped = SpeechError.apiError(instruction: instruction, options: options, underlying: error) + strongSelf.speakWithDefaultSpeechSynthesizer(instruction, error: wrapped) return } guard let data = data else { - strongSelf.speakWithDefaultSpeechSynthesizer(instruction, error: NSError(code: .spokenInstructionFailed, localizedFailureReason: strongSelf.localizedErrorMessage, spokenInstructionCode: .emptyMapboxSpeechResponse)) + let wrapped = SpeechError.noData(instruction: instruction, options: options) + strongSelf.speakWithDefaultSpeechSynthesizer(instruction, error: wrapped) return } - strongSelf.play(data) + strongSelf.play(instruction: instruction, data: data) strongSelf.cache(data, forKey: ssmlText) } audioTask?.resume() } - + /** Caches an instruction in an in-memory cache. */ - @objc open func downloadAndCacheSpokenInstruction(instruction: SpokenInstruction) { + open func downloadAndCacheSpokenInstruction(instruction: SpokenInstruction) { let ssmlText = instruction.ssmlText let options = SpeechOptions(ssml: ssmlText) if let locale = locale { @@ -198,7 +195,7 @@ open class MapboxVoiceController: RouteVoiceController, AVAudioPlayerDelegate { if let locale = routeProgress?.route.speechLocale { options.locale = locale } - + speech.audioData(with: options) { [weak self] (data, error) in guard let data = data else { return @@ -206,44 +203,65 @@ open class MapboxVoiceController: RouteVoiceController, AVAudioPlayerDelegate { self?.cache(data, forKey: ssmlText) } } - + private func cache(_ data: Data, forKey key: String) { cache.store(data, forKey: key, toDisk: true, completion: nil) } - + internal func cachedDataForKey(_ key: String) -> Data? { return cache.data(forKey: key) } - + internal func hasCachedSpokenInstructionForKey(_ key: String) -> Bool { return cachedDataForKey(key) != nil } - + + func safeInitalizeAudioPlayer(playerType: AVAudioPlayer.Type, data: Data, instruction: SpokenInstruction, engine: Any?, failure: AudioControlFailureHandler) -> AVAudioPlayer? { + do { + let player = try playerType.init(data: data) + return player + } catch { + let wrapped = SpeechError.unableToInitializePlayer(playerType: playerType, instruction: instruction, synthesizer: engine, underlying: error) + failure(wrapped) + return nil + } + } + /** Plays an audio file. */ - @objc open func play(_ data: Data) { - + open func play(instruction: SpokenInstruction, data: Data) { + let fallback: (SpeechError) -> Void = { [weak self] (error) in + self?.speakWithDefaultSpeechSynthesizer(instruction, error: error) + } + super.speechSynth.stopSpeaking(at: .immediate) audioQueue.async { [weak self] in guard let strongSelf = self else { return } - do { - strongSelf.audioPlayer = try strongSelf.audioPlayerType.init(data: data) - strongSelf.audioPlayer?.prepareToPlay() - strongSelf.audioPlayer?.delegate = strongSelf - try strongSelf.duckAudio() - let played = strongSelf.audioPlayer?.play() ?? false - - guard played else { - try strongSelf.unDuckAudio() - strongSelf.speakWithDefaultSpeechSynthesizer(strongSelf.lastSpokenInstruction!, error: NSError(code: .spokenInstructionFailed, localizedFailureReason: strongSelf.localizedErrorMessage, spokenInstructionCode: .audioPlayerFailedToPlay)) - return - } - - } catch let error as NSError { - strongSelf.speakWithDefaultSpeechSynthesizer(strongSelf.lastSpokenInstruction!, error: error) + strongSelf.audioPlayer = strongSelf.safeInitalizeAudioPlayer(playerType: strongSelf.audioPlayerType, data: data, instruction: instruction, engine: strongSelf.speech, failure: fallback) + strongSelf.audioPlayer?.prepareToPlay() + strongSelf.audioPlayer?.delegate = strongSelf + + strongSelf.safeDuckAudio(instruction: instruction, engine: strongSelf.speech, failure: fallback) + + let played = strongSelf.audioPlayer?.play() ?? false + + guard played else { + strongSelf.safeUnduckAudio(instruction: instruction, engine: strongSelf.speech, failure: fallback) + return } } } } + +//MARK: - Obsolete +extension MapboxVoiceController { + @available(*, deprecated, renamed: "MapboxVoiceController.play(instruction:data:)") + open func play(_ data: Data) { + guard let instruction = routeProgress?.currentLegProgress.currentStepProgress.currentSpokenInstruction else { + return + } + self.play(instruction: instruction, data: data) + } +} diff --git a/MapboxNavigation/NavigationComponent.swift b/MapboxNavigation/NavigationComponent.swift index 356ae843d48..ac6356a17bf 100644 --- a/MapboxNavigation/NavigationComponent.swift +++ b/MapboxNavigation/NavigationComponent.swift @@ -2,46 +2,42 @@ import Foundation import MapboxCoreNavigation import CoreLocation -/* +/** A navigation component is a member of the navigation UI view hierarchy that responds as the user progresses along a route according to the `NavigationServiceDelegate` protocol. */ -@objc public protocol NavigationComponent: NavigationServiceDelegate {} - +public protocol NavigationComponent: NavigationServiceDelegate {} /** The NavigationInteractionDelegate protocol is used to define interaction events that the top banner may need to know about. */ -@objc public protocol NavigationMapInteractionObserver: class { +public protocol NavigationMapInteractionObserver: class { /** Called when the NavigationMapView centers on a location. */ - @objc(navigationViewControllerDidCenterOnLocation:) func navigationViewController(didCenterOn location: CLLocation) } /** The CarPlayConnectionObserver protocol provides notification of a carplay unit connecting two the NavigationViewController. */ -@objc public protocol CarPlayConnectionObserver: class { - +public protocol CarPlayConnectionObserver: class { /** Called when the NavigationViewController detects that a CarPlay device has been connected. */ - @objc func didConnectToCarPlay() + func didConnectToCarPlay() /** Called when the NavigationViewController detects that a CarPlay device has been connected. */ - @objc func didDisconnectFromCarPlay() + func didDisconnectFromCarPlay() } - /** This protocol defines a UI Component that is capable of presenting a status message. */ -@objc public protocol NavigationStatusPresenter: class { +public protocol NavigationStatusPresenter: class { /** Shows the status view for a specified amount of time. */ - @objc func showStatus(title: String, spinner: Bool, duration: TimeInterval, animated: Bool, interactive: Bool) + func showStatus(title: String, spinner: Bool, duration: TimeInterval, animated: Bool, interactive: Bool) } diff --git a/MapboxNavigation/NavigationCustomizable.swift b/MapboxNavigation/NavigationCustomizable.swift index d1290585b95..7cf8159e020 100644 --- a/MapboxNavigation/NavigationCustomizable.swift +++ b/MapboxNavigation/NavigationCustomizable.swift @@ -5,7 +5,6 @@ import MapboxCoreNavigation The `NavigationCustomizable` protocol represents a UI-based mechanism that allows for customization of its visual style, as well as the navigation service that powers it. */ protocol NavigationCustomizable { - /** The navigation service that manages navigation along the route. */ diff --git a/MapboxNavigation/NavigationInteractionDelegate.swift b/MapboxNavigation/NavigationInteractionDelegate.swift deleted file mode 100644 index b1420c1e20d..00000000000 --- a/MapboxNavigation/NavigationInteractionDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ - -@objc public protocol NavigationInteractionDelegate: class { - - @objc optional func navigationViewController(_ controller: NavigationViewController, didRecenterAt location: CLLocation) - @objc optional func navigationViewControllerDidConnectCarPlay(_ controller: NavigationViewController) - @objc optional func navigationViewControllerDidDisconnectCarPlay(_ controller: NavigationViewController) - @objc optional func showStatus(title: String, withSpinner: Bool, for time: TimeInterval, animated: Bool, interactive: Bool) - -} diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index 3ff6e0b84ef..89bea213942 100644 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -7,9 +7,7 @@ import Turf /** `NavigationMapView` is a subclass of `MGLMapView` with convenience functions for adding `Route` lines to a map. */ -@objc(MBNavigationMapView) open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { - // MARK: Class Constants struct FrameIntervalOptions { @@ -24,34 +22,34 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { This property takes effect when the application has limited resources for animation, such as when the device is running on battery power. By default, this property is set to `MGLMapViewPreferredFramesPerSecond.lowPower`. */ - @objc public var minimumFramesPerSecond = MGLMapViewPreferredFramesPerSecond(20) + public var minimumFramesPerSecond = MGLMapViewPreferredFramesPerSecond(20) /** Returns the altitude that the map camera initally defaults to. */ - @objc public var defaultAltitude: CLLocationDistance = 1000.0 + public var defaultAltitude: CLLocationDistance = 1000.0 /** Returns the altitude the map conditionally zooms out to when user is on a motorway, and the maneuver length is sufficently long. - */ - @objc public var zoomedOutMotorwayAltitude: CLLocationDistance = 2000.0 + */ + public var zoomedOutMotorwayAltitude: CLLocationDistance = 2000.0 /** Returns the threshold for what the map considers a "long-enough" maneuver distance to trigger a zoom-out when the user enters a motorway. */ - @objc public var longManeuverDistance: CLLocationDistance = 1000.0 + public var longManeuverDistance: CLLocationDistance = 1000.0 /** Maximum distance the user can tap for a selection to be valid when selecting an alternate route. */ - @objc public var tapGestureDistanceThreshold: CGFloat = 50 + public var tapGestureDistanceThreshold: CGFloat = 50 /** The object that acts as the navigation delegate of the map view. */ - @objc public weak var navigationMapViewDelegate: NavigationMapViewDelegate? + public weak var navigationMapViewDelegate: NavigationMapViewDelegate? @available(*, deprecated, renamed: "navigationMapViewDelegate") - @objc public weak var navigationMapDelegate: NavigationMapViewDelegate? { + public weak var navigationMapDelegate: NavigationMapViewDelegate? { get { return navigationMapViewDelegate } set { navigationMapViewDelegate = newValue} } @@ -116,7 +114,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { open override var showsUserLocation: Bool { get { if tracksUserCourse || userLocationForCourseTracking != nil { - return !(userCourseView?.isHidden ?? true) + return !(userCourseView.isHidden) } return super.showsUserLocation } @@ -124,12 +122,9 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { if tracksUserCourse || userLocationForCourseTracking != nil { super.showsUserLocation = false - if userCourseView == nil { - userCourseView = UserPuckCourseView(frame: CGRect(origin: .zero, size: CGSize(width: 75, height: 75))) - } - userCourseView?.isHidden = !newValue + userCourseView.isHidden = !newValue } else { - userCourseView?.isHidden = true + userCourseView.isHidden = true super.showsUserLocation = newValue } } @@ -145,7 +140,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { - seealso: NavigationMapViewDelegate.navigationMapViewUserAnchorPoint(_:) */ var userAnchorPoint: CGPoint { - if let anchorPoint = navigationMapViewDelegate?.navigationMapViewUserAnchorPoint?(self), anchorPoint != .zero { + if let anchorPoint = navigationMapViewDelegate?.navigationMapViewUserAnchorPoint(self), anchorPoint != .zero { return anchorPoint } let contentFrame = bounds.inset(by: contentInset) @@ -162,9 +157,9 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { enableFrameByFrameCourseViewTracking(for: 3) altitude = defaultAltitude showsUserLocation = true - courseTrackingDelegate?.navigationMapViewDidStartTrackingCourse?(self) + courseTrackingDelegate?.navigationMapViewDidStartTrackingCourse(self) } else { - courseTrackingDelegate?.navigationMapViewDidStopTrackingCourse?(self) + courseTrackingDelegate?.navigationMapViewDidStopTrackingCourse(self) } if let location = userLocationForCourseTracking { updateCourseTracking(location: location, animated: true) @@ -173,19 +168,19 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { } /** - A `UIView` used to indicate the user’s location and course on the map. + A type that represents a `UIView` that is `CourseUpdatable`. + */ + public typealias UserCourseView = UIView & CourseUpdatable + + /** + A `UserCourseView` used to indicate the user’s location and course on the map. - If the view conforms to `UserCourseView`, its `UserCourseView.update(location:pitch:direction:animated:)` method is frequently called to ensure that its visual appearance matches the map’s camera. + The `UserCourseView`'s `UserCourseView.update(location:pitch:direction:animated:)` method is frequently called to ensure that its visual appearance matches the map’s camera. */ - @objc public var userCourseView: UIView? { + public var userCourseView: UserCourseView = UserPuckCourseView(frame: CGRect(origin: .zero, size: 75.0)) { didSet { - oldValue?.removeFromSuperview() - if let userCourseView = userCourseView { - if let location = userLocationForCourseTracking { - updateCourseTracking(location: location, animated: false) - } - addSubview(userCourseView) - } + oldValue.removeFromSuperview() + installUserCourseView() } } @@ -219,6 +214,8 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let mapTapGesture = self.mapTapGesture mapTapGesture.requireFailure(of: gestures) addGestureRecognizer(mapTapGesture) + + installUserCourseView() } open override func layoutMarginsDidChange() { @@ -262,7 +259,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { guard shouldPositionCourseViewFrameByFrame else { return } guard let location = userLocationForCourseTracking else { return } - userCourseView?.center = convert(location.coordinate, toPointTo: self) + userCourseView.center = convert(location.coordinate, toPointTo: self) } /** @@ -270,7 +267,6 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { This method accounts for the proximity to a maneuver and the current power source. It has no effect if `tracksUserCourse` is set to `true`. */ - @objc(updatePreferredFrameRateForRouteProgress:) open func updatePreferredFrameRate(for routeProgress: RouteProgress) { guard tracksUserCourse else { return } @@ -307,12 +303,19 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { //MARK: - User Tracking + func installUserCourseView() { + if let location = userLocationForCourseTracking { + updateCourseTracking(location: location, animated: false) + } + addSubview(userCourseView) + } + @objc private func disableUserCourseTracking() { guard tracksUserCourse else { return } tracksUserCourse = false } - @objc public func updateCourseTracking(location: CLLocation?, camera: MGLMapCamera? = nil, animated: Bool = false) { + public func updateCourseTracking(location: CLLocation?, camera: MGLMapCamera? = nil, animated: Bool = false) { // While animating to overhead mode, don't animate the puck. let duration: TimeInterval = animated && !isAnimatingToOverheadMode ? 1 : 0 animatesUserLocation = animated @@ -329,19 +332,11 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { // Animate course view updates in overview mode UIView.animate(withDuration: duration, delay: 0, options: [.curveLinear], animations: { [weak self] in guard let point = self?.convert(location.coordinate, toPointTo: self) else { return } - self?.userCourseView?.center = point + self?.userCourseView.center = point }) } - if let userCourseView = userCourseView as? UserCourseView { - if let customTransformation = userCourseView.update?(location: location, pitch: self.camera.pitch, direction: direction, animated: animated, tracksUserCourse: tracksUserCourse) { - customTransformation - } else { - self.userCourseView?.applyDefaultUserPuckTransformation(location: location, pitch: self.camera.pitch, direction: direction, animated: animated, tracksUserCourse: tracksUserCourse) - } - } else { - userCourseView?.applyDefaultUserPuckTransformation(location: location, pitch: self.camera.pitch, direction: direction, animated: animated, tracksUserCourse: tracksUserCourse) - } + userCourseView.update(location: location, pitch: self.camera.pitch, direction: direction, animated: animated, tracksUserCourse: tracksUserCourse) } //MARK: - Gesture Recognizers @@ -354,17 +349,14 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let waypointTest = waypoints(on: routes, closeTo: tapPoint) //are there waypoints near the tapped location? if let selected = waypointTest?.first { //test passes - navigationMapViewDelegate?.navigationMapView?(self, didSelect: selected) + navigationMapViewDelegate?.navigationMapView(self, didSelect: selected) return } else if let routes = self.routes(closeTo: tapPoint) { guard let selectedRoute = routes.first else { return } - navigationMapViewDelegate?.navigationMapView?(self, didSelect: selectedRoute) + navigationMapViewDelegate?.navigationMapView(self, didSelect: selectedRoute) } - } - typealias CompositeCourseView = UIView & UserCourseView - @objc func updateCourseView(_ sender: UIGestureRecognizer) { if sender.state == .ended { altitude = self.camera.altitude @@ -391,35 +383,33 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { if sender.state == .changed { guard let location = userLocationForCourseTracking else { return } - if let userCourseView = userCourseView as? CompositeCourseView { - userCourseView.update?(location: location, - pitch: camera.pitch, - direction: direction, - animated: false, - tracksUserCourse: tracksUserCourse) - - userCourseView.center = convert(location.coordinate, toPointTo: self) - } + userCourseView.update(location: location, + pitch: camera.pitch, + direction: direction, + animated: false, + tracksUserCourse: tracksUserCourse) + + userCourseView.center = convert(location.coordinate, toPointTo: self) } } // MARK: Feature Addition/Removal /** Showcases route array. Adds routes and waypoints to map, and sets camera to point encompassing the route. - */ + */ public static let defaultPadding: UIEdgeInsets = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20) - @objc public func showcase(_ routes: [Route], animated: Bool = false) { + public func showcase(_ routes: [Route], animated: Bool = false) { guard let active = routes.first, - let coords = active.coordinates, - !coords.isEmpty else { return } //empty array + let coords = active.coordinates, + !coords.isEmpty else { return } //empty array removeArrow() removeRoutes() removeWaypoints() - showRoutes(routes) - showWaypoints(active) + show(routes) + showWaypoints(on: active) fit(to: active, facing: 0, animated: animated) } @@ -440,13 +430,13 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { /** Adds or updates both the route line and the route line casing */ - @objc public func showRoutes(_ routes: [Route], legIndex: Int = 0) { + public func show(_ routes: [Route], legIndex: Int = 0) { guard let style = style else { return } guard let mainRoute = routes.first else { return } self.routes = routes - let polylines = navigationMapViewDelegate?.navigationMapView?(self, shapeFor: routes) ?? shape(for: routes, legIndex: legIndex) - let mainPolylineSimplified = navigationMapViewDelegate?.navigationMapView?(self, simplifiedShapeFor: mainRoute) ?? shape(forCasingOf: mainRoute, legIndex: legIndex) + let polylines = navigationMapViewDelegate?.navigationMapView(self, shapeFor: routes) ?? shape(for: routes, legIndex: legIndex) + let mainPolylineSimplified = navigationMapViewDelegate?.navigationMapView(self, simplifiedShapeFor: mainRoute) ?? shape(forCasingOf: mainRoute, legIndex: legIndex) if let source = style.source(withIdentifier: sourceIdentifier) as? MGLShapeSource, let sourceSimplified = style.source(withIdentifier: sourceCasingIdentifier) as? MGLShapeSource { @@ -458,8 +448,8 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { style.addSource(lineSource) style.addSource(lineCasingSource) - let line = navigationMapViewDelegate?.navigationMapView?(self, routeStyleLayerWithIdentifier: routeLayerIdentifier, source: lineSource) ?? routeStyleLayer(identifier: routeLayerIdentifier, source: lineSource) - let lineCasing = navigationMapViewDelegate?.navigationMapView?(self, routeCasingStyleLayerWithIdentifier: routeLayerCasingIdentifier, source: lineCasingSource) ?? routeCasingStyleLayer(identifier: routeLayerCasingIdentifier, source: lineSource) + let line = navigationMapViewDelegate?.navigationMapView(self, routeStyleLayerWithIdentifier: routeLayerIdentifier, source: lineSource) ?? routeStyleLayer(identifier: routeLayerIdentifier, source: lineSource) + let lineCasing = navigationMapViewDelegate?.navigationMapView(self, routeCasingStyleLayerWithIdentifier: routeLayerCasingIdentifier, source: lineCasingSource) ?? routeCasingStyleLayer(identifier: routeLayerCasingIdentifier, source: lineSource) for layer in style.layers.reversed() { if !(layer is MGLSymbolStyleLayer) && @@ -475,7 +465,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { /** Removes route line and route line casing from map */ - @objc public func removeRoutes() { + public func removeRoutes() { guard let style = style else { return } @@ -500,14 +490,14 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { /** Adds the route waypoints to the map given the current leg index. Previous waypoints for completed legs will be omitted. */ - @objc public func showWaypoints(_ route: Route, legIndex: Int = 0) { + public func showWaypoints(on route: Route, legIndex: Int = 0) { guard let style = style else { return } let waypoints: [Waypoint] = Array(route.legs.map { $0.destination }.dropLast()) - let source = navigationMapViewDelegate?.navigationMapView?(self, shapeFor: waypoints, legIndex: legIndex) ?? shape(for: waypoints, legIndex: legIndex) + let source = navigationMapViewDelegate?.navigationMapView(self, shapeFor: waypoints, legIndex: legIndex) ?? shape(for: waypoints, legIndex: legIndex) if route.routeOptions.waypoints.count > 2 { //are we on a multipoint route? routes = [route] //update the model @@ -517,8 +507,8 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let sourceShape = MGLShapeSource(identifier: waypointSourceIdentifier, shape: source, options: sourceOptions) style.addSource(sourceShape) - let circles = navigationMapViewDelegate?.navigationMapView?(self, waypointStyleLayerWithIdentifier: waypointCircleIdentifier, source: sourceShape) ?? routeWaypointCircleStyleLayer(identifier: waypointCircleIdentifier, source: sourceShape) - let symbols = navigationMapViewDelegate?.navigationMapView?(self, waypointSymbolStyleLayerWithIdentifier: waypointSymbolIdentifier, source: sourceShape) ?? routeWaypointSymbolStyleLayer(identifier: waypointSymbolIdentifier, source: sourceShape) + let circles = navigationMapViewDelegate?.navigationMapView(self, waypointStyleLayerWithIdentifier: waypointCircleIdentifier, source: sourceShape) ?? routeWaypointCircleStyleLayer(identifier: waypointCircleIdentifier, source: sourceShape) + let symbols = navigationMapViewDelegate?.navigationMapView(self, waypointSymbolStyleLayerWithIdentifier: waypointSymbolIdentifier, source: sourceShape) ?? routeWaypointSymbolStyleLayer(identifier: waypointSymbolIdentifier, source: sourceShape) if let arrowLayer = style.layer(withIdentifier: arrowCasingSymbolLayerIdentifier) { style.insertLayer(circles, above: arrowLayer) @@ -545,7 +535,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { /** Removes all waypoints from the map. */ - @objc public func removeWaypoints() { + public func removeWaypoints() { guard let style = style else { return } removeAnnotations(annotationsToRemove() ?? []) @@ -570,7 +560,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { /** Shows the step arrow given the current `RouteProgress`. */ - @objc public func addArrow(route: Route, legIndex: Int, stepIndex: Int) { + public func addArrow(route: Route, legIndex: Int, stepIndex: Int) { guard route.legs.indices.contains(legIndex), route.legs[legIndex].steps.indices.contains(stepIndex) else { return } @@ -627,7 +617,6 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { if let source = style.source(withIdentifier: arrowSourceStrokeIdentifier) as? MGLShapeSource { source.shape = arrowStrokeShape } else { - arrowStroke.minimumZoomLevel = arrow.minimumZoomLevel arrowStroke.lineCap = arrow.lineCap arrowStroke.lineJoin = arrow.lineJoin @@ -674,14 +663,13 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { style.insertLayer(arrowSymbolLayer, above: arrow) style.insertLayer(arrowSymbolLayerCasing, below: arrow) } - } } /** Removes the step arrow from the map. */ - @objc public func removeArrow() { + public func removeArrow() { guard let style = style else { return } @@ -762,7 +750,6 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { //Sort routes by closest distance to tap gesture. let closest = routes.sorted { (left, right) -> Bool in - //existance has been assured through use of filter. let leftLine = Polyline(left.coordinates!) let rightLine = Polyline(right.coordinates!) @@ -845,7 +832,6 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { var segments: [CongestionSegment] = [] segments.reserveCapacity(congestions.count) for (index, congestion) in congestions.enumerated() { - let congestionSegment: ([CLLocationCoordinate2D], CongestionLevel) = ([coordinates[index], coordinates[index + 1]], congestion) let coordinates = congestionSegment.0 let congestionLevel = congestionSegment.1 @@ -922,7 +908,6 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { } func routeStyleLayer(identifier: String, source: MGLSource) -> MGLStyleLayer { - let line = MGLLineStyleLayer(identifier: identifier, source: source) line.lineWidth = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", MBRouteLineWidthByZoomLevel) line.lineOpacity = NSExpression(forConditional: @@ -938,15 +923,14 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { } func routeCasingStyleLayer(identifier: String, source: MGLSource) -> MGLStyleLayer { - let lineCasing = MGLLineStyleLayer(identifier: identifier, source: source) // Take the default line width and make it wider for the casing lineCasing.lineWidth = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", MBRouteLineWidthByZoomLevel.multiplied(by: 1.5)) lineCasing.lineColor = NSExpression(forConditional: NSPredicate(format: "isAlternateRoute == true"), - trueExpression: NSExpression(forConstantValue: routeAlternateCasingColor), - falseExpression: NSExpression(forConstantValue: routeCasingColor)) + trueExpression: NSExpression(forConstantValue: routeAlternateCasingColor), + falseExpression: NSExpression(forConstantValue: routeCasingColor)) lineCasing.lineCap = NSExpression(forConstantValue: "round") lineCasing.lineJoin = NSExpression(forConstantValue: "round") @@ -978,7 +962,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { `NavigationViewController` is localized automatically, so you do not need to call this method on the value of `NavigationViewController.mapView`. */ - @objc public func localizeLabels() { + public func localizeLabels() { guard MGLAccountManager.hasChinaBaseURL == false else{ return } @@ -1016,7 +1000,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { } } - @objc public func showVoiceInstructionsOnMap(route: Route) { + public func showVoiceInstructionsOnMap(route: Route) { guard let style = style else { return } @@ -1060,11 +1044,10 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { } } - /** Sets the camera directly over a series of coordinates. */ - @objc public func setOverheadCameraView(from userLocation: CLLocationCoordinate2D, along coordinates: [CLLocationCoordinate2D], for padding: UIEdgeInsets) { + public func setOverheadCameraView(from userLocation: CLLocationCoordinate2D, along coordinates: [CLLocationCoordinate2D], for padding: UIEdgeInsets) { isAnimatingToOverheadMode = true let line = MGLPolyline(coordinates: coordinates, count: UInt(coordinates.count)) @@ -1102,9 +1085,23 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { /** Recenters the camera and begins tracking the user's location. */ - @objc public func recenterMap() { + public func recenterMap() { tracksUserCourse = true enableFrameByFrameCourseViewTracking(for: 3) } } +// MARK: - Deprecated + +extension NavigationMapView { + @available(*, deprecated, renamed: "show(_:legIndex:)") + public func showRoutes(_ routes: [Route], legIndex: Int = 0) { + self.show(routes, legIndex: legIndex) + } + + @available(*, deprecated, renamed: "showWaypoints(on:legIndex:)") + public func showWaypoints(_ route: Route, legIndex: Int = 0) { + showWaypoints(on: route, legIndex: legIndex) + } +} + diff --git a/MapboxNavigation/NavigationMapViewDelegate.swift b/MapboxNavigation/NavigationMapViewDelegate.swift index 34c7af4d784..78df882b2b0 100644 --- a/MapboxNavigation/NavigationMapViewDelegate.swift +++ b/MapboxNavigation/NavigationMapViewDelegate.swift @@ -5,8 +5,7 @@ import MapboxCoreNavigation /** The `NavigationMapViewDelegate` provides methods for configuring the NavigationMapView, as well as responding to events triggered by the NavigationMapView. */ -@objc(MBNavigationMapViewDelegate) -public protocol NavigationMapViewDelegate: class { +public protocol NavigationMapViewDelegate: class, UnimplementedLogging { /** Asks the receiver to return an MGLStyleLayer for routes, given an identifier and source. This method is invoked when the map view loads and any time routes are added. @@ -14,8 +13,9 @@ public protocol NavigationMapViewDelegate: class { - parameter identifier: The style identifier. - parameter source: The Layer source containing the route data that this method would style. - returns: An MGLStyleLayer that the map applies to all routes. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationMapView(_ mapView: NavigationMapView, routeStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? + func navigationMapView(_ mapView: NavigationMapView, routeStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? /** Asks the receiver to return an MGLStyleLayer for waypoints, given an identifier and source. @@ -24,8 +24,9 @@ public protocol NavigationMapViewDelegate: class { - parameter identifier: The style identifier. - parameter source: The Layer source containing the waypoint data that this method would style. - returns: An MGLStyleLayer that the map applies to all waypoints. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationMapView(_ mapView: NavigationMapView, waypointStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? + func navigationMapView(_ mapView: NavigationMapView, waypointStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? /** Asks the receiver to return an MGLStyleLayer for waypoint symbols, given an identifier and source. @@ -34,8 +35,9 @@ public protocol NavigationMapViewDelegate: class { - parameter identifier: The style identifier. - parameter source: The Layer source containing the waypoint data that this method would style. - returns: An MGLStyleLayer that the map applies to all waypoint symbols. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationMapView(_ mapView: NavigationMapView, waypointSymbolStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? + func navigationMapView(_ mapView: NavigationMapView, waypointSymbolStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? /** Asks the receiver to return an MGLStyleLayer for route casings, given an identifier and source. @@ -45,24 +47,25 @@ public protocol NavigationMapViewDelegate: class { - parameter identifier: The style identifier. - parameter source: The Layer source containing the route data that this method would style. - returns: An MGLStyleLayer that the map applies to the route. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationMapView(_ mapView: NavigationMapView, routeCasingStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? + func navigationMapView(_ mapView: NavigationMapView, routeCasingStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? /** Tells the receiver that the user has selected a route by interacting with the map view. - parameter mapView: The NavigationMapView. - parameter route: The route that was selected. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationMapView:didSelectRoute:) - optional func navigationMapView(_ mapView: NavigationMapView, didSelect route: Route) + func navigationMapView(_ mapView: NavigationMapView, didSelect route: Route) /** Tells the receiver that a waypoint was selected. - parameter mapView: The NavigationMapView. - parameter waypoint: The waypoint that was selected. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationMapView:didSelectWaypoint:) - optional func navigationMapView(_ mapView: NavigationMapView, didSelect waypoint: Waypoint) + func navigationMapView(_ mapView: NavigationMapView, didSelect waypoint: Waypoint) /** Asks the receiver to return an MGLShape that describes the geometry of the route. @@ -70,9 +73,9 @@ public protocol NavigationMapViewDelegate: class { - parameter mapView: The NavigationMapView. - parameter routes: The routes that the sender is asking about. The first route will always be rendered as the main route, while all subsequent routes will be rendered as alternate routes. - returns: Optionally, a `MGLShape` that defines the shape of the route, or `nil` to use default behavior. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationMapView:shapeForRoutes:) - optional func navigationMapView(_ mapView: NavigationMapView, shapeFor routes: [Route]) -> MGLShape? + func navigationMapView(_ mapView: NavigationMapView, shapeFor routes: [Route]) -> MGLShape? /** Asks the receiver to return an MGLShape that describes the geometry of the route at lower zoomlevels. @@ -80,59 +83,125 @@ public protocol NavigationMapViewDelegate: class { - parameter mapView: The NavigationMapView. - parameter route: The route that the sender is asking about. - returns: Optionally, a `MGLShape` that defines the shape of the route at lower zoomlevels, or `nil` to use default behavior. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationMapView:simplifiedShapeForRoute:) - optional func navigationMapView(_ mapView: NavigationMapView, simplifiedShapeFor route: Route) -> MGLShape? + func navigationMapView(_ mapView: NavigationMapView, simplifiedShapeFor route: Route) -> MGLShape? /** Asks the receiver to return an MGLShape that describes the geometry of the waypoint. - parameter mapView: The NavigationMapView. - parameter waypoints: The waypoints to be displayed on the map. - returns: Optionally, a `MGLShape` that defines the shape of the waypoint, or `nil` to use default behavior. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationMapView:shapeForWaypoints:legIndex:) - optional func navigationMapView(_ mapView: NavigationMapView, shapeFor waypoints: [Waypoint], legIndex: Int) -> MGLShape? + func navigationMapView(_ mapView: NavigationMapView, shapeFor waypoints: [Waypoint], legIndex: Int) -> MGLShape? /** Asks the receiver to return a CGPoint to serve as the anchor for the user icon. - important: The return value should be returned in the normal UIKit coordinate-space, NOT CoreAnimation's unit coordinate-space. - parameter mapView: The NavigationMapView. - returns: A CGPoint (in regular coordinate-space) that represents the point on-screen where the user location icon should be drawn. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationMapViewUserAnchorPoint:) - optional func navigationMapViewUserAnchorPoint(_ mapView: NavigationMapView) -> CGPoint - + func navigationMapViewUserAnchorPoint(_ mapView: NavigationMapView) -> CGPoint //MARK: Obsolete @available(*, deprecated, message: "The NavigationMapView no longer forwards MGLMapViewDelegate messages. Use MGLMapViewDelegate.mapView(_:imageFor:) instead.") - @objc(navigationMapView:imageForAnnotation:) - optional func navigationMapView(_ mapView: MGLMapView, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage? + + func navigationMapView(_ mapView: MGLMapView, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage? @available(*, deprecated, message: "The NavigationMapView no longer forwards MGLMapViewDelegate messages. Use MGLMapViewDelegate.mapView(_:viewFor:) instead.") - @objc(navigationMapView:viewForAnnotation:) - optional func navigationMapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? + + func navigationMapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? +} + +public extension NavigationMapViewDelegate { + func navigationMapView(_ mapView: NavigationMapView, routeStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + return nil + } + + func navigationMapView(_ mapView: NavigationMapView, waypointStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + return nil + } + + func navigationMapView(_ mapView: NavigationMapView, waypointSymbolStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + return nil + } + + func navigationMapView(_ mapView: NavigationMapView, routeCasingStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + return nil + } + + func navigationMapView(_ mapView: NavigationMapView, didSelect route: Route) { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + } + + func navigationMapView(_ mapView: NavigationMapView, didSelect waypoint: Waypoint) { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + } + + func navigationMapView(_ mapView: NavigationMapView, shapeFor routes: [Route]) -> MGLShape? { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + return nil + } + + func navigationMapView(_ mapView: NavigationMapView, simplifiedShapeFor route: Route) -> MGLShape? { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + return nil + } + + func navigationMapView(_ mapView: NavigationMapView, shapeFor waypoints: [Waypoint], legIndex: Int) -> MGLShape? { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + return nil + } + + func navigationMapViewUserAnchorPoint(_ mapView: NavigationMapView) -> CGPoint { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + return .zero + } + + func navigationMapView(_ mapView: MGLMapView, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage? { + //no-op, deprecated + return nil + } + + func navigationMapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? { + //no-op, deprecated + return nil + } } // MARK: NavigationMapViewCourseTrackingDelegate /** The `NavigationMapViewCourseTrackingDelegate` provides methods for responding to the `NavigationMapView` starting or stopping course tracking. */ -@objc(MBNavigationMapViewCourseTrackingDelegate) -public protocol NavigationMapViewCourseTrackingDelegate: class { +public protocol NavigationMapViewCourseTrackingDelegate: class, UnimplementedLogging { /** Tells the receiver that the map is now tracking the user course. - seealso: NavigationMapView.tracksUserCourse - parameter mapView: The NavigationMapView. */ - @objc(navigationMapViewDidStartTrackingCourse:) - optional func navigationMapViewDidStartTrackingCourse(_ mapView: NavigationMapView) + func navigationMapViewDidStartTrackingCourse(_ mapView: NavigationMapView) /** Tells the receiver that `tracksUserCourse` was set to false, signifying that the map is no longer tracking the user course. - seealso: NavigationMapView.tracksUserCourse - parameter mapView: The NavigationMapView. */ - @objc(navigationMapViewDidStopTrackingCourse:) - optional func navigationMapViewDidStopTrackingCourse(_ mapView: NavigationMapView) + func navigationMapViewDidStopTrackingCourse(_ mapView: NavigationMapView) +} + +public extension NavigationMapViewCourseTrackingDelegate { + func navigationMapViewDidStartTrackingCourse(_ mapView: NavigationMapView) { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + } + + func navigationMapViewDidStopTrackingCourse(_ mapView: NavigationMapView) { + logUnimplemented(protocolType: NavigationMapViewDelegate.self, level: .debug) + } } diff --git a/MapboxNavigation/NavigationOptions.swift b/MapboxNavigation/NavigationOptions.swift index c0b9545d7ea..2e3eb4be12a 100644 --- a/MapboxNavigation/NavigationOptions.swift +++ b/MapboxNavigation/NavigationOptions.swift @@ -8,40 +8,37 @@ import MapboxCoreNavigation - note: `NavigationOptions` is designed to be used with the `NavigationViewController` class to customize the user experience. To specify criteria when calculating routes, use the `NavigationRouteOptions` class. To modify user preferences that persist across navigation sessions, use the `NavigationSettings` class. */ - -@objc(MBNavigationOptions) open class NavigationOptions: NSObject, NavigationCustomizable { - /** The styles that the view controller’s internal `StyleManager` object can select from for display. If this property is set to `nil`, a `DayStyle` and a `NightStyle` are created to be used as the view controller’s styles. This property is set to `nil` by default. */ - @objc open var styles: [Style]? = nil + open var styles: [Style]? = nil /** The navigation service that manages navigation along the route. */ - @objc open var navigationService: NavigationService? + open var navigationService: NavigationService? /** The voice controller that manages the delivery of voice instructions during navigation. - */ - @objc open var voiceController: RouteVoiceController? + */ + open var voiceController: RouteVoiceController? /** The view controller to embed into the top section of the UI. If this property is set to `nil`, a `TopBannerViewController` is created and embedded in the UI. This property is set to `nil` by default. */ - @objc open var topBanner: ContainerViewController? + open var topBanner: ContainerViewController? /** The view controller to embed into the bottom section of the UI. If this property is set to `nil`, a `BottomBannerViewController` is created and embedded in the UI. This property is set to `nil` by default. */ - @objc open var bottomBanner: ContainerViewController? + open var bottomBanner: ContainerViewController? // This makes the compiler happy. required public override init() { @@ -57,7 +54,7 @@ open class NavigationOptions: NSObject, NavigationCustomizable { - parameter topBanner: The container view controller that presents the top banner. - parameter bottomBanner: The container view controller that presents the bottom banner. */ - @objc public convenience init(styles: [Style]? = nil, navigationService: NavigationService? = nil, voiceController: RouteVoiceController? = nil, topBanner: ContainerViewController? = nil, bottomBanner: ContainerViewController? = nil) { + public convenience init(styles: [Style]? = nil, navigationService: NavigationService? = nil, voiceController: RouteVoiceController? = nil, topBanner: ContainerViewController? = nil, bottomBanner: ContainerViewController? = nil) { self.init() self.styles = styles self.navigationService = navigationService @@ -69,7 +66,7 @@ open class NavigationOptions: NSObject, NavigationCustomizable { /** Convienence factory-method for convenient bridging to Objective-C. */ - @objc public class func navigationOptions() -> Self { + public class func navigationOptions() -> Self { return self.init() } } diff --git a/MapboxNavigation/NavigationView.swift b/MapboxNavigation/NavigationView.swift index 3496900dfd8..cbe1b0b975a 100644 --- a/MapboxNavigation/NavigationView.swift +++ b/MapboxNavigation/NavigationView.swift @@ -1,7 +1,6 @@ import UIKit import MapboxDirections - /** A view that represents the root view of the MapboxNavigation drop-in UI. @@ -34,11 +33,9 @@ import MapboxDirections | 3 | +--------------------+ ``` -*/ + */ @IBDesignable -@objc(MBNavigationView) open class NavigationView: UIView { - private enum Constants { static let endOfRouteHeight: CGFloat = 260.0 static let buttonSpacing: CGFloat = 8.0 diff --git a/MapboxNavigation/NavigationViewController+Obsolete.swift b/MapboxNavigation/NavigationViewController+Obsolete.swift index 11517febef6..78c3cb011e3 100644 --- a/MapboxNavigation/NavigationViewController+Obsolete.swift +++ b/MapboxNavigation/NavigationViewController+Obsolete.swift @@ -6,11 +6,11 @@ import MapboxDirections extension NavigationViewController { @available(*, deprecated, message: "Use the new init(route:options:) initalizer.") - @objc(initWithRoute:styles:navigationService:voiceController:) + public convenience init(for route: Route, - styles: [Style]? = nil, - navigationService: NavigationService? = nil, - voiceController: RouteVoiceController? = nil) { + styles: [Style]? = nil, + navigationService: NavigationService? = nil, + voiceController: RouteVoiceController? = nil) { let bridge = NavigationOptions() bridge.styles = styles bridge.navigationService = navigationService @@ -21,7 +21,7 @@ extension NavigationViewController { @available(*, deprecated, renamed: "navigationService", message: "NavigationViewController no longer directly manages a RouteController. See MapboxNavigationService, which contains a protocol-bound reference to the RouteController, for more information.") /// :nodoc: obsoleted - @objc public final var routeController: RouteController! { + public final var routeController: RouteController! { get { fatalError() } @@ -32,7 +32,7 @@ extension NavigationViewController { @available(*, deprecated, renamed: "navigationService.eventsManager", message: "NavigationViewController no-longer directly manages a NavigationEventsManager. See MapboxNavigationService, which contains a reference to the eventsManager, for more information.") /// :nodoc: obsoleted - @objc public final var eventsManager: NavigationEventsManager! { + public final var eventsManager: NavigationEventsManager! { get { fatalError() } @@ -43,7 +43,7 @@ extension NavigationViewController { @available(*, deprecated, renamed: "navigationService.locationManager", message: "NavigationViewController no-longer directly manages an NavigationLocationManager. See MapboxNavigationService, which contains a reference to the locationManager, for more information.") /// :nodoc: obsoleted - @objc public final var locationManager: NavigationLocationManager! { + public final var locationManager: NavigationLocationManager! { get { fatalError() } @@ -54,7 +54,7 @@ extension NavigationViewController { @available(*, deprecated, renamed: "init(for:styles:navigationService:voiceController:)", message: "Intializing a NavigationViewController directly with a RouteController is no longer supported. Use a NavigationService instead.") /// :nodoc: Obsoleted method. - @objc(initWithRoute:directions:styles:routeController:locationManager:voiceController:eventsManager:) + public convenience init(for route: Route, directions: Directions = Directions.shared, styles: [Style]? = [DayStyle(), NightStyle()], @@ -74,5 +74,4 @@ extension NavigationViewController { public class func carPlayManagerDidEndNavigation(_ carPlayManager: CarPlayManager, window: UIWindow) { fatalError() } - } diff --git a/MapboxNavigation/NavigationViewController.swift b/MapboxNavigation/NavigationViewController.swift index 7c7cc6924ce..52d2648225f 100644 --- a/MapboxNavigation/NavigationViewController.swift +++ b/MapboxNavigation/NavigationViewController.swift @@ -21,15 +21,13 @@ public typealias ContainerViewController = UIViewController & NavigationComponen `CarPlayNavigationViewController` manages the corresponding user interface on a CarPlay screen. */ -@objc(MBNavigationViewController) open class NavigationViewController: UIViewController, NavigationStatusPresenter { - - /** + /** A `Route` object constructed by [MapboxDirections](https://mapbox.github.io/mapbox-navigation-ios/directions/). In cases where you need to update the route after navigation has started you can set a new `route` here and `NavigationViewController` will update its UI accordingly. */ - @objc public var route: Route { + public var route: Route { get { return navigationService.route } @@ -37,41 +35,41 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter navigationService.route = newValue NavigationSettings.shared.distanceUnit = route.routeOptions.locale.usesMetric ? .kilometer : .mile - navigationComponents.forEach { $0.navigationService?(navigationService, didRerouteAlong: newValue, at: nil, proactive: false) } + navigationComponents.forEach { $0.navigationService(navigationService, didRerouteAlong: newValue, at: nil, proactive: false) } } } /** An instance of `Directions` need for rerouting. See [Mapbox Directions](https://mapbox.github.io/mapbox-navigation-ios/directions/) for further information. */ - @objc public var directions: Directions { + public var directions: Directions { return navigationService!.directions } /** An optional `MGLMapCamera` you can use to improve the initial transition from a previous viewport and prevent a trigger from an excessive significant location update. */ - @objc public var pendingCamera: MGLMapCamera? + public var pendingCamera: MGLMapCamera? /** An instance of `MGLAnnotation` representing the origin of your route. */ - @objc public var origin: MGLAnnotation? + public var origin: MGLAnnotation? /** The receiver’s delegate. */ - @objc public weak var delegate: NavigationViewControllerDelegate? + public weak var delegate: NavigationViewControllerDelegate? /** The voice controller that vocalizes spoken instructions along the route at the appropriate times. */ - @objc public var voiceController: RouteVoiceController! + public var voiceController: RouteVoiceController! /** The navigation service that coordinates the view controller’s nonvisual components, tracking the user’s location as they proceed along the route. */ - @objc private(set) public var navigationService: NavigationService! { + private(set) public var navigationService: NavigationService! { didSet { mapViewController?.navService = navigationService } @@ -93,17 +91,17 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter By default, this property is set to `true`, causing the user location annotation to be snapped to the route. */ - @objc public var snapsUserLocationAnnotationToRoute = true + public var snapsUserLocationAnnotationToRoute = true /** Toggles sending of UILocalNotification upon upcoming steps when application is in the background. Defaults to `true`. */ - @objc public var sendsNotifications: Bool = true + public var sendsNotifications: Bool = true /** Shows a button that allows drivers to report feedback such as accidents, closed roads, poor instructions, etc. Defaults to `true`. */ - @objc public var showsReportFeedback: Bool = true { + public var showsReportFeedback: Bool = true { didSet { mapViewController?.reportButton.isHidden = !showsReportFeedback showsEndOfRouteFeedback = showsReportFeedback @@ -111,9 +109,9 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter } /** - Shows End of route Feedback UI when the route controller arrives at the final destination. Defaults to `true.` - */ - @objc public var showsEndOfRouteFeedback: Bool = true { + Shows End of route Feedback UI when the route controller arrives at the final destination. Defaults to `true.` + */ + public var showsEndOfRouteFeedback: Bool = true { didSet { mapViewController?.showsEndOfRoute = showsEndOfRouteFeedback } @@ -122,7 +120,7 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter /** If true, the map style and UI will automatically be updated given the time of day. */ - @objc public var automaticallyAdjustsStyleForTimeOfDay = true { + public var automaticallyAdjustsStyleForTimeOfDay = true { didSet { styleManager.automaticallyAdjustsStyleForTimeOfDay = automaticallyAdjustsStyleForTimeOfDay } @@ -131,8 +129,7 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter /** If `true`, `UIApplication.isIdleTimerDisabled` is set to `true` in `viewWillAppear(_:)` and `false` in `viewWillDisappear(_:)`. If your application manages the idle timer itself, set this property to `false`. */ - @objc public var shouldManageApplicationIdleTimer = true - + public var shouldManageApplicationIdleTimer = true var isConnectedToCarPlay: Bool { if #available(iOS 12.0, *) { @@ -167,7 +164,7 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter /** A Boolean value that determines whether the map annotates the locations at which instructions are spoken for debugging purposes. */ - @objc public var annotatesSpokenInstructions = false + public var annotatesSpokenInstructions = false var styleManager: StyleManager! @@ -193,10 +190,8 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter - parameter route: The route to navigate along. - parameter options: The navigation options to use for the navigation session. */ - @objc(initWithRoute:options:) required public init(for route: Route, options: NavigationOptions? = nil) { - super.init(nibName: nil, bundle: nil) self.navigationService = options?.navigationService ?? MapboxNavigationService(route: route) @@ -210,7 +205,7 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter styleManager.styles = options?.styles ?? [DayStyle(), NightStyle()] let bottomBanner = options?.bottomBanner ?? { - let viewController = BottomBannerViewController() + let viewController: BottomBannerViewController = .init() viewController.delegate = self return viewController }() @@ -230,7 +225,6 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter self.mapViewController = mapViewController mapViewController.destination = route.legs.last?.destination mapViewController.view.translatesAutoresizingMaskIntoConstraints = false - embed(mapViewController, in: view) { (parent, map) -> [NSLayoutConstraint] in return map.view.constraintsForPinning(to: parent.view) @@ -241,7 +235,6 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter updateMapStyle(currentStyle, animated: false) } - mapViewController.view.pinInSuperview() mapViewController.reportButton.isHidden = !showsReportFeedback @@ -275,8 +268,7 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter self.navigationService.start() view.clipsToBounds = true - - + guard let firstInstruction = navigationService.routeProgress.currentLegProgress.currentStepProgress.currentVisualInstruction else { return } @@ -299,7 +291,6 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter if shouldManageApplicationIdleTimer { UIApplication.shared.isIdleTimerDisabled = false } - } func notifyUserAboutLowVolumeIfNeeded() { @@ -365,68 +356,66 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter //MARK: - RouteMapViewControllerDelegate extension NavigationViewController: RouteMapViewControllerDelegate { public func navigationMapView(_ mapView: NavigationMapView, routeCasingStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { - return delegate?.navigationViewController?(self, routeCasingStyleLayerWithIdentifier: identifier, source: source) + return delegate?.navigationViewController(self, routeCasingStyleLayerWithIdentifier: identifier, source: source) } public func navigationMapView(_ mapView: NavigationMapView, routeStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { - return delegate?.navigationViewController?(self, routeStyleLayerWithIdentifier: identifier, source: source) + return delegate?.navigationViewController(self, routeStyleLayerWithIdentifier: identifier, source: source) } public func navigationMapView(_ mapView: NavigationMapView, didSelect route: Route) { - delegate?.navigationViewController?(self, didSelect: route) + delegate?.navigationViewController(self, didSelect: route) } - @objc public func navigationMapView(_ mapView: NavigationMapView, shapeFor routes: [Route]) -> MGLShape? { - return delegate?.navigationViewController?(self, shapeFor: routes) + public func navigationMapView(_ mapView: NavigationMapView, shapeFor routes: [Route]) -> MGLShape? { + return delegate?.navigationViewController(self, shapeFor: routes) } - @objc public func navigationMapView(_ mapView: NavigationMapView, simplifiedShapeFor route: Route) -> MGLShape? { - return delegate?.navigationViewController?(self, simplifiedShapeFor: route) + public func navigationMapView(_ mapView: NavigationMapView, simplifiedShapeFor route: Route) -> MGLShape? { + return delegate?.navigationViewController(self, simplifiedShapeFor: route) } public func navigationMapView(_ mapView: NavigationMapView, waypointStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { - return delegate?.navigationViewController?(self, waypointStyleLayerWithIdentifier: identifier, source: source) + return delegate?.navigationViewController(self, waypointStyleLayerWithIdentifier: identifier, source: source) } public func navigationMapView(_ mapView: NavigationMapView, waypointSymbolStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { - return delegate?.navigationViewController?(self, waypointSymbolStyleLayerWithIdentifier: identifier, source: source) + return delegate?.navigationViewController(self, waypointSymbolStyleLayerWithIdentifier: identifier, source: source) } - @objc public func navigationMapView(_ mapView: NavigationMapView, shapeFor waypoints: [Waypoint], legIndex: Int) -> MGLShape? { - return delegate?.navigationViewController?(self, shapeFor: waypoints, legIndex: legIndex) + public func navigationMapView(_ mapView: NavigationMapView, shapeFor waypoints: [Waypoint], legIndex: Int) -> MGLShape? { + return delegate?.navigationViewController(self, shapeFor: waypoints, legIndex: legIndex) } - //Still Kept around for the EORVC. On it's way out. func mapViewControllerDidDismiss(_ mapViewController: RouteMapViewController, byCanceling canceled: Bool) { - if delegate?.navigationViewControllerDidDismiss?(self, byCanceling: canceled) != nil { + if delegate?.navigationViewControllerDidDismiss(self, byCanceling: canceled) != nil { // The receiver should handle dismissal of the NavigationViewController } else { dismiss(animated: true, completion: nil) } } - public func navigationMapViewUserAnchorPoint(_ mapView: NavigationMapView) -> CGPoint { - return delegate?.navigationViewController?(self, mapViewUserAnchorPoint: mapView) ?? .zero + return delegate?.navigationViewController(self, mapViewUserAnchorPoint: mapView) ?? .zero } func mapViewControllerShouldAnnotateSpokenInstructions(_ routeMapViewController: RouteMapViewController) -> Bool { return annotatesSpokenInstructions } - @objc func mapViewController(_ mapViewController: RouteMapViewController, roadNameAt location: CLLocation) -> String? { - guard let roadName = delegate?.navigationViewController?(self, roadNameAt: location) else { + func mapViewController(_ mapViewController: RouteMapViewController, roadNameAt location: CLLocation) -> String? { + guard let roadName = delegate?.navigationViewController(self, roadNameAt: location) else { return nil } return roadName } - @objc public func label(_ label: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { - return delegate?.label?(label, willPresent: instruction, as: presented) + public func label(_ label: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { + return delegate?.label(label, willPresent: instruction, as: presented) } - @objc func mapViewController(_ mapViewController: RouteMapViewController, didCenterOn location: CLLocation) { + func mapViewController(_ mapViewController: RouteMapViewController, didCenterOn location: CLLocation) { navigationComponents.compactMap({$0 as? NavigationMapInteractionObserver}).forEach { $0.navigationViewController(didCenterOn: location) } @@ -435,52 +424,49 @@ extension NavigationViewController: RouteMapViewControllerDelegate { //MARK: - NavigationServiceDelegate extension NavigationViewController: NavigationServiceDelegate { - - @objc public func navigationService(_ service: NavigationService, shouldRerouteFrom location: CLLocation) -> Bool { + public func navigationService(_ service: NavigationService, shouldRerouteFrom location: CLLocation) -> Bool { let defaultBehavior = RouteController.DefaultBehavior.shouldRerouteFromLocation - let componentsWantReroute = navigationComponents.allSatisfy { $0.navigationService?(service, shouldRerouteFrom: location) ?? defaultBehavior } - return componentsWantReroute && (delegate?.navigationViewController?(self, shouldRerouteFrom: location) ?? defaultBehavior) + let componentsWantReroute = navigationComponents.allSatisfy { $0.navigationService(service, shouldRerouteFrom: location) } + return componentsWantReroute && (delegate?.navigationViewController(self, shouldRerouteFrom: location) ?? defaultBehavior) } - @objc public func navigationService(_ service: NavigationService, willRerouteFrom location: CLLocation) { + public func navigationService(_ service: NavigationService, willRerouteFrom location: CLLocation) { for component in navigationComponents { - component.navigationService?(service, willRerouteFrom: location) + component.navigationService(service, willRerouteFrom: location) } - delegate?.navigationViewController?(self, willRerouteFrom: location) + delegate?.navigationViewController(self, willRerouteFrom: location) } - @objc public func navigationService(_ service: NavigationService, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) { + public func navigationService(_ service: NavigationService, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) { for component in navigationComponents { - component.navigationService?(service, didRerouteAlong: route, at: location, proactive: proactive) + component.navigationService(service, didRerouteAlong: route, at: location, proactive: proactive) } - delegate?.navigationViewController?(self, didRerouteAlong: route) + delegate?.navigationViewController(self, didRerouteAlong: route) } - @objc public func navigationService(_ service: NavigationService, didFailToRerouteWith error: Error) { + public func navigationService(_ service: NavigationService, didFailToRerouteWith error: Error) { for component in navigationComponents { - component.navigationService?(service, didFailToRerouteWith: error) + component.navigationService(service, didFailToRerouteWith: error) } - delegate?.navigationViewController?(self, didFailToRerouteWith: error) + delegate?.navigationViewController(self, didFailToRerouteWith: error) } - @objc public func navigationService(_ service: NavigationService, shouldDiscard location: CLLocation) -> Bool { + public func navigationService(_ service: NavigationService, shouldDiscard location: CLLocation) -> Bool { let defaultBehavior = RouteController.DefaultBehavior.shouldDiscardLocation - let componentsWantToDiscard = navigationComponents.allSatisfy { $0.navigationService?(service, shouldDiscard: location) ?? defaultBehavior } - return componentsWantToDiscard && (delegate?.navigationViewController?(self, shouldDiscard: location) ?? defaultBehavior) + let componentsWantToDiscard = navigationComponents.allSatisfy { $0.navigationService(service, shouldDiscard: location) } + return componentsWantToDiscard && (delegate?.navigationViewController(self, shouldDiscard: location) ?? defaultBehavior) } - @objc public func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { - + public func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { //Check to see if we're in a tunnel. checkTunnelState(at: location, along: progress) - //Pass the message onto our navigation components for component in navigationComponents { - component.navigationService?(service, didUpdate: progress, with: location, rawLocation: rawLocation) + component.navigationService(service, didUpdate: progress, with: location, rawLocation: rawLocation) } // If the user has arrived, don't snap the user puck. @@ -488,7 +474,7 @@ extension NavigationViewController: NavigationServiceDelegate { // we should accurately depict this. let destination = progress.currentLeg.destination - let shouldPrevent = navigationService.delegate?.navigationService?(navigationService, shouldPreventReroutesWhenArrivingAt: destination) ?? RouteController.DefaultBehavior.shouldPreventReroutesWhenArrivingAtWaypoint + let shouldPrevent = navigationService.delegate?.navigationService(navigationService, shouldPreventReroutesWhenArrivingAt: destination) ?? RouteController.DefaultBehavior.shouldPreventReroutesWhenArrivingAtWaypoint let userHasArrivedAndShouldPreventRerouting = shouldPrevent && !progress.currentLegProgress.userHasArrivedAtWaypoint if snapsUserLocationAnnotationToRoute, @@ -504,12 +490,12 @@ extension NavigationViewController: NavigationServiceDelegate { } // Finally, pass the message onto the NVC delegate. - delegate?.navigationViewController?(self, didUpdate: progress, with: location, rawLocation: rawLocation) + delegate?.navigationViewController(self, didUpdate: progress, with: location, rawLocation: rawLocation) } - @objc public func navigationService(_ service: NavigationService, didPassSpokenInstructionPoint instruction: SpokenInstruction, routeProgress: RouteProgress) { + public func navigationService(_ service: NavigationService, didPassSpokenInstructionPoint instruction: SpokenInstruction, routeProgress: RouteProgress) { for component in navigationComponents { - component.navigationService?(service, didPassSpokenInstructionPoint: instruction, routeProgress: routeProgress) + component.navigationService(service, didPassSpokenInstructionPoint: instruction, routeProgress: routeProgress) } // Remove any notification about an already complete maneuver, even if there isn’t another notification to replace it with yet. @@ -523,24 +509,24 @@ extension NavigationViewController: NavigationServiceDelegate { } } - @objc public func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { + public func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { for component in navigationComponents { - component.navigationService?(service, didPassVisualInstructionPoint: instruction, routeProgress: routeProgress) + component.navigationService(service, didPassVisualInstructionPoint: instruction, routeProgress: routeProgress) } } - @objc public func navigationService(_ service: NavigationService, willArriveAt waypoint: Waypoint, after remainingTimeInterval: TimeInterval, distance: CLLocationDistance) { + public func navigationService(_ service: NavigationService, willArriveAt waypoint: Waypoint, after remainingTimeInterval: TimeInterval, distance: CLLocationDistance) { for component in navigationComponents { - component.navigationService?(service, willArriveAt: waypoint, after: remainingTimeInterval, distance: distance) + component.navigationService(service, willArriveAt: waypoint, after: remainingTimeInterval, distance: distance) } - delegate?.navigationViewController?(self, willArriveAt: waypoint, after: remainingTimeInterval, distance: distance) + delegate?.navigationViewController(self, willArriveAt: waypoint, after: remainingTimeInterval, distance: distance) } - @objc public func navigationService(_ service: NavigationService, didArriveAt waypoint: Waypoint) -> Bool { + public func navigationService(_ service: NavigationService, didArriveAt waypoint: Waypoint) -> Bool { let defaultBehavior = RouteController.DefaultBehavior.didArriveAtWaypoint - let componentsWantAdvance = navigationComponents.allSatisfy { $0.navigationService?(service, didArriveAt: waypoint) ?? defaultBehavior } - let advancesToNextLeg = componentsWantAdvance && (delegate?.navigationViewController?(self, didArriveAt: waypoint) ?? defaultBehavior) + let componentsWantAdvance = navigationComponents.allSatisfy { $0.navigationService(service, didArriveAt: waypoint) } + let advancesToNextLeg = componentsWantAdvance && (delegate?.navigationViewController(self, didArriveAt: waypoint) ?? defaultBehavior) if service.routeProgress.isFinalLeg && advancesToNextLeg && showsEndOfRouteFeedback { showEndOfRouteFeedback() @@ -548,32 +534,32 @@ extension NavigationViewController: NavigationServiceDelegate { return advancesToNextLeg } - @objc public func showEndOfRouteFeedback(duration: TimeInterval = 1.0, completionHandler: ((Bool) -> Void)? = nil) { + public func showEndOfRouteFeedback(duration: TimeInterval = 1.0, completionHandler: ((Bool) -> Void)? = nil) { guard let mapController = mapViewController else { return } mapController.showEndOfRoute(duration: duration, completion: completionHandler) } - @objc public func navigationService(_ service: NavigationService, willBeginSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) { + public func navigationService(_ service: NavigationService, willBeginSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) { for component in navigationComponents { - component.navigationService?(service, willBeginSimulating: progress, becauseOf: reason) + component.navigationService(service, willBeginSimulating: progress, becauseOf: reason) } } public func navigationService(_ service: NavigationService, didBeginSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) { for component in navigationComponents { - component.navigationService?(service, didBeginSimulating: progress, becauseOf: reason) + component.navigationService(service, didBeginSimulating: progress, becauseOf: reason) } } - @objc public func navigationService(_ service: NavigationService, willEndSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) { + public func navigationService(_ service: NavigationService, willEndSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) { for component in navigationComponents { - component.navigationService?(service, willEndSimulating: progress, becauseOf: reason) + component.navigationService(service, willEndSimulating: progress, becauseOf: reason) } } public func navigationService(_ service: NavigationService, didEndSimulating progress: RouteProgress, becauseOf reason: SimulationIntent) { for component in navigationComponents { - component.navigationService?(service, didEndSimulating: progress, becauseOf: reason) + component.navigationService(service, didEndSimulating: progress, becauseOf: reason) } } @@ -592,20 +578,17 @@ extension NavigationViewController: NavigationServiceDelegate { } public func navigationService(_ service: NavigationService, shouldPreventReroutesWhenArrivingAt waypoint: Waypoint) -> Bool { - let defaultBehavior = RouteController.DefaultBehavior.shouldPreventReroutesWhenArrivingAtWaypoint - return navigationComponents.allSatisfy { $0.navigationService?(service, shouldPreventReroutesWhenArrivingAt: waypoint) ?? defaultBehavior } + return navigationComponents.allSatisfy { $0.navigationService(service, shouldPreventReroutesWhenArrivingAt: waypoint) } } public func navigationServiceShouldDisableBatteryMonitoring(_ service: NavigationService) -> Bool { - let defaultBehavior = RouteController.DefaultBehavior.shouldDisableBatteryMonitoring - return navigationComponents.allSatisfy { $0.navigationServiceShouldDisableBatteryMonitoring?(service) ?? defaultBehavior } + return navigationComponents.allSatisfy { $0.navigationServiceShouldDisableBatteryMonitoring(service) } } } // MARK: - StyleManagerDelegate extension NavigationViewController: StyleManagerDelegate { - @objc(locationForStyleManager:) public func location(for styleManager: StyleManager) -> CLLocation? { if let location = navigationService.router.location { return location @@ -616,7 +599,6 @@ extension NavigationViewController: StyleManagerDelegate { } } - @objc(styleManager:didApplyStyle:) public func styleManager(_ styleManager: StyleManager, didApply style: Style) { updateMapStyle(style) } @@ -631,7 +613,7 @@ extension NavigationViewController: StyleManagerDelegate { setNeedsStatusBarAppearanceUpdate() } - @objc public func styleManagerDidRefreshAppearance(_ styleManager: StyleManager) { + public func styleManagerDidRefreshAppearance(_ styleManager: StyleManager) { mapView?.reloadStyle(self) } } @@ -653,14 +635,12 @@ extension NavigationViewController: TopBannerViewControllerDelegate { let progress = navigationService.routeProgress let route = progress.route switch direction { - case .up where banner.isDisplayingSteps: banner.dismissStepsTable() case .down where !banner.isDisplayingSteps: banner.displayStepsTable() - if banner.isDisplayingPreviewInstructions { mapViewController?.recenter(self) } @@ -702,9 +682,6 @@ extension NavigationViewController: TopBannerViewControllerDelegate { } mapViewController?.center(on: upcomingStep, route: route, legIndex: legIndex, stepIndex: nextStepIndex, animated: animated, completion: previewBanner) - - - } public func topBanner(_ banner: TopBannerViewController, didSelect legIndex: Int, stepIndex: Int, cell: StepTableViewCell) { @@ -733,7 +710,7 @@ fileprivate extension Route { extension NavigationViewController: BottomBannerViewControllerDelegate { public func didTapCancel(_ sender: Any) { - if delegate?.navigationViewControllerDidDismiss?(self, byCanceling: true) != nil { + if delegate?.navigationViewControllerDidDismiss(self, byCanceling: true) != nil { // The receiver should handle dismissal of the NavigationViewController } else { dismiss(animated: true, completion: nil) diff --git a/MapboxNavigation/NavigationViewControllerDelegate.swift b/MapboxNavigation/NavigationViewControllerDelegate.swift index 92ac97aeba0..1cf23276241 100644 --- a/MapboxNavigation/NavigationViewControllerDelegate.swift +++ b/MapboxNavigation/NavigationViewControllerDelegate.swift @@ -7,15 +7,15 @@ import MapboxCoreNavigation For convenience, several location-related methods in the `NavigationServiceDelegate` protocol have corresponding methods in this protocol. */ -@objc(MBNavigationViewControllerDelegate) -public protocol NavigationViewControllerDelegate: VisualInstructionDelegate { +public protocol NavigationViewControllerDelegate: VisualInstructionDelegate{ /** Called when the navigation view controller is dismissed, such as when the user ends a trip. - parameter navigationViewController: The navigation view controller that was dismissed. - parameter canceled: True if the user dismissed the navigation view controller by tapping the Cancel button; false if the navigation view controller dismissed by some other means. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationViewControllerDidDismiss(_ navigationViewController: NavigationViewController, byCanceling canceled: Bool) + func navigationViewControllerDidDismiss(_ navigationViewController: NavigationViewController, byCanceling canceled: Bool) /** Called when movement of the user updates the route progress model. @@ -24,9 +24,9 @@ public protocol NavigationViewControllerDelegate: VisualInstructionDelegate { - parameter progress: the RouteProgress model that was updated. - parameter location: the guaranteed location, possibly snapped, associated with the progress update. - parameter rawLocation: the raw location, from the location manager, associated with the progress update. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:didUpdateProgress:withLocation:rawLocation:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) + func navigationViewController(_ navigationViewController: NavigationViewController, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) /** Called as the user approaches a waypoint. @@ -37,9 +37,9 @@ public protocol NavigationViewControllerDelegate: VisualInstructionDelegate { - parameter remainingTimeInterval: The estimated number of seconds until arrival. - parameter distance: The current distance from the waypoint, in meters. - note: This method will likely be called several times as you approach a destination. To respond to the user’s arrival only once, your delegate can define a property that keeps track of whether this method has already been called for the given waypoint. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:willArriveAtWaypoint:after:distance:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, willArriveAt waypoint: Waypoint, after remainingTimeInterval: TimeInterval, distance: CLLocationDistance) + func navigationViewController(_ navigationViewController: NavigationViewController, willArriveAt waypoint: Waypoint, after remainingTimeInterval: TimeInterval, distance: CLLocationDistance) /** Called when the user arrives at the destination waypoint for a route leg. @@ -50,9 +50,9 @@ public protocol NavigationViewControllerDelegate: VisualInstructionDelegate { - parameter navigationViewController: The navigation view controller that has arrived at a waypoint. - parameter waypoint: The waypoint that the user has arrived at. - returns: True to automatically advance to the next leg, or false to remain on the now completed leg. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:didArriveAtWaypoint:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, didArriveAt waypoint: Waypoint) -> Bool + func navigationViewController(_ navigationViewController: NavigationViewController, didArriveAt waypoint: Waypoint) -> Bool /** Returns whether the navigation view controller should be allowed to calculate a new route. @@ -62,9 +62,9 @@ public protocol NavigationViewControllerDelegate: VisualInstructionDelegate { - parameter navigationViewController: The navigation view controller that has detected the need to calculate a new route. - parameter location: The user’s current location. - returns: True to allow the navigation view controller to calculate a new route; false to keep tracking the current route. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:shouldRerouteFromLocation:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, shouldRerouteFrom location: CLLocation) -> Bool + func navigationViewController(_ navigationViewController: NavigationViewController, shouldRerouteFrom location: CLLocation) -> Bool /** Called immediately before the navigation view controller calculates a new route. @@ -73,9 +73,9 @@ public protocol NavigationViewControllerDelegate: VisualInstructionDelegate { - parameter navigationViewController: The navigation view controller that will calculate a new route. - parameter location: The user’s current location. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:willRerouteFromLocation:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, willRerouteFrom location: CLLocation?) + func navigationViewController(_ navigationViewController: NavigationViewController, willRerouteFrom location: CLLocation?) /** Called immediately after the navigation view controller receives a new route. @@ -84,9 +84,9 @@ public protocol NavigationViewControllerDelegate: VisualInstructionDelegate { - parameter navigationViewController: The navigation view controller that has calculated a new route. - parameter route: The new route. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:didRerouteAlongRoute:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, didRerouteAlong route: Route) + func navigationViewController(_ navigationViewController: NavigationViewController, didRerouteAlong route: Route) /** Called when the navigation view controller fails to receive a new route. @@ -95,74 +95,78 @@ public protocol NavigationViewControllerDelegate: VisualInstructionDelegate { - parameter navigationViewController: The navigation view controller that has calculated a new route. - parameter error: An error raised during the process of obtaining a new route. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:didFailToRerouteWithError:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, didFailToRerouteWith error: Error) + func navigationViewController(_ navigationViewController: NavigationViewController, didFailToRerouteWith error: Error) /** Returns an `MGLStyleLayer` that determines the appearance of the route line. If this method is unimplemented, the navigation view controller’s map view draws the route line using an `MGLLineStyleLayer`. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationViewController(_ navigationViewController: NavigationViewController, routeStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? + func navigationViewController(_ navigationViewController: NavigationViewController, routeStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? /** Returns an `MGLStyleLayer` that determines the appearance of the route line’s casing. If this method is unimplemented, the navigation view controller’s map view draws the route line’s casing using an `MGLLineStyleLayer` whose width is greater than that of the style layer returned by `navigationViewController(_:routeStyleLayerWithIdentifier:source:)`. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationViewController(_ navigationViewController: NavigationViewController, routeCasingStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? + func navigationViewController(_ navigationViewController: NavigationViewController, routeCasingStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? /** Returns an `MGLShape` that represents the path of the route line. If this method is unimplemented, the navigation view controller’s map view represents the route line using an `MGLPolylineFeature` based on `route`’s `coordinates` property. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:shapeForRoutes:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, shapeFor routes: [Route]) -> MGLShape? + func navigationViewController(_ navigationViewController: NavigationViewController, shapeFor routes: [Route]) -> MGLShape? /** Returns an `MGLShape` that represents the path of the route line’s casing. If this method is unimplemented, the navigation view controller’s map view represents the route line’s casing using an `MGLPolylineFeature` identical to the one returned by `navigationViewController(_:shapeFor:)`. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:simplifiedShapeForRoute:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, simplifiedShapeFor route: Route) -> MGLShape? + func navigationViewController(_ navigationViewController: NavigationViewController, simplifiedShapeFor route: Route) -> MGLShape? - /* + /** Returns an `MGLStyleLayer` that marks the location of each destination along the route when there are multiple destinations. The returned layer is added to the map below the layer returned by `navigationViewController(_:waypointSymbolStyleLayerWithIdentifier:source:)`. If this method is unimplemented, the navigation view controller’s map view marks each destination waypoint with a circle. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationViewController(_ navigationViewController: NavigationViewController, waypointStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? + func navigationViewController(_ navigationViewController: NavigationViewController, waypointStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? - /* + /** Returns an `MGLStyleLayer` that places an identifying symbol on each destination along the route when there are multiple destinations. The returned layer is added to the map above the layer returned by `navigationViewController(_:waypointStyleLayerWithIdentifier:source:)`. If this method is unimplemented, the navigation view controller’s map view labels each destination waypoint with a number, starting with 1 at the first destination, 2 at the second destination, and so on. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func navigationViewController(_ navigationViewController: NavigationViewController, waypointSymbolStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? + func navigationViewController(_ navigationViewController: NavigationViewController, waypointSymbolStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? /** Returns an `MGLShape` that represents the destination waypoints along the route (that is, excluding the origin). If this method is unimplemented, the navigation map view represents the route waypoints using `navigationViewController(_:shapeFor:legIndex:)`. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:shapeForWaypoints:legIndex:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, shapeFor waypoints: [Waypoint], legIndex: Int) -> MGLShape? + func navigationViewController(_ navigationViewController: NavigationViewController, shapeFor waypoints: [Waypoint], legIndex: Int) -> MGLShape? /** Called when the user taps to select a route on the navigation view controller’s map view. - parameter navigationViewController: The navigation view controller presenting the route that the user selected. - parameter route: The route on the map that the user selected. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:didSelectRoute:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, didSelect route: Route) + func navigationViewController(_ navigationViewController: NavigationViewController, didSelect route: Route) /** Returns the center point of the user course view in screen coordinates relative to the map view. */ - @objc optional func navigationViewController(_ navigationViewController: NavigationViewController, mapViewUserAnchorPoint mapView: NavigationMapView) -> CGPoint + func navigationViewController(_ navigationViewController: NavigationViewController, mapViewUserAnchorPoint mapView: NavigationMapView) -> CGPoint /** Allows the delegate to decide whether to ignore a location update. @@ -172,9 +176,9 @@ public protocol NavigationViewControllerDelegate: VisualInstructionDelegate { - parameter navigationViewController: The navigation view controller that discarded the location. - parameter location: The location that will be discarded. - returns: If `true`, the location is discarded and the `NavigationViewController` will not consider it. If `false`, the location will not be thrown out. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:shouldDiscardLocation:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, shouldDiscard location: CLLocation) -> Bool + func navigationViewController(_ navigationViewController: NavigationViewController, shouldDiscard location: CLLocation) -> Bool /** Called to allow the delegate to customize the contents of the road name label that is displayed towards the bottom of the map view. @@ -184,19 +188,117 @@ public protocol NavigationViewControllerDelegate: VisualInstructionDelegate { - parameter navigationViewController: The navigation view controller that will display the road name. - parameter location: The user’s current location. - returns: The road name to display in the label, or nil to hide the label. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(navigationViewController:roadNameAtLocation:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, roadNameAt location: CLLocation) -> String? - + func navigationViewController(_ navigationViewController: NavigationViewController, roadNameAt location: CLLocation) -> String? //MARK: Obsolete @available(*, deprecated, message: "Use MGLMapViewDelegate.mapView(_:imageFor:) instead.") - @objc(navigationViewController:imageForAnnotation:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage? + + func navigationViewController(_ navigationViewController: NavigationViewController, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage? @available(*, deprecated, message: "Use MGLMapViewDelegate.mapView(_:viewFor:) instead.") - @objc(navigationViewController:viewForAnnotation:) - optional func navigationViewController(_ navigationViewController: NavigationViewController, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? + + func navigationViewController(_ navigationViewController: NavigationViewController, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? } +public extension NavigationViewControllerDelegate { + func navigationViewControllerDidDismiss(_ navigationViewController: NavigationViewController, byCanceling canceled: Bool) { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + } + + func navigationViewController(_ navigationViewController: NavigationViewController, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .info) + } + + func navigationViewController(_ navigationViewController: NavigationViewController, willArriveAt waypoint: Waypoint, after remainingTimeInterval: TimeInterval, distance: CLLocationDistance) { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + } + + func navigationViewController(_ navigationViewController: NavigationViewController, didArriveAt waypoint: Waypoint) -> Bool { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return RouteController.DefaultBehavior.didArriveAtWaypoint + } + + func navigationViewController(_ navigationViewController: NavigationViewController, shouldRerouteFrom location: CLLocation) -> Bool { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return RouteController.DefaultBehavior.shouldRerouteFromLocation + } + + func navigationViewController(_ navigationViewController: NavigationViewController, willRerouteFrom location: CLLocation?) { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + } + + func navigationViewController(_ navigationViewController: NavigationViewController, didRerouteAlong route: Route) { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + } + + func navigationViewController(_ navigationViewController: NavigationViewController, didFailToRerouteWith error: Error) { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + } + + func navigationViewController(_ navigationViewController: NavigationViewController, routeStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return nil + } + + func navigationViewController(_ navigationViewController: NavigationViewController, routeCasingStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return nil + } + + func navigationViewController(_ navigationViewController: NavigationViewController, shapeFor routes: [Route]) -> MGLShape? { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return nil + } + + func navigationViewController(_ navigationViewController: NavigationViewController, simplifiedShapeFor route: Route) -> MGLShape? { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return nil + } + + func navigationViewController(_ navigationViewController: NavigationViewController, waypointStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return nil + } + + func navigationViewController(_ navigationViewController: NavigationViewController, waypointSymbolStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return nil + } + + func navigationViewController(_ navigationViewController: NavigationViewController, shapeFor waypoints: [Waypoint], legIndex: Int) -> MGLShape? { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return nil + } + + func navigationViewController(_ navigationViewController: NavigationViewController, didSelect route: Route) { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + } + + func navigationViewController(_ navigationViewController: NavigationViewController, mapViewUserAnchorPoint mapView: NavigationMapView) -> CGPoint { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .info) + return .zero + } + + func navigationViewController(_ navigationViewController: NavigationViewController, shouldDiscard location: CLLocation) -> Bool { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return RouteController.DefaultBehavior.shouldDiscardLocation + } + + func navigationViewController(_ navigationViewController: NavigationViewController, roadNameAt location: CLLocation) -> String? { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return nil + } + + func navigationViewController(_ navigationViewController: NavigationViewController, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage? { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return nil + } + + func navigationViewController(_ navigationViewController: NavigationViewController, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? { + logUnimplemented(protocolType: NavigationViewControllerDelegate.self, level: .debug) + return nil + } +} diff --git a/MapboxNavigation/NavigationViewLayout.swift b/MapboxNavigation/NavigationViewLayout.swift index 8fc29b16423..d5bb2dbfd56 100644 --- a/MapboxNavigation/NavigationViewLayout.swift +++ b/MapboxNavigation/NavigationViewLayout.swift @@ -21,7 +21,6 @@ extension NavigationView { bottomBannerContainerView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true bottomBannerContainerView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - wayNameView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true wayNameView.bottomAnchor.constraint(equalTo: bottomBannerContainerView.topAnchor, constant: -10).isActive = true } @@ -33,6 +32,5 @@ extension NavigationView { endOfRouteView?.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true self.endOfRouteHeightConstraint?.isActive = true - } } diff --git a/MapboxNavigation/NextBannerView.swift b/MapboxNavigation/NextBannerView.swift index c975f8e24e6..b196e88e6b5 100644 --- a/MapboxNavigation/NextBannerView.swift +++ b/MapboxNavigation/NextBannerView.swift @@ -3,14 +3,11 @@ import MapboxDirections import MapboxCoreNavigation /// :nodoc: -@objc(MBNextInstructionLabel) -open class NextInstructionLabel: InstructionLabel { } +open class NextInstructionLabel: InstructionLabel {} /// :nodoc: @IBDesignable -@objc(MBNextBannerView) open class NextBannerView: UIView, NavigationComponent { - weak var maneuverView: ManeuverView! weak var instructionLabel: NextInstructionLabel! weak var bottomSeparatorView: SeparatorView! @@ -92,14 +89,13 @@ open class NextBannerView: UIView, NavigationComponent { bottomSeparatorView.heightAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale).isActive = true } - @objc public func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { + public func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { update(for: instruction) } /** Updates the instructions banner info with a given `VisualInstructionBanner`. */ - @objc(updateForVisualInstructionBanner:) public func update(for visualInstruction: VisualInstructionBanner?) { guard let tertiaryInstruction = visualInstruction?.tertiaryInstruction, !tertiaryInstruction.containsLaneIndications else { hide() @@ -127,5 +123,4 @@ open class NextBannerView: UIView, NavigationComponent { self.isHidden = true }, completion: nil) } - } diff --git a/MapboxNavigation/RatingControl.swift b/MapboxNavigation/RatingControl.swift index fd9f2c127f5..92dcb026b01 100644 --- a/MapboxNavigation/RatingControl.swift +++ b/MapboxNavigation/RatingControl.swift @@ -3,9 +3,7 @@ import CoreGraphics typealias RatingClosure = (Int) -> Void //rating -/*@IBDesignable*/ class RatingControl: UIStackView { - // MARK: Constants static let defaultSize = CGSize(width: 32.0, height: 32.0) private let starTemplate = UIImage(named: "star", in: .mapboxNavigation, compatibleWith: nil) @@ -92,7 +90,6 @@ class RatingControl: UIStackView { private func updateSelectionStates() { for (index, button) in stars.enumerated() { - let selected = index < rating button.tintColor = selected ? selectedColor : normalColor button.isSelected = selected diff --git a/MapboxNavigation/RecentItem.swift b/MapboxNavigation/RecentItem.swift index a4486a14695..a1ee6e550d4 100644 --- a/MapboxNavigation/RecentItem.swift +++ b/MapboxNavigation/RecentItem.swift @@ -4,7 +4,6 @@ import MapboxGeocoder import CarPlay struct RecentItem: Codable, Equatable { - static func ==(lhs: RecentItem, rhs: RecentItem) -> Bool { return lhs.timestamp == rhs.timestamp && lhs.geocodedPlacemark == rhs.geocodedPlacemark } @@ -44,7 +43,6 @@ struct RecentItem: Codable, Equatable { } extension Array where Element == RecentItem { - func save() { let encoder = JSONEncoder() let data = try? encoder.encode(self) diff --git a/MapboxNavigation/Resources/Base.lproj/Navigation.storyboard b/MapboxNavigation/Resources/Base.lproj/Navigation.storyboard index d091483a808..bef5d8aaf99 100644 --- a/MapboxNavigation/Resources/Base.lproj/Navigation.storyboard +++ b/MapboxNavigation/Resources/Base.lproj/Navigation.storyboard @@ -1,12 +1,9 @@ - - - - + + - - + @@ -14,7 +11,7 @@ - + @@ -29,14 +26,14 @@ - + - + @@ -103,7 +100,7 @@ - + @@ -134,7 +131,7 @@ - + diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 290a3fdcb85..d11f55f8d1e 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -8,75 +8,12 @@ import Turf class ArrowFillPolyline: MGLPolylineFeature {} class ArrowStrokePolyline: ArrowFillPolyline {} -extension RouteMapViewController: NavigationComponent { - - func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { - - let route = progress.route - let legIndex = progress.legIndex - let stepIndex = progress.currentLegProgress.stepIndex - - mapView.updatePreferredFrameRate(for: progress) - if currentLegIndexMapped != legIndex { - mapView.showWaypoints(route, legIndex: legIndex) - mapView.showRoutes([route], legIndex: legIndex) - - currentLegIndexMapped = legIndex - } - - if currentStepIndexMapped != stepIndex { - updateMapOverlays(for: progress) - currentStepIndexMapped = stepIndex - } - - if annotatesSpokenInstructions { - mapView.showVoiceInstructionsOnMap(route: route) - } - } - - @objc public func navigationService(_ service: NavigationService, didPassSpokenInstructionPoint instruction: SpokenInstruction, routeProgress: RouteProgress) { - updateCameraAltitude(for: routeProgress) - } - - - func navigationService(_ service: NavigationService, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) { - currentStepIndexMapped = 0 - let route = router.route - let stepIndex = router.routeProgress.currentLegProgress.stepIndex - let legIndex = router.routeProgress.legIndex - - - mapView.addArrow(route: route, legIndex: legIndex, stepIndex: stepIndex + 1) - mapView.showRoutes([route], legIndex: legIndex) - mapView.showWaypoints(route) - - if annotatesSpokenInstructions { - mapView.showVoiceInstructionsOnMap(route: route) - } - - if isInOverviewMode { - if let coordinates = route.coordinates, let userLocation = router.location?.coordinate { - mapView.contentInset = contentInset(forOverviewing: true) - mapView.setOverheadCameraView(from: userLocation, along: coordinates, for: contentInset(forOverviewing: true)) - } - } else { - mapView.tracksUserCourse = true - navigationView.wayNameView.isHidden = true - } - - } - -} - - class RouteMapViewController: UIViewController { - var navigationView: NavigationView { return view as! NavigationView } var mapView: NavigationMapView { return navigationView.mapView } var reportButton: FloatingButton { return navigationView.reportButton } var topBannerContainerView: BannerContainerView { return navigationView.topBannerContainerView } var bottomBannerContainerView: BannerContainerView { return navigationView.bottomBannerContainerView } - lazy var endOfRouteViewController: EndOfRouteViewController = { let storyboard = UIStoryboard(name: "Navigation", bundle: .mapboxNavigation) @@ -158,7 +95,6 @@ class RouteMapViewController: UIViewController { var labelRoadNameCompletionHandler: (LabelRoadNameCompletionHandler)? convenience init(navigationService: NavigationService, delegate: RouteMapViewControllerDelegate? = nil, topBanner: ContainerViewController, bottomBanner: ContainerViewController) { - self.init() self.navService = navigationService self.delegate = delegate @@ -172,7 +108,6 @@ class RouteMapViewController: UIViewController { topContainer.backgroundColor = .clear - let bottomContainer = navigationView.bottomBannerContainerView embed(bottomBanner, in: bottomContainer) { (parent, banner) -> [NSLayoutConstraint] in banner.view.translatesAutoresizingMaskIntoConstraints = false @@ -184,7 +119,6 @@ class RouteMapViewController: UIViewController { view.bringSubviewToFront(topBannerContainerView) } - override func loadView() { view = NavigationView(delegate: self) view.frame = parent?.view.bounds ?? UIScreen.main.bounds @@ -374,14 +308,10 @@ class RouteMapViewController: UIViewController { } } - - private func setCamera(altitude: Double) { guard mapView.altitude != altitude else { return } mapView.altitude = altitude } - - /** Modifies the gesture recognizers to also update the map’s frame rate. */ func makeGestureRecognizersResetFrameRate() { @@ -413,7 +343,7 @@ class RouteMapViewController: UIViewController { var contentFrame = mapView.bounds.inset(by: insets) // Avoid letting the puck go partially off-screen, and add a comfortable padding beyond that. - let courseViewBounds = mapView.userCourseView?.bounds ?? .zero + let courseViewBounds = mapView.userCourseView.bounds // If it is not possible to position it right above the content area, center it at the remaining space. contentFrame = contentFrame.insetBy(dx: min(NavigationMapView.courseViewMinimumInsets.left + courseViewBounds.width / 2.0, contentFrame.width / 2.0), dy: min(NavigationMapView.courseViewMinimumInsets.top + courseViewBounds.height / 2.0, contentFrame.height / 2.0)) @@ -509,6 +439,61 @@ class RouteMapViewController: UIViewController { } } +// MARK: - NavigationComponent +extension RouteMapViewController: NavigationComponent { + func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { + let route = progress.route + let legIndex = progress.legIndex + let stepIndex = progress.currentLegProgress.stepIndex + + mapView.updatePreferredFrameRate(for: progress) + if currentLegIndexMapped != legIndex { + mapView.showWaypoints(on: route, legIndex: legIndex) + mapView.show([route], legIndex: legIndex) + + currentLegIndexMapped = legIndex + } + + if currentStepIndexMapped != stepIndex { + updateMapOverlays(for: progress) + currentStepIndexMapped = stepIndex + } + + if annotatesSpokenInstructions { + mapView.showVoiceInstructionsOnMap(route: route) + } + } + + public func navigationService(_ service: NavigationService, didPassSpokenInstructionPoint instruction: SpokenInstruction, routeProgress: RouteProgress) { + updateCameraAltitude(for: routeProgress) + } + + func navigationService(_ service: NavigationService, didRerouteAlong route: Route, at location: CLLocation?, proactive: Bool) { + currentStepIndexMapped = 0 + let route = router.route + let stepIndex = router.routeProgress.currentLegProgress.stepIndex + let legIndex = router.routeProgress.legIndex + + mapView.addArrow(route: route, legIndex: legIndex, stepIndex: stepIndex + 1) + mapView.show([route], legIndex: legIndex) + mapView.showWaypoints(on: route) + + if annotatesSpokenInstructions { + mapView.showVoiceInstructionsOnMap(route: route) + } + + if isInOverviewMode { + if let coordinates = route.coordinates, let userLocation = router.location?.coordinate { + mapView.contentInset = contentInset(forOverviewing: true) + mapView.setOverheadCameraView(from: userLocation, along: coordinates, for: contentInset(forOverviewing: true)) + } + } else { + mapView.tracksUserCourse = true + navigationView.wayNameView.isHidden = true + } + } +} + // MARK: - UIContentContainer extension RouteMapViewController { @@ -530,7 +515,7 @@ extension RouteMapViewController: NavigationViewDelegate { // MARK: VisualInstructionDelegate func label(_ label: InstructionLabel, willPresent instruction: VisualInstruction, as presented: NSAttributedString) -> NSAttributedString? { - return delegate?.label?(label, willPresent: instruction, as: presented) + return delegate?.label(label, willPresent: instruction, as: presented) } // MARK: NavigationMapViewCourseTrackingDelegate @@ -545,38 +530,37 @@ extension RouteMapViewController: NavigationViewDelegate { mapView.logoView.isHidden = true } - //MARK: NavigationMapViewDelegate func navigationMapView(_ mapView: NavigationMapView, routeStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { - return delegate?.navigationMapView?(mapView, routeStyleLayerWithIdentifier: identifier, source: source) + return delegate?.navigationMapView(mapView, routeStyleLayerWithIdentifier: identifier, source: source) } func navigationMapView(_ mapView: NavigationMapView, routeCasingStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { - return delegate?.navigationMapView?(mapView, routeCasingStyleLayerWithIdentifier: identifier, source: source) + return delegate?.navigationMapView(mapView, routeCasingStyleLayerWithIdentifier: identifier, source: source) } func navigationMapView(_ mapView: NavigationMapView, waypointStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { - return delegate?.navigationMapView?(mapView, waypointStyleLayerWithIdentifier: identifier, source: source) + return delegate?.navigationMapView(mapView, waypointStyleLayerWithIdentifier: identifier, source: source) } func navigationMapView(_ mapView: NavigationMapView, waypointSymbolStyleLayerWithIdentifier identifier: String, source: MGLSource) -> MGLStyleLayer? { - return delegate?.navigationMapView?(mapView, waypointSymbolStyleLayerWithIdentifier: identifier, source: source) + return delegate?.navigationMapView(mapView, waypointSymbolStyleLayerWithIdentifier: identifier, source: source) } func navigationMapView(_ mapView: NavigationMapView, shapeFor waypoints: [Waypoint], legIndex: Int) -> MGLShape? { - return delegate?.navigationMapView?(mapView, shapeFor: waypoints, legIndex: legIndex) + return delegate?.navigationMapView(mapView, shapeFor: waypoints, legIndex: legIndex) } func navigationMapView(_ mapView: NavigationMapView, shapeFor routes: [Route]) -> MGLShape? { - return delegate?.navigationMapView?(mapView, shapeFor: routes) + return delegate?.navigationMapView(mapView, shapeFor: routes) } func navigationMapView(_ mapView: NavigationMapView, didSelect route: Route) { - delegate?.navigationMapView?(mapView, didSelect: route) + delegate?.navigationMapView(mapView, didSelect: route) } func navigationMapView(_ mapView: NavigationMapView, simplifiedShapeFor route: Route) -> MGLShape? { - return delegate?.navigationMapView?(mapView, simplifiedShapeFor: route) + return delegate?.navigationMapView(mapView, simplifiedShapeFor: route) } func navigationMapViewUserAnchorPoint(_ mapView: NavigationMapView) -> CGPoint { @@ -586,7 +570,7 @@ extension RouteMapViewController: NavigationViewDelegate { } //otherwise, ask the delegate or return .zero - return delegate?.navigationMapViewUserAnchorPoint?(mapView) ?? .zero + return delegate?.navigationMapViewUserAnchorPoint(mapView) ?? .zero } /** @@ -595,9 +579,8 @@ extension RouteMapViewController: NavigationViewDelegate { - parameter location: The user’s current location. */ func labelCurrentRoad(at rawLocation: CLLocation, for snappedLocation: CLLocation? = nil) { - guard navigationView.resumeButton.isHidden else { - return + return } let roadName = delegate?.mapViewController(self, roadNameAt: rawLocation) @@ -714,18 +697,18 @@ extension RouteMapViewController: NavigationViewDelegate { private func roadFeature(for line: MGLPolylineFeature) -> (roadName: String?, shieldName: NSAttributedString?) { let roadNameRecord = roadFeatureHelper(ref: line.attribute(forKey: "ref"), - shield: line.attribute(forKey: "shield"), - reflen: line.attribute(forKey: "reflen"), - name: line.attribute(forKey: "name")) + shield: line.attribute(forKey: "shield"), + reflen: line.attribute(forKey: "reflen"), + name: line.attribute(forKey: "name")) return (roadName: roadNameRecord.roadName, shieldName: roadNameRecord.shieldName) } private func roadFeature(for line: MGLMultiPolylineFeature) -> (roadName: String?, shieldName: NSAttributedString?) { let roadNameRecord = roadFeatureHelper(ref: line.attribute(forKey: "ref"), - shield: line.attribute(forKey: "shield"), - reflen: line.attribute(forKey: "reflen"), - name: line.attribute(forKey: "name")) + shield: line.attribute(forKey: "shield"), + reflen: line.attribute(forKey: "reflen"), + name: line.attribute(forKey: "name")) return (roadName: roadNameRecord.roadName, shieldName: roadNameRecord.shieldName) } @@ -768,8 +751,8 @@ extension RouteMapViewController: NavigationViewDelegate { func showRouteIfNeeded() { guard isViewLoaded && view.window != nil else { return } guard !mapView.showsRoute else { return } - mapView.showRoutes([router.route], legIndex: router.routeProgress.legIndex) - mapView.showWaypoints(router.route, legIndex: router.routeProgress.legIndex) + mapView.show([router.route], legIndex: router.routeProgress.legIndex) + mapView.showWaypoints(on: router.route, legIndex: router.routeProgress.legIndex) let currentLegProgress = router.routeProgress.currentLegProgress let nextStepIndex = currentLegProgress.stepIndex + 1 @@ -782,9 +765,6 @@ extension RouteMapViewController: NavigationViewDelegate { mapView.showVoiceInstructionsOnMap(route: router.route) } } - - - } // MARK: - Keyboard Handling @@ -793,7 +773,6 @@ extension RouteMapViewController { fileprivate func subscribeToKeyboardNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(RouteMapViewController.keyboardWillShow(notification:)), name:UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(RouteMapViewController.keyboardWillHide(notification:)), name:UIResponder.keyboardWillHideNotification, object: nil) - } fileprivate func unsubscribeFromKeyboardNotifications() { NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil) @@ -850,7 +829,7 @@ internal extension UIView.AnimationOptions { } } } -@objc protocol RouteMapViewControllerDelegate: NavigationMapViewDelegate, VisualInstructionDelegate { +protocol RouteMapViewControllerDelegate: NavigationMapViewDelegate, VisualInstructionDelegate { func mapViewControllerDidDismiss(_ mapViewController: RouteMapViewController, byCanceling canceled: Bool) func mapViewControllerShouldAnnotateSpokenInstructions(_ routeMapViewController: RouteMapViewController) -> Bool @@ -863,8 +842,7 @@ internal extension UIView.AnimationOptions { - parameter location: The user’s current location. - return: The road name to display in the label, or the empty string to hide the label, or nil to query the map’s vector tiles for the road name. */ - @objc func mapViewController(_ mapViewController: RouteMapViewController, roadNameAt location: CLLocation) -> String? - + func mapViewController(_ mapViewController: RouteMapViewController, roadNameAt location: CLLocation) -> String? - @objc func mapViewController(_ mapViewController: RouteMapViewController, didCenterOn location: CLLocation) + func mapViewController(_ mapViewController: RouteMapViewController, didCenterOn location: CLLocation) } diff --git a/MapboxNavigation/RouteVoiceController.swift b/MapboxNavigation/RouteVoiceController.swift index 1613d099426..1e88758569d 100644 --- a/MapboxNavigation/RouteVoiceController.swift +++ b/MapboxNavigation/RouteVoiceController.swift @@ -3,7 +3,7 @@ import Foundation import AVFoundation import MapboxDirections import MapboxCoreNavigation - +import MapboxSpeech extension NSAttributedString { public func pronounced(_ pronunciation: String) -> NSAttributedString { @@ -55,9 +55,7 @@ extension SpokenInstruction { If you need to supply a third-party speech synthesizer, define a subclass of `RouteVoiceController` that overrides the `speak(_:)` method. If the third-party speech synthesizer requires a network connection, you can instead subclass `MapboxVoiceController` to take advantage of its prefetching functionality. */ -@objc(MBRouteVoiceController) open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate { - lazy var speechSynth = AVSpeechSynthesizer() let audioQueue = DispatchQueue(label: Bundle.mapboxNavigation.bundleIdentifier! + ".audio") @@ -65,17 +63,17 @@ open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate { /** If true, a noise indicating the user is going to be rerouted will play prior to rerouting. */ - @objc public var playRerouteSound = true + public var playRerouteSound = true /** Sound to play prior to reroute. Inherits volume level from `volume`. */ - @objc public var rerouteSoundPlayer: AVAudioPlayer = try! AVAudioPlayer(data: NSDataAsset(name: "reroute-sound", bundle: .mapboxNavigation)!.data, fileTypeHint: AVFileType.mp3.rawValue) + public var rerouteSoundPlayer: AVAudioPlayer = try! AVAudioPlayer(data: NSDataAsset(name: "reroute-sound", bundle: .mapboxNavigation)!.data, fileTypeHint: AVFileType.mp3.rawValue) /** Delegate used for getting metadata information about a particular spoken instruction. */ - @objc public weak var voiceControllerDelegate: VoiceControllerDelegate? + public weak var voiceControllerDelegate: VoiceControllerDelegate? var lastSpokenInstruction: SpokenInstruction? var routeProgress: RouteProgress? @@ -86,8 +84,7 @@ open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate { /** Default initializer for `RouteVoiceController`. */ - @objc - public init(navigationService: NavigationService) { + public init(navigationService: NavigationService) { super.init() verifyBackgroundAudio() @@ -149,23 +146,51 @@ open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate { speechSynth.stopSpeaking(at: .word) + safeMixAudio(instruction: nil, engine: speechSynth) { + voiceControllerDelegate?.voiceController(self, spokenInstructionsDidFailWith: $0) + } + + rerouteSoundPlayer.play() + } + + public func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) { + safeUnduckAudio(instruction: nil, engine: synthesizer) { + voiceControllerDelegate?.voiceController(self, spokenInstructionsDidFailWith: $0) + } + } + + typealias AudioControlFailureHandler = (SpeechError) -> Void + func safeDuckAudio(instruction: SpokenInstruction?, engine: Any?, failure: AudioControlFailureHandler) { do { - try mixAudio() + try tryDuckAudio() } catch { - voiceControllerDelegate?.voiceController?(self, spokenInstructionsDidFailWith: error) + let wrapped = SpeechError.unableToControlAudio(instruction: instruction, action: .duck, synthesizer: engine, underlying: error) + failure(wrapped) + return } - rerouteSoundPlayer.play() } - @objc public func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) { + func safeUnduckAudio(instruction: SpokenInstruction?, engine: Any?, failure: AudioControlFailureHandler) { do { - try unDuckAudio() + try tryUnduckAudio() } catch { - voiceControllerDelegate?.voiceController?(self, spokenInstructionsDidFailWith: error) + let wrapped = SpeechError.unableToControlAudio(instruction: instruction, action: .duck, synthesizer: engine, underlying: error) + failure(wrapped) + return + } + } + + func safeMixAudio(instruction: SpokenInstruction?, engine: Any?, failure: AudioControlFailureHandler) { + do { + try tryMixAudio() + } catch { + let wrapped = SpeechError.unableToControlAudio(instruction: instruction, action: .mix, synthesizer: engine, underlying: error) + failure(wrapped) + return } } - func duckAudio() throws { + func tryDuckAudio() throws { let audioSession = AVAudioSession.sharedInstance() if #available(iOS 12.0, *) { try audioSession.setCategory(.playback, mode: .voicePrompt, options: [.duckOthers, .mixWithOthers]) @@ -175,13 +200,13 @@ open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate { try audioSession.setActive(true) } - func mixAudio() throws { + func tryMixAudio() throws { let audioSession = AVAudioSession.sharedInstance() try audioSession.setCategory(.ambient, mode: audioSession.mode) try audioSession.setActive(true) } - func unDuckAudio() throws { + func tryUnduckAudio() throws { try AVAudioSession.sharedInstance().setActive(false, options: [.notifyOthersOnDeactivation]) } @@ -205,13 +230,11 @@ open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate { assert(routeProgress != nil, "routeProgress should not be nil.") if speechSynth.isSpeaking, let lastSpokenInstruction = lastSpokenInstruction { - voiceControllerDelegate?.voiceController?(self, didInterrupt: lastSpokenInstruction, with: instruction) + voiceControllerDelegate?.voiceController(self, didInterrupt: lastSpokenInstruction, with: instruction) } - do { - try duckAudio() - } catch { - voiceControllerDelegate?.voiceController?(self, spokenInstructionsDidFailWith: error) + safeDuckAudio(instruction: instruction, engine: speechSynth) { + voiceControllerDelegate?.voiceController(self, spokenInstructionsDidFailWith: $0) } var utterance: AVSpeechUtterance? @@ -221,7 +244,7 @@ open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate { utterance!.voice = AVSpeechSynthesisVoice(identifier: AVSpeechSynthesisVoiceIdentifierAlex) } - let modifiedInstruction = voiceControllerDelegate?.voiceController?(self, willSpeak: instruction, routeProgress: routeProgress!) ?? instruction + let modifiedInstruction = voiceControllerDelegate?.voiceController(self, willSpeak: instruction, routeProgress: routeProgress!) ?? instruction if utterance?.voice == nil { utterance = AVSpeechUtterance(attributedString: modifiedInstruction.attributedText(for: routeProgress!.currentLegProgress)) @@ -241,17 +264,26 @@ open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate { /** The `VoiceControllerDelegate` protocol defines methods that allow an object to respond to significant events related to spoken instructions. */ -@objc(MBVoiceControllerDelegate) -public protocol VoiceControllerDelegate { +public protocol VoiceControllerDelegate: class, UnimplementedLogging { + /** + Called when the voice controller falls back to a backup speech syntehsizer, but is still able to speak the instruction. + + - parameter voiceController: The voice controller that experienced the failure. + - parameter synthesizer: the Speech engine that was used as the fallback. + - parameter error: An error explaining the failure and its cause. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. + */ + func voiceController(_ voiceController: RouteVoiceController, didFallBackTo synthesizer: AVSpeechSynthesizer, error: SpeechError) + /** Called when the voice controller failed to speak an instruction. - parameter voiceController: The voice controller that experienced the failure. - - parameter error: An error explaining the failure and its cause. The `MBSpokenInstructionErrorCodeKey` key of the error’s user info dictionary is a `SpokenInstructionErrorCode` indicating the cause of the failure. + - parameter error: An error explaining the failure and its cause. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(voiceController:spokenInstrucionsDidFailWithError:) - optional func voiceController(_ voiceController: RouteVoiceController, spokenInstructionsDidFailWith error: Error) + func voiceController(_ voiceController: RouteVoiceController, spokenInstructionsDidFailWith error: SpeechError) /** Called when one spoken instruction interrupts another instruction currently being spoken. @@ -259,16 +291,38 @@ public protocol VoiceControllerDelegate { - parameter voiceController: The voice controller that experienced the interruption. - parameter interruptedInstruction: The spoken instruction currently in progress that has been interrupted. - parameter interruptingInstruction: The spoken instruction that is interrupting the current instruction. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc(voiceController:didInterruptSpokenInstruction:withInstruction:) - optional func voiceController(_ voiceController: RouteVoiceController, didInterrupt interruptedInstruction: SpokenInstruction, with interruptingInstruction: SpokenInstruction) + func voiceController(_ voiceController: RouteVoiceController, didInterrupt interruptedInstruction: SpokenInstruction, with interruptingInstruction: SpokenInstruction) - /** Called when a spoken is about to speak. Useful if it is necessary to give a custom instruction instead. Noting, changing the `distanceAlongStep` property on `SpokenInstruction` will have no impact on when the instruction will be said. + /** + Called when a spoken is about to speak. Useful if it is necessary to give a custom instruction instead. Noting, changing the `distanceAlongStep` property on `SpokenInstruction` will have no impact on when the instruction will be said. - parameter voiceController: The voice controller that will speak an instruction. - parameter instruction: The spoken instruction that will be said. - parameter routeProgress: The `RouteProgress` just before when the instruction is scheduled to be spoken. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. **/ - @objc(voiceController:willSpeakSpokenInstruction:routeProgress:) - optional func voiceController(_ voiceController: RouteVoiceController, willSpeak instruction: SpokenInstruction, routeProgress: RouteProgress) -> SpokenInstruction? + + func voiceController(_ voiceController: RouteVoiceController, willSpeak instruction: SpokenInstruction, routeProgress: RouteProgress) -> SpokenInstruction? +} + +public extension VoiceControllerDelegate { + + func voiceController(_ voiceController: RouteVoiceController, didFallBackTo synthesizer: AVSpeechSynthesizer, error: SpeechError) { + logUnimplemented(protocolType: VoiceControllerDelegate.self, level: .debug) + } + + func voiceController(_ voiceController: RouteVoiceController, spokenInstructionsDidFailWith error: Error) { + logUnimplemented(protocolType: VoiceControllerDelegate.self, level: .debug) + } + + func voiceController(_ voiceController: RouteVoiceController, didInterrupt interruptedInstruction: SpokenInstruction, with interruptingInstruction: SpokenInstruction) { + logUnimplemented(protocolType: VoiceControllerDelegate.self, level: .debug) + } + + func voiceController(_ voiceController: RouteVoiceController, willSpeak instruction: SpokenInstruction, routeProgress: RouteProgress) -> SpokenInstruction? { + logUnimplemented(protocolType: VoiceControllerDelegate.self, level: .debug) + return nil + } } diff --git a/MapboxNavigation/StatusView.swift b/MapboxNavigation/StatusView.swift index 82c96c05c18..5f619711f81 100644 --- a/MapboxNavigation/StatusView.swift +++ b/MapboxNavigation/StatusView.swift @@ -1,23 +1,23 @@ import UIKit /// :nodoc: -@objc public protocol DeprecatedStatusViewDelegate: class {} +public protocol DeprecatedStatusViewDelegate: class {} /** A protocol for listening in on changes made to a `StatusView`. */ @available(*, deprecated, message: "Add a target to StatusView for UIControl.Event.valueChanged instead.") -@objc public protocol StatusViewDelegate: DeprecatedStatusViewDelegate { +public protocol StatusViewDelegate: DeprecatedStatusViewDelegate { /** Indicates a value in the status view has changed by the user interacting with it. */ @available(*, deprecated, message: "Add a target to StatusView for UIControl.Event.valueChanged instead.") - @objc optional func statusView(_ statusView: StatusView, valueChangedTo value: Double) + func statusView(_ statusView: StatusView, valueChangedTo value: Double) } /// :nodoc: -@objc private protocol StatusViewDelegateDeprecations { - @objc optional func statusView(_ statusView: StatusView, valueChangedTo value: Double) +private protocol StatusViewDelegateDeprecations { + func statusView(_ statusView: StatusView, valueChangedTo value: Double) } /** @@ -26,18 +26,16 @@ import UIKit A translucent bar that responds to tap and swipe gestures, similar to a scrubber or stepper control, and expands and collapses to maximize screen real estate. */ @IBDesignable -@objc(MBStatusView) public class StatusView: UIControl { - weak var activityIndicatorView: UIActivityIndicatorView! weak var textLabel: UILabel! - @objc public weak var delegate: DeprecatedStatusViewDelegate? + public weak var delegate: DeprecatedStatusViewDelegate? var panStartPoint: CGPoint? var isCurrentlyVisible: Bool = false @available(*, deprecated, renamed: "isEnabled") - @objc public var canChangeValue: Bool { + public var canChangeValue: Bool { get { return isEnabled } @@ -49,16 +47,16 @@ public class StatusView: UIControl { var value: Double = 0 { didSet { sendActions(for: .valueChanged) - (delegate as? StatusViewDelegateDeprecations)?.statusView?(self, valueChangedTo: value) + (delegate as? StatusViewDelegateDeprecations)?.statusView(self, valueChangedTo: value) } } - @objc public override init(frame: CGRect) { + public override init(frame: CGRect) { super.init(frame: frame) commonInit() } - @objc public required init?(coder aDecoder: NSCoder) { + public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) commonInit() } @@ -124,7 +122,6 @@ public class StatusView: UIControl { } } - public func showStatus(title: String, spinner spin: Bool = false, duration: TimeInterval, animated: Bool = true, interactive: Bool = false) { show(title, showSpinner: spin, interactive: interactive) guard duration < .infinity else { return } @@ -136,7 +133,6 @@ public class StatusView: UIControl { let title = String.localizedStringWithFormat(format, NumberFormatter.localizedString(from: speed as NSNumber, number: .decimal)) showStatus(title: title, duration: .infinity, interactive: true) } - /** Shows the status view with an optional spinner. @@ -167,7 +163,6 @@ public class StatusView: UIControl { Hides the status view. */ public func hide(delay: TimeInterval = 0, animated: Bool = true) { - let hide = { self.isHidden = true self.textLabel.alpha = 0 diff --git a/MapboxNavigation/StepsViewController.swift b/MapboxNavigation/StepsViewController.swift index e8089e28bcf..d1469731dd8 100644 --- a/MapboxNavigation/StepsViewController.swift +++ b/MapboxNavigation/StepsViewController.swift @@ -4,30 +4,25 @@ import MapboxCoreNavigation import Turf /// :nodoc: -@objc(MBStepsBackgroundView) open class StepsBackgroundView: UIView { } - /** `StepsViewControllerDelegate` provides methods for user interactions in a `StepsViewController`. */ -@objc public protocol StepsViewControllerDelegate: class { - +public protocol StepsViewControllerDelegate: class { /** Called when the user selects a step in a `StepsViewController`. */ - @objc optional func stepsViewController(_ viewController: StepsViewController, didSelect legIndex: Int, stepIndex: Int, cell: StepTableViewCell) + func stepsViewController(_ viewController: StepsViewController, didSelect legIndex: Int, stepIndex: Int, cell: StepTableViewCell) /** Called when the user dismisses the `StepsViewController`. */ - @objc func didDismissStepsViewController(_ viewController: StepsViewController) + func didDismissStepsViewController(_ viewController: StepsViewController) } /// :nodoc: -@objc(MBStepsViewController) public class StepsViewController: UIViewController { - weak var tableView: UITableView! weak var backgroundView: UIView! weak var bottomView: UIView! @@ -57,7 +52,6 @@ public class StepsViewController: UIViewController { @discardableResult func rebuildDataSourceIfNecessary() -> Bool { - let legIndex = routeProgress.legIndex let stepIndex = routeProgress.currentLegProgress.stepIndex let didProcessCurrentStep = previousLegIndex == legIndex && previousStepIndex == stepIndex @@ -110,7 +104,6 @@ public class StepsViewController: UIViewController { } @objc func progressDidChange(_ notification: Notification) { - if rebuildDataSourceIfNecessary() { tableView.reloadData() } @@ -189,10 +182,10 @@ public class StepsViewController: UIViewController { Dismisses the `StepsViewController`. */ public func dismiss(completion: CompletionHandler? = nil) { - willMove(toParent: nil) - view.removeFromSuperview() - removeFromParent() - completion?() + willMove(toParent: nil) + view.removeFromSuperview() + removeFromParent() + completion?() } } @@ -210,7 +203,7 @@ extension StepsViewController: UITableViewDelegate { // For the current leg, we need to know the upcoming step. stepIndex += indexPath.row + 1 > sections[indexPath.section].count ? 0 : 1 } - delegate?.stepsViewController?(self, didSelect: indexPath.section, stepIndex: stepIndex, cell: cell) + delegate?.stepsViewController(self, didSelect: indexPath.section, stepIndex: stepIndex, cell: cell) } } @@ -278,13 +271,10 @@ extension StepsViewController: UITableViewDataSource { } /// :nodoc: -@objc(MBStepInstructionsView) open class StepInstructionsView: BaseInstructionsBannerView { } /// :nodoc: -@objc(MBStepTableViewCell) open class StepTableViewCell: UITableViewCell { - weak var instructionsView: StepInstructionsView! weak var separatorView: SeparatorView! diff --git a/MapboxNavigation/String.swift b/MapboxNavigation/String.swift index a0763a36951..09d778609bb 100644 --- a/MapboxNavigation/String.swift +++ b/MapboxNavigation/String.swift @@ -1,4 +1,5 @@ import Foundation +import CommonCrypto let ISO8601Formatter: DateFormatter = { let formatter = DateFormatter() @@ -25,4 +26,17 @@ extension String { func byReplacing(_ replacements: [Replacement]) -> String { return replacements.reduce(self) { $0.replacingOccurrences(of: $1.of, with: $1.with) } } + + /** + Returns the MD5 hash of the string. + */ + var md5: String { + let length = Int(CC_MD5_DIGEST_LENGTH) + let digest = utf8CString.withUnsafeBufferPointer { (body) -> [UInt8] in + var digest = [UInt8](repeating: 0, count: length) + CC_MD5(body.baseAddress, CC_LONG(lengthOfBytes(using: .utf8)), &digest) + return digest + } + return digest.lazy.map { String(format: "%02x", $0) }.joined() + } } diff --git a/MapboxNavigation/Style.swift b/MapboxNavigation/Style.swift index 84b4765d0f3..fad6e9b633d 100644 --- a/MapboxNavigation/Style.swift +++ b/MapboxNavigation/Style.swift @@ -7,8 +7,7 @@ import UIKit */ @objc(MBStyle) open class Style: NSObject { - - /// General styling + // MARK: General styling /** Sets the tint color for guidance arrow, highlighted text, progress bar and more. @@ -67,7 +66,6 @@ open class DismissButton: Button { } /// :nodoc: @objc(MBFloatingButton) open class FloatingButton: Button { - static let buttonSize = CGSize(width: 50, height: 50) static let sizeConstraintPriority = UILayoutPriority(999.0) //Don't fight with the stack view (superview) when it tries to hide buttons. @@ -108,7 +106,6 @@ open class FloatingButton: Button { /// :nodoc: @objc(MBReportButton) public class ReportButton: Button { - static let defaultInsets: UIEdgeInsets = 10.0 static let defaultCornerRadius: CGFloat = 4.0 @@ -187,7 +184,6 @@ public class ResumeButton: UIControl { /// :nodoc: @objc(MBDraggableView) open class StepListIndicatorView: UIView { - // Workaround the fact that UIView properties are not marked with UI_APPEARANCE_SELECTOR @objc dynamic open var gradientColors: [UIColor] = [.gray, .lightGray, .gray] { didSet { @@ -213,7 +209,6 @@ open class StepListIndicatorView: UIView { view.frame = parentView.bounds parentView.addSubview(view) } - } /// :nodoc: @@ -347,7 +342,6 @@ open class SecondaryLabel: InstructionLabel { } /// :nodoc: @objc(MBTimeRemainingLabel) open class TimeRemainingLabel: StylableLabel { - // Sets the text color for no or unknown traffic @objc dynamic public var trafficUnknownColor: UIColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) { didSet { @@ -383,7 +377,6 @@ open class SubtitleLabel: StylableLabel { } /// :nodoc: @objc(MBWayNameView) open class WayNameView: UIView { - private static let textInsets = UIEdgeInsets(top: 6, left: 14, bottom: 6, right: 14) lazy var label: WayNameLabel = .forAutoLayout() @@ -426,7 +419,6 @@ open class WayNameView: UIView { commonInit() } - func commonInit() { addSubview(label) layoutMargins = WayNameView.textInsets @@ -446,7 +438,6 @@ open class WayNameLabel: StylableLabel {} /// :nodoc: @objc(MBProgressBar) public class ProgressBar: UIView { - let bar = UIView() // Sets the color of the progress bar. @@ -494,7 +485,6 @@ public class ProgressBar: UIView { /// :nodoc: @objc(MBLineView) public class LineView: UIView { - // Set the line color on all line views. @objc dynamic public var lineColor: UIColor = .black { didSet { @@ -506,13 +496,11 @@ public class LineView: UIView { /// :nodoc: @objc(MBSeparatorView) -public class SeparatorView: UIView { } +public class SeparatorView: UIView {} /// :nodoc: @objc(MBStylableButton) open class StylableButton: UIButton { - - // Sets the font on the button’s titleLabel @objc dynamic open var textFont: UIFont = UIFont.systemFont(ofSize: 20, weight: .medium) { didSet { @@ -562,7 +550,6 @@ open class ManeuverContainerView: UIView { } } - /// :nodoc: @objc(MBBannerContainerView) open class BannerContainerView: UIView { } @@ -575,7 +562,6 @@ open class TopBannerView: UIView { } @objc(MBBottomBannerView) open class BottomBannerView: UIView { } -@objc(MBBottomPaddingView) open class BottomPaddingView: BottomBannerView { } /// :nodoc: @@ -584,7 +570,6 @@ class NavigationAnnotation: MGLPointAnnotation { } /// :nodoc: @objc(MBMarkerView) public class MarkerView: UIView { - // Sets the inner color on the pin. @objc public dynamic var innerColor: UIColor = .white { didSet { diff --git a/MapboxNavigation/StyleKitMarker.swift b/MapboxNavigation/StyleKitMarker.swift index d6410d6537b..08465375b26 100644 --- a/MapboxNavigation/StyleKitMarker.swift +++ b/MapboxNavigation/StyleKitMarker.swift @@ -1,6 +1,6 @@ import UIKit -@objc(MBStyleKitMarker) + public class StyleKitMarker: NSObject { //// Drawing Methods diff --git a/MapboxNavigation/StyleManager.swift b/MapboxNavigation/StyleManager.swift index 15324d552f3..417b01cdd14 100644 --- a/MapboxNavigation/StyleManager.swift +++ b/MapboxNavigation/StyleManager.swift @@ -1,49 +1,64 @@ import UIKit import Solar +import MapboxCoreNavigation /** The `StyleManagerDelegate` protocol defines a set of methods used for controlling the style. */ -@objc(MBStyleManagerDelegate) -public protocol StyleManagerDelegate: NSObjectProtocol { +public protocol StyleManagerDelegate: class, UnimplementedLogging { /** - Asks the delegate for a location to use when calculating sunset and sunrise. + Asks the delegate for a location to use when calculating sunset and sunrise + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. . */ - @objc(locationForStyleManager:) func location(for styleManager: StyleManager) -> CLLocation? /** Informs the delegate that a style was applied. - This delegate method is the equivalent of `Notification.Name.styleManagerDidApplyStyle`. + + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. + */ - @objc(styleManager:didApplyStyle:) - optional func styleManager(_ styleManager: StyleManager, didApply style: Style) + func styleManager(_ styleManager: StyleManager, didApply style: Style) /** Informs the delegate that the manager forcefully refreshed UIAppearance. + - note: This delegate method includes a default implementation that prints a warning to the console when this method is called. See `UnimplementedLogging` for details. */ - @objc optional func styleManagerDidRefreshAppearance(_ styleManager: StyleManager) + func styleManagerDidRefreshAppearance(_ styleManager: StyleManager) +} + +public extension StyleManagerDelegate { + func location(for styleManager: StyleManager) -> CLLocation? { + logUnimplemented(protocolType: StyleManagerDelegate.self, level: .debug) + return nil + } + + func styleManager(_ styleManager: StyleManager, didApply style: Style) { + logUnimplemented(protocolType: StyleManagerDelegate.self, level: .debug) + } + + func styleManagerDidRefreshAppearance(_ styleManager: StyleManager) { + logUnimplemented(protocolType: StyleManagerDelegate.self, level: .debug) + } } /** A manager that handles `Style` objects. The manager listens for significant time changes and changes to the content size to apply an approriate style for the given condition. */ -@objc(MBStyleManager) open class StyleManager: NSObject { - /** The receiver of the delegate. See `StyleManagerDelegate` for more information. */ - @objc public weak var delegate: StyleManagerDelegate? + public weak var delegate: StyleManagerDelegate? /** Determines whether the style manager should apply a new style given the time of day. - precondition: Two styles must be provided for this property to have any effect. */ - @objc public var automaticallyAdjustsStyleForTimeOfDay = true { + public var automaticallyAdjustsStyleForTimeOfDay = true { didSet { resetTimeOfDayTimer() } @@ -57,7 +72,7 @@ open class StyleManager: NSObject { - precondition: Two styles must be provided for `StyleManager.automaticallyAdjustsStyleForTimeOfDay` to have any effect. */ - @objc public var styles = [Style]() { + public var styles = [Style]() { didSet { applyStyle() resetTimeOfDayTimer() @@ -74,7 +89,7 @@ open class StyleManager: NSObject { } } - @objc public override init() { + public override init() { super.init() resumeNotifications() resetTimeOfDayTimer() @@ -134,7 +149,7 @@ open class StyleManager: NSObject { style.apply() currentStyleType = styleType currentStyle = style - delegate?.styleManager?(self, didApply: style) + delegate?.styleManager(self, didApply: style) } } @@ -148,7 +163,7 @@ open class StyleManager: NSObject { style.apply() currentStyleType = style.styleType currentStyle = style - delegate?.styleManager?(self, didApply: style) + delegate?.styleManager(self, didApply: style) } return } @@ -159,7 +174,7 @@ open class StyleManager: NSObject { style.apply() currentStyleType = style.styleType currentStyle = style - delegate?.styleManager?(self, didApply: style) + delegate?.styleManager(self, didApply: style) } return } @@ -180,8 +195,8 @@ open class StyleManager: NSObject { private func postDidApplyStyleNotification(style: Style) { NotificationCenter.default.post(name: .styleManagerDidApplyStyle, object: self, userInfo: [ - MBStyleManagerNotificationUserInfoKey.styleKey: style, - MBStyleManagerNotificationUserInfoKey.styleManagerKey: self + StyleManagerNotificationUserInfoKey.styleKey: style, + StyleManagerNotificationUserInfoKey.styleManagerKey: self ]) } @@ -210,7 +225,7 @@ open class StyleManager: NSObject { } } - delegate?.styleManagerDidRefreshAppearance?(self) + delegate?.styleManagerDidRefreshAppearance(self) } } diff --git a/MapboxNavigation/StyleType.swift b/MapboxNavigation/StyleType.swift index 8153aa05537..713e3d0b388 100644 --- a/MapboxNavigation/StyleType.swift +++ b/MapboxNavigation/StyleType.swift @@ -1,8 +1,6 @@ import Foundation -@objc(MBStyleType) -public enum StyleType: Int, CustomStringConvertible { - +@objc public enum StyleType: Int, CustomStringConvertible { case day case night diff --git a/MapboxNavigation/TopBannerViewController.swift b/MapboxNavigation/TopBannerViewController.swift index 7e22474bf6a..258c492899f 100644 --- a/MapboxNavigation/TopBannerViewController.swift +++ b/MapboxNavigation/TopBannerViewController.swift @@ -2,23 +2,47 @@ import Foundation import MapboxCoreNavigation import MapboxDirections - -@objc public protocol TopBannerViewControllerDelegate: class { - @objc optional func topBanner(_ banner: TopBannerViewController, didSwipeInDirection direction: UISwipeGestureRecognizer.Direction) +public protocol TopBannerViewControllerDelegate: class, UnimplementedLogging { + func topBanner(_ banner: TopBannerViewController, didSwipeInDirection direction: UISwipeGestureRecognizer.Direction) - @objc optional func topBanner(_ banner: TopBannerViewController, didSelect legIndex: Int, stepIndex: Int, cell: StepTableViewCell) + func topBanner(_ banner: TopBannerViewController, didSelect legIndex: Int, stepIndex: Int, cell: StepTableViewCell) - @objc optional func topBanner(_ banner: TopBannerViewController, willDisplayStepsController: StepsViewController) + func topBanner(_ banner: TopBannerViewController, willDisplayStepsController: StepsViewController) - @objc optional func topBanner(_ banner: TopBannerViewController, didDisplayStepsController: StepsViewController) + func topBanner(_ banner: TopBannerViewController, didDisplayStepsController: StepsViewController) - @objc optional func topBanner(_ banner: TopBannerViewController, willDismissStepsController: StepsViewController) + func topBanner(_ banner: TopBannerViewController, willDismissStepsController: StepsViewController) - @objc optional func topBanner(_ banner: TopBannerViewController, didDismissStepsController: StepsViewController) + func topBanner(_ banner: TopBannerViewController, didDismissStepsController: StepsViewController) } -@objc open class TopBannerViewController: UIViewController { +public extension TopBannerViewControllerDelegate { + func topBanner(_ banner: TopBannerViewController, didSwipeInDirection direction: UISwipeGestureRecognizer.Direction) { + logUnimplemented(protocolType: TopBannerViewControllerDelegate.self, level: .debug) + } + + func topBanner(_ banner: TopBannerViewController, didSelect legIndex: Int, stepIndex: Int, cell: StepTableViewCell) { + logUnimplemented(protocolType: TopBannerViewControllerDelegate.self, level: .debug) + } + + func topBanner(_ banner: TopBannerViewController, willDisplayStepsController: StepsViewController) { + logUnimplemented(protocolType: TopBannerViewControllerDelegate.self, level: .debug) + } + + func topBanner(_ banner: TopBannerViewController, didDisplayStepsController: StepsViewController) { + logUnimplemented(protocolType: TopBannerViewControllerDelegate.self, level: .debug) + } + + func topBanner(_ banner: TopBannerViewController, willDismissStepsController: StepsViewController) { + logUnimplemented(protocolType: TopBannerViewControllerDelegate.self, level: .debug) + } + func topBanner(_ banner: TopBannerViewController, didDismissStepsController: StepsViewController) { + logUnimplemented(protocolType: TopBannerViewControllerDelegate.self, level: .debug) + } +} + +open class TopBannerViewController: UIViewController { weak var delegate: TopBannerViewControllerDelegate? = nil lazy var topPaddingView: TopBannerView = .forAutoLayout() @@ -79,7 +103,7 @@ import MapboxDirections return [instructionsBannerView] + secondaryChildren } private var secondaryChildren: [UIView] { - return [lanesView, nextBannerView, statusView] + return [lanesView, nextBannerView, statusView] } public var isDisplayingPreviewInstructions: Bool { @@ -88,7 +112,6 @@ import MapboxDirections private(set) public var isDisplayingSteps: Bool = false - private(set) var previewSteps: [RouteStep]? private(set) var currentPreviewStep: (RouteStep, Int)? @@ -102,7 +125,6 @@ import MapboxDirections super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } - override open func viewDidLoad() { view.backgroundColor = .clear super.viewDidLoad() @@ -111,7 +133,6 @@ import MapboxDirections setupInformationStackView() } - private func setupViews() { let children = [stepsContainer, topPaddingView, informationStackView] view.addSubviews(children) @@ -150,7 +171,6 @@ import MapboxDirections } } - public func displayStepsTable() { dismissStepsTable() @@ -163,7 +183,7 @@ import MapboxDirections var stepsHeightPresizingConstraint: NSLayoutConstraint? = nil - delegate?.topBanner?(self, willDisplayStepsController: controller) + delegate?.topBanner(self, willDisplayStepsController: controller) embed(controller, in: stepsContainer) { (parent, child) -> [NSLayoutConstraint] in child.view.translatesAutoresizingMaskIntoConstraints = false @@ -191,29 +211,26 @@ import MapboxDirections stepsHeightPresizingConstraint?.isActive = false NSLayoutConstraint.activate(self.stepsContainerShowConstraints) - let finally: (Bool) -> Void = { [weak self] _ in guard let self = self else { return } self.view.isUserInteractionEnabled = true - self.delegate?.topBanner?(self, didDisplayStepsController: controller) + self.delegate?.topBanner(self, didDisplayStepsController: controller) } UIView.animate(withDuration: 0.35, delay: 0.0, options: [.curveEaseOut], animations: parent.view.layoutIfNeeded, completion: finally) } hideSecondaryChildren(completion: stepsInAnimation) - } public func dismissStepsTable(completion: CompletionHandler? = nil) { - guard let parent = parent, let steps = stepsViewController else { return } + guard let parent = parent, let steps = stepsViewController else { return } parent.view.layoutIfNeeded() - delegate?.topBanner?(self, willDismissStepsController: steps) - + delegate?.topBanner(self, willDismissStepsController: steps) NSLayoutConstraint.deactivate(stepsContainerShowConstraints) NSLayoutConstraint.activate(stepsContainerHideConstraints) @@ -224,7 +241,7 @@ import MapboxDirections } self.view.isUserInteractionEnabled = true - self.delegate?.topBanner?(self, didDismissStepsController: steps) + self.delegate?.topBanner(self, didDismissStepsController: steps) completion?() } @@ -244,8 +261,6 @@ import MapboxDirections steps.dismiss() self.stepsViewController = nil } - - } private func showSecondaryChildren(completion: CompletionHandler? = nil) { statusView.isHidden = !statusView.isCurrentlyVisible @@ -296,8 +311,6 @@ import MapboxDirections previewSteps = steps currentPreviewStep = (step, index) - - guard let instructions = step.instructionsDisplayedAlongStep?.last else { return } let instructionsView = StepInstructionsView(frame: instructionsBannerView.frame) @@ -333,7 +346,6 @@ import MapboxDirections } } - private func addInstructionsBanner() { informationStackView.insertArrangedSubview(instructionsBannerView, at: 0) instructionsBannerView.delegate = self @@ -346,7 +358,6 @@ extension TopBannerViewController: NavigationComponent { public func navigationService(_ service: NavigationService, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) { routeProgress = progress instructionsBannerView.updateDistance(for: progress.currentLegProgress.currentStepProgress) - } public func navigationService(_ service: NavigationService, didPassVisualInstructionPoint instruction: VisualInstructionBanner, routeProgress: RouteProgress) { @@ -409,14 +420,13 @@ extension TopBannerViewController: InstructionsBannerViewDelegate { } public func didSwipeInstructionsBanner(_ sender: BaseInstructionsBannerView, swipeDirection direction: UISwipeGestureRecognizer.Direction) { - delegate?.topBanner?(self, didSwipeInDirection: direction) + delegate?.topBanner(self, didSwipeInDirection: direction) } } extension TopBannerViewController: StepsViewControllerDelegate { - public func stepsViewController(_ viewController: StepsViewController, didSelect legIndex: Int, stepIndex: Int, cell: StepTableViewCell) { - delegate?.topBanner?(self, didSelect: legIndex, stepIndex: stepIndex, cell: cell) + delegate?.topBanner(self, didSelect: legIndex, stepIndex: stepIndex, cell: cell) } public func didDismissStepsViewController(_ viewController: StepsViewController) { diff --git a/MapboxNavigation/Transitioning.swift b/MapboxNavigation/Transitioning.swift index 4df08593aa4..1bbdbca4345 100644 --- a/MapboxNavigation/Transitioning.swift +++ b/MapboxNavigation/Transitioning.swift @@ -7,7 +7,6 @@ class Interactor: UIPercentDrivenInteractiveTransition { class DismissAnimator: NSObject { } extension DismissAnimator: UIViewControllerAnimatedTransitioning { - func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.5 } @@ -72,10 +71,9 @@ extension PresentAnimator: UIViewControllerAnimatedTransitioning { } } -@objc protocol DismissDraggable: UIViewControllerTransitioningDelegate { +protocol DismissDraggable: UIViewControllerTransitioningDelegate { var interactor: Interactor { get } var draggableHeight: CGFloat { get } - @objc optional func handleDismissPan(_ sender: UIPanGestureRecognizer) } fileprivate extension Selector { @@ -90,7 +88,6 @@ extension DismissDraggable where Self: UIViewController { } fileprivate extension UIViewController { - @objc func handleDismissPan(_ sender: UIPanGestureRecognizer) { self.handlePan(sender) } diff --git a/MapboxNavigation/UICollectionView.swift b/MapboxNavigation/UICollectionView.swift index 5a938040c44..ab11fccdebe 100644 --- a/MapboxNavigation/UICollectionView.swift +++ b/MapboxNavigation/UICollectionView.swift @@ -1,7 +1,6 @@ import UIKit extension UICollectionView { - func numberOfRows(using delegate: UIViewController & UICollectionViewDelegateFlowLayout) -> Int { let layout = (collectionViewLayout as? UICollectionViewFlowLayout)! diff --git a/MapboxNavigation/UIDevice.swift b/MapboxNavigation/UIDevice.swift index 9e0582a3cb1..fa494e033e4 100644 --- a/MapboxNavigation/UIDevice.swift +++ b/MapboxNavigation/UIDevice.swift @@ -5,11 +5,10 @@ import CoreLocation #endif extension UIDevice { - /** Returns a `Bool` whether the device is plugged in. Returns false if not an iOS device. */ - @objc public var isPluggedIn: Bool { + public var isPluggedIn: Bool { #if os(iOS) return [.charging, .full].contains(UIDevice.current.batteryState) #else diff --git a/MapboxNavigation/UIFont.swift b/MapboxNavigation/UIFont.swift index 499bd82e188..753a989a1b3 100644 --- a/MapboxNavigation/UIFont.swift +++ b/MapboxNavigation/UIFont.swift @@ -1,7 +1,6 @@ import UIKit extension UIFont { - var fontSizeMultiplier: CGFloat { get { switch UIApplication.shared.preferredContentSizeCategory { @@ -25,7 +24,7 @@ extension UIFont { /** Returns an adjusted font for the `preferredContentSizeCategory`. */ - @objc public var adjustedFont: UIFont { + public var adjustedFont: UIFont { let font = with(multiplier: fontSizeMultiplier) return font } diff --git a/MapboxNavigation/UIView.swift b/MapboxNavigation/UIView.swift index e731566f3a1..e352f623170 100644 --- a/MapboxNavigation/UIView.swift +++ b/MapboxNavigation/UIView.swift @@ -75,7 +75,6 @@ extension UIView { pinTo(parentView: superview, respectingMargins: margins) } - class func forAutoLayout(frame: CGRect = .zero, hidden: Bool = false) -> ViewType { let view = ViewType.init(frame: frame) view.isHidden = hidden diff --git a/MapboxNavigation/UIViewController.swift b/MapboxNavigation/UIViewController.swift index ad6b47a7c7a..411309835c4 100644 --- a/MapboxNavigation/UIViewController.swift +++ b/MapboxNavigation/UIViewController.swift @@ -1,8 +1,6 @@ import UIKit - extension UIViewController { - func topMostViewController() -> UIViewController? { return topViewController(controller: self) } diff --git a/MapboxNavigation/UserCourseView.swift b/MapboxNavigation/UserCourseView.swift index fd03ca6a64f..2cba4cb162b 100644 --- a/MapboxNavigation/UserCourseView.swift +++ b/MapboxNavigation/UserCourseView.swift @@ -5,21 +5,20 @@ import Mapbox let PuckSize: CGFloat = 45 /** - A view that represents the user’s location and course on a `NavigationMapView`. + A protocol that represents a `UIView` which tracks the user’s location and course on a `NavigationMapView`. */ -@objc(MBUserCourseView) -public protocol UserCourseView where Self: UIView { - @objc optional var location: CLLocation { get set } - @objc optional var direction: CLLocationDegrees { get set } - @objc optional var pitch: CLLocationDegrees { get set } - +public protocol CourseUpdatable where Self: UIView { /** Updates the view to reflect the given location and other camera properties. */ - @objc optional func update(location: CLLocation, pitch: CGFloat, direction: CLLocationDegrees, animated: Bool, tracksUserCourse: Bool) + func update(location: CLLocation, pitch: CGFloat, direction: CLLocationDegrees, animated: Bool, tracksUserCourse: Bool) } -extension UIView { +public extension CourseUpdatable { + func update(location: CLLocation, pitch: CGFloat, direction: CLLocationDegrees, animated: Bool, tracksUserCourse: Bool) { + applyDefaultUserPuckTransformation(location: location, pitch: pitch, direction: direction, animated: animated, tracksUserCourse: tracksUserCourse) + } + func applyDefaultUserPuckTransformation(location: CLLocation, pitch: CGFloat, direction: CLLocationDegrees, animated: Bool, tracksUserCourse: Bool) { let duration: TimeInterval = animated ? 1 : 0 UIView.animate(withDuration: duration, delay: 0, options: [.beginFromCurrentState, .curveLinear], animations: { @@ -36,16 +35,13 @@ extension UIView { /** A view representing the user’s location on screen. */ -@objc(MBUserPuckCourseView) -public class UserPuckCourseView: UIView, UserCourseView { - +public class UserPuckCourseView: UIView, CourseUpdatable { /** Transforms the location of the user puck. */ public func update(location: CLLocation, pitch: CGFloat, direction: CLLocationDegrees, animated: Bool, tracksUserCourse: Bool) { let duration: TimeInterval = animated ? 1 : 0 UIView.animate(withDuration: duration, delay: 0, options: [.beginFromCurrentState, .curveLinear], animations: { - let angle = tracksUserCourse ? 0 : CLLocationDegrees(direction - location.course) self.puckView.layer.setAffineTransform(CGAffineTransform.identity.rotated(by: -CGFloat(angle.toRadians()))) @@ -53,7 +49,6 @@ public class UserPuckCourseView: UIView, UserCourseView { transform = CATransform3DScale(transform, tracksUserCourse ? 1 : 0.5, tracksUserCourse ? 1 : 0.5, 1) transform.m34 = -1.0 / 1000 // (-1 / distance to projection plane) self.layer.sublayerTransform = transform - }, completion: nil) } diff --git a/MapboxNavigation/VisualInstruction.swift b/MapboxNavigation/VisualInstruction.swift index 15a7a710514..6f337bf6e5b 100644 --- a/MapboxNavigation/VisualInstruction.swift +++ b/MapboxNavigation/VisualInstruction.swift @@ -4,7 +4,6 @@ import CarPlay #endif extension VisualInstruction { - /// Returns true if `VisualInstruction.components` contains any `LaneIndicationComponent`. public var containsLaneIndications: Bool { return components.contains(where: { $0 is LaneIndicationComponent }) diff --git a/MapboxNavigation/VisualInstructionComponent.swift b/MapboxNavigation/VisualInstructionComponent.swift index 3af9ac2c509..2a69c9b3f69 100644 --- a/MapboxNavigation/VisualInstructionComponent.swift +++ b/MapboxNavigation/VisualInstructionComponent.swift @@ -2,7 +2,6 @@ import UIKit import MapboxDirections extension VisualInstructionComponent { - static let scale = UIScreen.main.scale var cacheKey: String? { diff --git a/MapboxNavigationTests/BottomBannerSnapshotTests.swift b/MapboxNavigationTests/BottomBannerSnapshotTests.swift index 83e72275614..c9737732a30 100644 --- a/MapboxNavigationTests/BottomBannerSnapshotTests.swift +++ b/MapboxNavigationTests/BottomBannerSnapshotTests.swift @@ -52,7 +52,6 @@ class BottomBannerSnapshotTests: SnapshotTest { verify(host, for: Device.iPhone8.portrait) } - func applyStyling(to subject: BottomBannerViewController) { subject.bottomBannerView.backgroundColor = .white diff --git a/MapboxNavigationTests/BridgingTests.m b/MapboxNavigationTests/BridgingTests.m deleted file mode 100644 index 3fc0076fded..00000000000 --- a/MapboxNavigationTests/BridgingTests.m +++ /dev/null @@ -1,35 +0,0 @@ -#import -@import MapboxNavigation; -@import MapboxCoreNavigation; -@import TestHelper; - -@interface BridgingTests : XCTestCase - -@end - -@implementation BridgingTests - -- (void)testNavigationOptions { - MBNavigationOptions *options = [MBNavigationOptions navigationOptions]; - XCTAssertNotNil(options); -} - -- (void)testRouteVoiceController { - MBRoute *route = [MBFixture routeFromJSONFileName:@"routeWithInstructions"]; - MBNavigationService *service = [[MBNavigationService alloc] initWithRoute:route - directions:nil - locationSource:nil - eventsManagerType:nil - simulating:MBNavigationSimulationOptionsNever - routerType:nil]; - - MBRouteVoiceController *voiceController = [[MBRouteVoiceController alloc] initWithNavigationService:service]; - voiceController.voiceControllerDelegate = self; -} - -- (void)testNavigationMapView { - MBNavigationMapView *mapView = nil; - mapView.navigationMapViewDelegate = self; -} - -@end diff --git a/MapboxNavigationTests/CPBarButton+MBTestable.m b/MapboxNavigationTests/CPBarButton+MBTestable.m index 4780adebd17..75180c840ae 100644 --- a/MapboxNavigationTests/CPBarButton+MBTestable.m +++ b/MapboxNavigationTests/CPBarButton+MBTestable.m @@ -9,7 +9,6 @@ - (instancetype)init_original_initWithType:(CPBarButtonType)type handler:(void ( @end - static char *HandlerKey; @implementation CPBarButton (MBTestable) @@ -20,7 +19,6 @@ + (void)load { Method newMethod = class_getInstanceMethod(self, @selector(initWithType:handlerToKeep:)); class_replaceMethod(self, @selector(initWithType:handler:), method_getImplementation(newMethod), method_getTypeEncoding(newMethod)); - } - (instancetype)initWithType:(CPBarButtonType)type handlerToKeep:(void (^ _Nullable)(CPBarButton *barButton))handler { diff --git a/MapboxNavigationTests/CPMapTemplate+MBTestable.mm b/MapboxNavigationTests/CPMapTemplate+MBTestable.mm index 582652854b2..5879fb79981 100644 --- a/MapboxNavigationTests/CPMapTemplate+MBTestable.mm +++ b/MapboxNavigationTests/CPMapTemplate+MBTestable.mm @@ -12,7 +12,6 @@ @interface CPMapTemplate (MBTestableInternal) @end - @implementation CPMapTemplate (MBTestable) #pragma clang diagnostic push diff --git a/MapboxNavigationTests/CarPlayCompassViewTests.swift b/MapboxNavigationTests/CarPlayCompassViewTests.swift index 0e2b0e0359f..320b29d6802 100644 --- a/MapboxNavigationTests/CarPlayCompassViewTests.swift +++ b/MapboxNavigationTests/CarPlayCompassViewTests.swift @@ -4,7 +4,6 @@ import Mapbox @testable import MapboxNavigation class CarPlayCompassViewTests: SnapshotTest { - var window: UIWindow! let styles = [DayStyle(), NightStyle()] diff --git a/MapboxNavigationTests/CarPlayManagerTests.swift b/MapboxNavigationTests/CarPlayManagerTests.swift index 40f73e41233..ca4e2563ad1 100644 --- a/MapboxNavigationTests/CarPlayManagerTests.swift +++ b/MapboxNavigationTests/CarPlayManagerTests.swift @@ -13,7 +13,6 @@ import CarPlay @available(iOS 12.0, *) class CarPlayManagerTests: XCTestCase { - var manager: CarPlayManager? var searchController: CarPlaySearchController? var eventsManagerSpy: NavigationEventsManagerSpy? @@ -214,7 +213,6 @@ class CarPlayManagerTests: XCTestCase { } func testRouteFailure() { - let manager = CarPlayManager() let spy = CarPlayManagerFailureDelegateSpy() @@ -224,12 +222,9 @@ class CarPlayManagerTests: XCTestCase { manager.delegate = spy manager.didCalculate(nil, for: fakeOptions, between: nil, error: testError, completionHandler: { }) XCTAssert(spy.recievedError == testError, "Delegate should have receieved error") - } - func testDirectionsOverride() { - class DirectionsInvocationSpy: Directions { typealias VoidClosure = () -> Void var payload: VoidClosure? @@ -362,7 +357,6 @@ class CarPlayManagerSpec: QuickSpec { expect(mapTemplateSpy.currentTripPreviews).toNot(beEmpty()) expect(mapTemplateSpy.currentPreviewTextConfiguration?.startButtonTitle).to(equal(customStartButtonTitleText)) } - }) }) @@ -414,7 +408,6 @@ class CarPlayManagerSpec: QuickSpec { } private class CustomTripPreviewDelegate: CarPlayManagerDelegate { - var customTripPreviewTextConfiguration: CPTripPreviewTextConfiguration? var customTrip: CPTrip? @@ -479,7 +472,6 @@ class CarPlayManagerFailureDelegateSpy: CarPlayManagerDelegate { @available(iOS 12.0, *) class TestCarPlayManagerDelegate: CarPlayManagerDelegate { - public fileprivate(set) var navigationInitiated = false public fileprivate(set) var currentService: NavigationService? public fileprivate(set) var navigationEnded = false @@ -541,7 +533,6 @@ class CarPlayNavigationViewControllerTestable: CarPlayNavigationViewController { @available(iOS 12.0, *) class TestCarPlaySearchControllerDelegate: CarPlaySearchControllerDelegate { - public fileprivate(set) var interfaceController: CPInterfaceController? public fileprivate(set) var carPlayManager: CarPlayManager? @@ -603,7 +594,6 @@ public class MapTemplateSpyProvider: MapTemplateProvider { @available(iOS 12.0, *) class FakeCPInterfaceController: CPInterfaceController { - /** A simple stub which allows for instantiation of a CPInterfaceController for testing. diff --git a/MapboxNavigationTests/CarPlayNavigationViewControllerTests.swift b/MapboxNavigationTests/CarPlayNavigationViewControllerTests.swift index 2aab4f2a22e..887909c25f1 100644 --- a/MapboxNavigationTests/CarPlayNavigationViewControllerTests.swift +++ b/MapboxNavigationTests/CarPlayNavigationViewControllerTests.swift @@ -5,7 +5,6 @@ import XCTest import Foundation import TestHelper - @available(iOS 12.0, *) fileprivate class CarPlayNavigationDelegateSpy: NSObject, CarPlayNavigationDelegate { var didArriveExpectation: XCTestExpectation! @@ -28,9 +27,8 @@ fileprivate class CPManeuverFake: CPManeuver { @available(iOS 12.0, *) fileprivate class CPNavigationSessionFake: CPNavigationSession { - init(maneuvers: [CPManeuver]) { + init(maneuvers: [CPManeuver]) { fakeManeuvers = maneuvers - } override var upcomingManeuvers: [CPManeuver] { @@ -48,7 +46,6 @@ fileprivate class CPNavigationSessionFake: CPNavigationSession { @available(iOS 12.0, *) fileprivate class CarPlayNavigationViewControllerTests: XCTestCase { func testCarplayDisplaysCorrectEstimates() { - //set up the litany of dependancies let directions = Directions(accessToken: "fafedeadbeef") let manager = CarPlayManager(directions: directions) @@ -68,7 +65,7 @@ fileprivate class CarPlayNavigationViewControllerTests: XCTestCase { let subject = CarPlayNavigationViewController(navigationService: navService, mapTemplate: mapSpy, interfaceController: interface, manager: manager) subject.startNavigationSession(for: trip) let payload: [RouteControllerNotificationUserInfoKey:Any] = [.routeProgressKey : navService.routeProgress, - .locationKey: location] + .locationKey: location] let fakeNotication = NSNotification(name: .routeControllerProgressDidChange, object: navService.router, userInfo: payload) //fire the fake notification diff --git a/MapboxNavigationTests/CompassViewTests.swift b/MapboxNavigationTests/CompassViewTests.swift index faf40b66af6..6d35dcb152c 100644 --- a/MapboxNavigationTests/CompassViewTests.swift +++ b/MapboxNavigationTests/CompassViewTests.swift @@ -1,9 +1,7 @@ import XCTest @testable import MapboxNavigation - class CompassViewTests: XCTestCase { - func testDirections() { let compassView = CarPlayCompassView() let min: CLLocationDirection = -720 diff --git a/MapboxNavigationTests/DataCacheTests.swift b/MapboxNavigationTests/DataCacheTests.swift index db0fd754c15..554316e3e7d 100644 --- a/MapboxNavigationTests/DataCacheTests.swift +++ b/MapboxNavigationTests/DataCacheTests.swift @@ -2,9 +2,7 @@ import XCTest import TestHelper @testable import MapboxNavigation - class DataCacheTests: XCTestCase { - let cache: DataCache = DataCache() private func clearDisk() { diff --git a/MapboxCoreNavigationTests/Fixtures/md5_crazy_strings.txt b/MapboxNavigationTests/Fixtures/md5_crazy_strings.txt similarity index 100% rename from MapboxCoreNavigationTests/Fixtures/md5_crazy_strings.txt rename to MapboxNavigationTests/Fixtures/md5_crazy_strings.txt diff --git a/MapboxNavigationTests/GuidanceCardsSnapshotTests.swift b/MapboxNavigationTests/GuidanceCardsSnapshotTests.swift index 5352cc7a995..24c936abd3b 100644 --- a/MapboxNavigationTests/GuidanceCardsSnapshotTests.swift +++ b/MapboxNavigationTests/GuidanceCardsSnapshotTests.swift @@ -8,7 +8,6 @@ import Foundation @available(iOS 11.0, *) /// :nodoc: class GuidanceCardsSnapshotTests: SnapshotTest { - override func setUp() { super.setUp() recordMode = false diff --git a/MapboxNavigationTests/ImageCacheTests.swift b/MapboxNavigationTests/ImageCacheTests.swift index 33ad1d6d478..cc857676d18 100644 --- a/MapboxNavigationTests/ImageCacheTests.swift +++ b/MapboxNavigationTests/ImageCacheTests.swift @@ -1,9 +1,7 @@ import XCTest @testable import MapboxNavigation - class ImageCacheTests: XCTestCase { - let cache: ImageCache = ImageCache() let asyncTimeout: TimeInterval = 10.0 diff --git a/MapboxNavigationTests/ImageDownloaderTests.swift b/MapboxNavigationTests/ImageDownloaderTests.swift index d045b229b01..24bc694929b 100644 --- a/MapboxNavigationTests/ImageDownloaderTests.swift +++ b/MapboxNavigationTests/ImageDownloaderTests.swift @@ -1,9 +1,7 @@ import XCTest @testable import MapboxNavigation - class ImageDownloaderTests: XCTestCase { - lazy var sessionConfig: URLSessionConfiguration = { let config = URLSessionConfiguration.default config.protocolClasses = [ImageLoadingURLProtocolSpy.self] diff --git a/MapboxNavigationTests/ImageRepositoryTests.swift b/MapboxNavigationTests/ImageRepositoryTests.swift index 0f357cac10a..9a53e1cef28 100644 --- a/MapboxNavigationTests/ImageRepositoryTests.swift +++ b/MapboxNavigationTests/ImageRepositoryTests.swift @@ -2,7 +2,6 @@ import XCTest @testable import MapboxNavigation class ImageRepositoryTests: XCTestCase { - lazy var repository: ImageRepository = { let repo = ImageRepository.shared let config = URLSessionConfiguration.default @@ -27,7 +26,6 @@ class ImageRepositoryTests: XCTestCase { } override func tearDown() { - super.tearDown() } diff --git a/MapboxNavigationTests/InstructionPresenterTests.swift b/MapboxNavigationTests/InstructionPresenterTests.swift index 257ff7683ac..59828c42b79 100644 --- a/MapboxNavigationTests/InstructionPresenterTests.swift +++ b/MapboxNavigationTests/InstructionPresenterTests.swift @@ -4,11 +4,8 @@ import MapboxDirections import TestHelper @testable import MapboxNavigation - class InstructionPresenterTests: XCTestCase { - func testExitInstructionProvidesExit() { - let exitAttribute = VisualInstructionComponent(type: .exit, text: "Exit", imageURL: nil, abbreviation: nil, abbreviationPriority: 0) let exitCodeAttribute = VisualInstructionComponent(type: .exitCode, text: "123A", imageURL: nil, abbreviation: nil, abbreviationPriority: 0) let exitInstruction = VisualInstruction(text: nil, maneuverType: .takeOffRamp, maneuverDirection: .right, components: [exitAttribute, exitCodeAttribute]) diff --git a/MapboxNavigationTests/InstructionsBannerViewIntegrationTests.swift b/MapboxNavigationTests/InstructionsBannerViewIntegrationTests.swift index 001bbcfc8b0..de2ddbcc28c 100644 --- a/MapboxNavigationTests/InstructionsBannerViewIntegrationTests.swift +++ b/MapboxNavigationTests/InstructionsBannerViewIntegrationTests.swift @@ -3,7 +3,6 @@ import MapboxDirections @testable import MapboxNavigation @testable import MapboxCoreNavigation - func instructionsView(size: CGSize = .iPhone6Plus) -> InstructionsBannerView { let bannerHeight: CGFloat = 96 return InstructionsBannerView(frame: CGRect(origin: .zero, size: CGSize(width: size.width, height: bannerHeight))) @@ -13,18 +12,16 @@ func makeVisualInstruction(_ maneuverType: ManeuverType = .arrive, _ maneuverDirection: ManeuverDirection = .left, primaryInstruction: [VisualInstructionComponent], secondaryInstruction: [VisualInstructionComponent]?) -> VisualInstructionBanner { - let primary = VisualInstruction(text: "Instruction", maneuverType: maneuverType, maneuverDirection: maneuverDirection, components: primaryInstruction) var secondary: VisualInstruction? = nil if let secondaryInstruction = secondaryInstruction { secondary = VisualInstruction(text: "Instruction", maneuverType: maneuverType, maneuverDirection: maneuverDirection, components: secondaryInstruction) } - return VisualInstructionBanner(distanceAlongStep: 482.803, primaryInstruction: primary, secondaryInstruction: secondary, tertiaryInstruction: nil, drivingSide: .right) + return VisualInstructionBanner(distanceAlongStep: 482.803, primaryInstruction: primary, secondaryInstruction: secondary, tertiaryInstruction: nil, drivingSide: .right) } class InstructionsBannerViewIntegrationTests: XCTestCase { - private lazy var reverseDelegate = TextReversingDelegate() private lazy var silentDelegate = DefaultBehaviorDelegate() @@ -35,7 +32,7 @@ class InstructionsBannerViewIntegrationTests: XCTestCase { }() lazy var instructions: [VisualInstructionComponent] = { - let components = [ + let components = [ VisualInstructionComponent(type: .image, text: "US 101", imageURL: ShieldImage.us101.url, abbreviation: nil, abbreviationPriority: 0), VisualInstructionComponent(type: .delimiter, text: "/", imageURL: nil, abbreviation: nil, abbreviationPriority: 0), VisualInstructionComponent(type: .image, text: "I 280", imageURL: ShieldImage.i280.url, abbreviation: nil, abbreviationPriority: 0) @@ -47,7 +44,7 @@ class InstructionsBannerViewIntegrationTests: XCTestCase { VisualInstructionComponent(type: .image, text: "ANK 1", imageURL: nil, abbreviation: nil, abbreviationPriority: NSNotFound), VisualInstructionComponent(type: .text, text: "Ankh-Morpork Highway 1", imageURL: nil, abbreviation: nil, abbreviationPriority: NSNotFound) ] - + lazy var typicalInstruction: VisualInstructionBanner = makeVisualInstruction(primaryInstruction: [VisualInstructionComponent(type: .text, text: "Main Street", imageURL: nil, abbreviation: "Main St", abbreviationPriority: 0)], secondaryInstruction: nil) private func resetImageCache() { @@ -83,7 +80,6 @@ class InstructionsBannerViewIntegrationTests: XCTestCase { view.update(for: typicalInstruction) XCTAssert(view.primaryLabel.attributedText?.string == "teertS niaM") - } func testCustomDelegateReturningNilTriggersDefaultBehavior() { @@ -93,10 +89,8 @@ class InstructionsBannerViewIntegrationTests: XCTestCase { view.update(for: typicalInstruction) XCTAssert(view.primaryLabel.attributedText?.string == "Main Street") - } - func testDelimiterIsShownWhenShieldsNotLoaded() { let view = instructionsView() @@ -156,7 +150,7 @@ class InstructionsBannerViewIntegrationTests: XCTestCase { //ensure that second callback fires wait(for: [secondExpectation], timeout: 1) - + //Slash should no longer be present XCTAssertNil(view.primaryLabel.text!.firstIndex(of: "/"), "Expected instruction text not to contain a slash: \(view.primaryLabel.text!)") } @@ -177,7 +171,6 @@ class InstructionsBannerViewIntegrationTests: XCTestCase { XCTAssert(type(of: attachment) == GenericShieldAttachment.self, "Unexpected Attachment type:" + String(describing: attachment)) } wait(for: [foundAttachment], timeout: 0) - } func testRouteShieldsAreGenericUntilTheyLoad() { @@ -318,7 +311,6 @@ class InstructionsBannerViewIntegrationTests: XCTestCase { XCTAssertNotNil(imageRepository.cachedImageForKey(component.cacheKey!)) } - } private class TextReversingDelegate: VisualInstructionDelegate { diff --git a/MapboxNavigationTests/InstructionsBannerViewSnapshotTests.swift b/MapboxNavigationTests/InstructionsBannerViewSnapshotTests.swift index 3f945e19534..24ff7b378e7 100644 --- a/MapboxNavigationTests/InstructionsBannerViewSnapshotTests.swift +++ b/MapboxNavigationTests/InstructionsBannerViewSnapshotTests.swift @@ -5,7 +5,6 @@ import MapboxDirections @testable import MapboxCoreNavigation class InstructionsBannerViewSnapshotTests: FBSnapshotTestCase { - let imageRepository: ImageRepository = ImageRepository.shared let asyncTimeout: TimeInterval = 2.0 @@ -301,7 +300,6 @@ class InstructionsBannerViewSnapshotTests: FBSnapshotTestCase { } extension InstructionsBannerViewSnapshotTests { - // UIAppearance proxy do not work in unit test environment so we have to style manually func styleInstructionsView(_ view: InstructionsBannerView) { view.backgroundColor = .white diff --git a/MapboxNavigationTests/InstructionsCardCollectionTests.swift b/MapboxNavigationTests/InstructionsCardCollectionTests.swift index 54ba61c96bc..4da6de351c9 100644 --- a/MapboxNavigationTests/InstructionsCardCollectionTests.swift +++ b/MapboxNavigationTests/InstructionsCardCollectionTests.swift @@ -6,7 +6,6 @@ import MapboxDirections /// :nodoc: class InstructionsCardCollectionTests: XCTestCase { - lazy var initialRoute: Route = { return Fixture.route(from: jsonFileName) }() @@ -55,6 +54,7 @@ class InstructionsCardCollectionTests: XCTestCase { /// Simulation: Scroll to the next card step instructions. let simulatedTargetContentOffset = UnsafeMutablePointer.allocate(capacity: 1) simulatedTargetContentOffset.pointee = CGPoint(x: 0, y: 50) + subject.scrollViewWillBeginDragging(subject.instructionCollectionView) subject.scrollViewWillEndDragging(subject.instructionCollectionView, withVelocity: CGPoint(x: 2.0, y: 0.0), targetContentOffset: simulatedTargetContentOffset) /// Validation: Preview step instructions should be equal to next card step instructions @@ -69,27 +69,28 @@ class InstructionsCardCollectionTests: XCTestCase { let routeProgress = instructionsCardCollectionDataSource.progress let service = instructionsCardCollectionDataSource.service let instructionsCardCollectionSpy = instructionsCardCollectionDataSource.delegate - + let intersectionLocation = routeProgress.route.legs.first!.steps.first!.intersections!.first!.location let fakeLocation = CLLocation(latitude: intersectionLocation.latitude, longitude: intersectionLocation.longitude) subject.navigationService(service, didUpdate: routeProgress, with: fakeLocation, rawLocation: fakeLocation) - + let activeCard = (subject.instructionCollectionView.dataSource!.collectionView(subject.instructionCollectionView, cellForItemAt: IndexPath(row: 0, section: 0)) as! InstructionsCardCell).container!.instructionsCardView XCTAssertEqual(activeCard.step!.instructions, "Head north on 6th Avenue") - + let nextCard = (subject.instructionCollectionView.dataSource!.collectionView(subject.instructionCollectionView, cellForItemAt: IndexPath(row: 1, section: 0)) as! InstructionsCardCell).container!.instructionsCardView XCTAssertEqual(nextCard.step!.instructions, "Turn right onto Lincoln Way") - + /// Simulation: Scroll to the previous card step instructions. let simulatedTargetContentOffset = UnsafeMutablePointer.allocate(capacity: 1) simulatedTargetContentOffset.pointee = CGPoint(x: 0, y: 50) + subject.scrollViewWillBeginDragging(subject.instructionCollectionView) subject.scrollViewWillEndDragging(subject.instructionCollectionView, withVelocity: CGPoint(x: 2.0, y: 0.0), targetContentOffset: simulatedTargetContentOffset) - + /// Validation: Preview step instructions should be equal to next card step instructions XCTAssertTrue(subject.isInPreview) var previewStep = instructionsCardCollectionSpy.step XCTAssertEqual(previewStep!.instructions, "Turn right onto Lincoln Way") - + /// Validation: Preview step instructions should be equal to first card step instructions subject.scrollViewWillBeginDragging(subject.instructionCollectionView) subject.scrollViewWillEndDragging(subject.instructionCollectionView, withVelocity: CGPoint(x: -2.0, y: 0.0), targetContentOffset: simulatedTargetContentOffset) @@ -102,19 +103,20 @@ class InstructionsCardCollectionTests: XCTestCase { let routeProgress = instructionsCardCollectionDataSource.progress let service = instructionsCardCollectionDataSource.service let instructionsCardCollectionSpy = instructionsCardCollectionDataSource.delegate - + let intersectionLocation = routeProgress.route.legs.first!.steps.first!.intersections!.first!.location let fakeLocation = CLLocation(latitude: intersectionLocation.latitude, longitude: intersectionLocation.longitude) subject.navigationService(service, didUpdate: routeProgress, with: fakeLocation, rawLocation: fakeLocation) - + let activeCard = (subject.instructionCollectionView.dataSource!.collectionView(subject.instructionCollectionView, cellForItemAt: IndexPath(row: 0, section: 0)) as! InstructionsCardCell).container!.instructionsCardView XCTAssertEqual(activeCard.step!.instructions, "Head north on 6th Avenue") - + /// Simulation: Attempt to scroll to the next card step instructions. let simulatedTargetContentOffset = UnsafeMutablePointer.allocate(capacity: 1) simulatedTargetContentOffset.pointee = CGPoint(x: 0, y: 50) + subject.scrollViewWillBeginDragging(subject.instructionCollectionView) subject.scrollViewWillEndDragging(subject.instructionCollectionView, withVelocity: CGPoint(x: 0.0, y: 0.0), targetContentOffset: simulatedTargetContentOffset) - + XCTAssertTrue(subject.isInPreview) XCTAssertNotNil(instructionsCardCollectionSpy.step) } @@ -149,7 +151,6 @@ class InstructionsCardCollectionTests: XCTestCase { /// :nodoc: class InstructionsCardCollectionDelegateSpy: NSObject, InstructionsCardCollectionDelegate { - var step: RouteStep? = nil func instructionsCardCollection(_ instructionsCardCollection: InstructionsCardViewController, didPreview step: RouteStep) { @@ -173,15 +174,15 @@ class TestInstructionsCardStyle: InstructionsCardStyle { var secondaryLabelTextColor: UIColor = .darkGray var secondaryLabelHighlightedTextColor: UIColor = .gray lazy var distanceLabelNormalFont: UIFont = { - return UIFont.systemFont(ofSize: 16.0) + return UIFont.systemFont(ofSize: 16.0) }() var distanceLabelValueTextColor: UIColor = .yellow var distanceLabelUnitTextColor: UIColor = .orange lazy var distanceLabelUnitFont: UIFont = { - return UIFont.systemFont(ofSize: 20.0) + return UIFont.systemFont(ofSize: 20.0) }() lazy var distanceLabelValueFont: UIFont = { - return UIFont.systemFont(ofSize: 12.0) + return UIFont.systemFont(ofSize: 12.0) }() var distanceLabelHighlightedTextColor: UIColor = .red var maneuverViewPrimaryColor: UIColor = .blue diff --git a/MapboxNavigationTests/LaneTests.swift b/MapboxNavigationTests/LaneTests.swift index 1c18e83cdf8..89208ad7073 100644 --- a/MapboxNavigationTests/LaneTests.swift +++ b/MapboxNavigationTests/LaneTests.swift @@ -5,9 +5,7 @@ import MapboxDirections @testable import MapboxNavigation @testable import MapboxCoreNavigation - class LaneTests: FBSnapshotTestCase { - override func setUp() { super.setUp() recordMode = false @@ -23,12 +21,10 @@ class LaneTests: FBSnapshotTestCase { } func verifyAllLanes(size: CGSize) { - let leftHandLanes = TestableLane.testableLanes(drivingSide: .left) let rightHandLanes = TestableLane.testableLanes(drivingSide: .right) func addLanes(lanes: [TestableLane], stackView: UIStackView) { - let containerView = UIStackView(orientation: .vertical, spacing: 5, autoLayout: true) for lane in lanes { @@ -93,7 +89,6 @@ struct TestableLane { } extension UIStackView { - func setBackgroundColor(_ color: UIColor) { let subview = UIView(frame: bounds) subview.backgroundColor = color diff --git a/MapboxNavigationTests/LeakTest.swift b/MapboxNavigationTests/LeakTest.swift index a285e3f88d7..3b65ae6067d 100644 --- a/MapboxNavigationTests/LeakTest.swift +++ b/MapboxNavigationTests/LeakTest.swift @@ -15,7 +15,6 @@ public struct LeakTest { weak var leaked : AnyObject? = nil autoreleasepool { - var evaluated : AnyObject? = self.constructor() if let vc = evaluated as? UIViewController{ @@ -36,7 +35,6 @@ public struct LeakTest { var actionResult : Any? = nil autoreleasepool { - var evaluated : P? = self.constructor() as? P if evaluated == nil { @@ -63,9 +61,7 @@ public struct LeakTest { } public func leak() -> Predicate { - return Predicate.simple("leak") { expression in - guard let leakTest = try expression.evaluate() else{ return PredicateStatus.fail } @@ -75,9 +71,7 @@ public func leak() -> Predicate { } public func leakWhen

(_ action : @escaping (P) -> Any) -> Predicate where P:AnyObject { - return Predicate.simple("leak when") { expression in - guard let leakTest = try expression.evaluate() else{ return PredicateStatus.fail } diff --git a/MapboxNavigationTests/LeaksSpec.swift b/MapboxNavigationTests/LeaksSpec.swift index a0e3542d880..afc6e887c99 100644 --- a/MapboxNavigationTests/LeaksSpec.swift +++ b/MapboxNavigationTests/LeaksSpec.swift @@ -7,7 +7,6 @@ import MapboxDirections @testable import MapboxNavigation class LeaksSpec: QuickSpec { - lazy var initialRoute: Route = { let jsonRoute = (response["routes"] as! [AnyObject]).first as! [String: Any] let waypoint1 = Waypoint(coordinate: CLLocationCoordinate2D(latitude: 37.795042, longitude: -122.413165)) @@ -23,13 +22,11 @@ class LeaksSpec: QuickSpec { override func spec() { describe("RouteVoiceController") { - let voiceController = LeakTest { return RouteVoiceController(navigationService: self.dummySvc) } let resumeNotifications: (RouteVoiceController) -> () = { controller in - controller.observeNotifications(by: self.dummySvc) } diff --git a/MapboxNavigationTests/ManeuverArrowTests.swift b/MapboxNavigationTests/ManeuverArrowTests.swift index c1d0e1fc642..dd2fd4c7f11 100644 --- a/MapboxNavigationTests/ManeuverArrowTests.swift +++ b/MapboxNavigationTests/ManeuverArrowTests.swift @@ -6,14 +6,12 @@ import TestHelper @testable import MapboxCoreNavigation class ManeuverArrowTests: FBSnapshotTestCase { - let waypointRoute = Fixture.route(from: "waypoint-after-turn") override func setUp() { super.setUp() recordMode = false } - func testManeuverArrowHandlesWaypointsCorrectly() { let plotter = NavigationPlotter(frame: CGRect(origin: .zero, size: CGSize(width: 1000, height: 1000))) diff --git a/MapboxNavigationTests/ManeuverViewTests.swift b/MapboxNavigationTests/ManeuverViewTests.swift index a0f8dc46aaf..8cc1bff19f1 100644 --- a/MapboxNavigationTests/ManeuverViewTests.swift +++ b/MapboxNavigationTests/ManeuverViewTests.swift @@ -4,9 +4,7 @@ import MapboxDirections @testable import MapboxNavigation @testable import MapboxCoreNavigation - class ManeuverViewTests: FBSnapshotTestCase { - let maneuverView = ManeuverView(frame: CGRect(origin: .zero, size: CGSize(width: 50, height: 50))) override func setUp() { @@ -98,7 +96,6 @@ class ManeuverViewTests: FBSnapshotTestCase { } extension UIImage { - convenience init?(view: UIView) { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0) defer { UIGraphicsEndImageContext() } diff --git a/MapboxNavigationTests/MapboxVoiceControllerTests.swift b/MapboxNavigationTests/MapboxVoiceControllerTests.swift index ecab054db0c..e343031c6ba 100644 --- a/MapboxNavigationTests/MapboxVoiceControllerTests.swift +++ b/MapboxNavigationTests/MapboxVoiceControllerTests.swift @@ -6,9 +6,7 @@ import AVKit @testable import TestHelper @testable import MapboxNavigation - class MapboxVoiceControllerTests: XCTestCase { - var speechAPISpy: SpeechAPISpy! var route: Route { @@ -73,7 +71,6 @@ class MapboxVoiceControllerTests: XCTestCase { let instruction = routeProgress.currentLegProgress.currentStepProgress.currentSpokenInstruction - let handler: XCTNSNotificationExpectation.Handler = { note in return true } @@ -86,7 +83,6 @@ class MapboxVoiceControllerTests: XCTestCase { let firstCall = speechAPISpy.audioDataCalls.first! firstCall.fulfill() - wait(for: [play, prepare], timeout: 4) } diff --git a/MapboxNavigationTests/NavigationMapViewTests.swift b/MapboxNavigationTests/NavigationMapViewTests.swift index df153c4a468..4a259926bc3 100644 --- a/MapboxNavigationTests/NavigationMapViewTests.swift +++ b/MapboxNavigationTests/NavigationMapViewTests.swift @@ -4,9 +4,7 @@ import TestHelper @testable import MapboxNavigation @testable import MapboxCoreNavigation - class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { - let response = Fixture.JSONFromFileNamed(name: "route-with-instructions") var styleLoadingExpectation: XCTestExpectation? var mapView: NavigationMapView? @@ -67,14 +65,13 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { } func testNavigationMapViewCombineWithDissimilarCongestions() { - let congestionSegmentsSevere = mapView!.combine(coordinates, with: [ .low, .low, .severe, .low, .low - ]) + ]) // The severe breaks the trend of .low. // Any time the current congestion level is different than the previous segment, we have to create a new congestion segment. @@ -95,7 +92,7 @@ class NavigationMapViewTests: XCTestCase, MGLMapViewDelegate { mapView!.addAnnotation(PersistentAnnotation()) XCTAssertEqual(mapView!.annotations!.count, 2) - mapView!.showWaypoints(route) + mapView!.showWaypoints(on: route) XCTAssertEqual(mapView!.annotations!.count, 3) mapView!.removeWaypoints() diff --git a/MapboxNavigationTests/NavigationViewControllerTests.swift b/MapboxNavigationTests/NavigationViewControllerTests.swift index a604b3260bb..0b4706e29d1 100644 --- a/MapboxNavigationTests/NavigationViewControllerTests.swift +++ b/MapboxNavigationTests/NavigationViewControllerTests.swift @@ -10,12 +10,10 @@ let response = Fixture.JSONFromFileNamed(name: jsonFileName) let otherResponse = Fixture.JSONFromFileNamed(name: "route-for-lane-testing") class NavigationViewControllerTests: XCTestCase { - var customRoadName = [CLLocationCoordinate2D: String?]() var updatedStyleNumberOfTimes = 0 lazy var dependencies: (navigationViewController: NavigationViewController, navigationService: NavigationService, startLocation: CLLocation, poi: [CLLocation], endLocation: CLLocation, voice: RouteVoiceController) = { - let fakeDirections = DirectionsSpy(accessToken: "garbage", host: nil) let fakeService = MapboxNavigationService(route: initialRoute, directions: fakeDirections, locationSource: NavigationLocationManagerStub(), simulating: .never) let fakeVoice: RouteVoiceController = RouteVoiceControllerStub(navigationService: fakeService) @@ -60,7 +58,6 @@ class NavigationViewControllerTests: XCTestCase { // Brief: navigationViewController(_:roadNameAt:) delegate method is implemented, // with a road name provided and wayNameView label is visible. func testNavigationViewControllerDelegateRoadNameAtLocationImplemented() { - let navigationViewController = dependencies.navigationViewController let service = dependencies.navigationService @@ -154,7 +151,6 @@ class NavigationViewControllerTests: XCTestCase { // Brief: navigationViewController(_:roadNameAt:) delegate method is implemented, // with a blank road name (empty string) provided and wayNameView label is hidden. func testNavigationViewControllerDelegateRoadNameAtLocationEmptyString() { - let navigationViewController = dependencies.navigationViewController let service = dependencies.navigationService @@ -175,7 +171,6 @@ class NavigationViewControllerTests: XCTestCase { } func testNavigationViewControllerDelegateRoadNameAtLocationUmimplemented() { - let navigationViewController = dependencies.navigationViewController UIApplication.shared.delegate!.window!!.addSubview(navigationViewController.view) @@ -183,7 +178,6 @@ class NavigationViewControllerTests: XCTestCase { // Identify a location without a custom road name. let fultonStreetLocation = dependencies.poi[2] - navigationViewController.mapViewController!.labelRoadNameCompletionHandler = { (defaultRoadNameAssigned) in XCTAssertTrue(defaultRoadNameAssigned, "label road name was not successfully set") @@ -211,7 +205,7 @@ class NavigationViewControllerTests: XCTestCase { let firstDestination = initialRoute.routeOptions.waypoints.last!.coordinate let destinations = annotations.filter(annotationFilter(matching: firstDestination)) XCTAssert(!destinations.isEmpty, "Destination annotation does not exist on map") - + //lets set the second route navigationViewController.route = newRoute @@ -221,7 +215,6 @@ class NavigationViewControllerTests: XCTestCase { //do we have a destination on the second route? let newDestinations = newAnnotations.filter(annotationFilter(matching: secondDestination)) XCTAssert(!newDestinations.isEmpty, "New destination annotation does not exist on map") - } func testBlankBanner() { diff --git a/MapboxNavigationTests/RouteControllerSnapshotTests.swift b/MapboxNavigationTests/RouteControllerSnapshotTests.swift index 412aa70dcfc..826c997ba79 100644 --- a/MapboxNavigationTests/RouteControllerSnapshotTests.swift +++ b/MapboxNavigationTests/RouteControllerSnapshotTests.swift @@ -6,9 +6,7 @@ import TestHelper @testable import MapboxCoreNavigation @testable import MapboxNavigation - class RouteControllerSnapshotTests: FBSnapshotTestCase { - var replayManager: ReplayLocationManager? override func setUp() { diff --git a/MapboxNavigationTests/RouteTests.swift b/MapboxNavigationTests/RouteTests.swift index d6194101285..d5f9bbc2bc1 100644 --- a/MapboxNavigationTests/RouteTests.swift +++ b/MapboxNavigationTests/RouteTests.swift @@ -4,7 +4,6 @@ import TestHelper import Turf @testable import MapboxNavigation - class RouteTests: XCTestCase { func testPolylineAroundManeuver() { // Convert the match from https://github.com/mapbox/navigation-ios-examples/pull/28 into a route. diff --git a/MapboxNavigationTests/SimulatedLocationManagerTests.swift b/MapboxNavigationTests/SimulatedLocationManagerTests.swift index 41539ea5388..0468ef6b8c6 100644 --- a/MapboxNavigationTests/SimulatedLocationManagerTests.swift +++ b/MapboxNavigationTests/SimulatedLocationManagerTests.swift @@ -5,9 +5,7 @@ import MapboxDirections @testable import MapboxCoreNavigation @testable import MapboxNavigation - class SimulatedLocationManagerTests: FBSnapshotTestCase { - override func setUp() { super.setUp() recordMode = false @@ -33,7 +31,6 @@ class SimulatedLocationManagerTests: FBSnapshotTestCase { verify(view) } - } class SimulatedLocationManagerSpy: NSObject, CLLocationManagerDelegate { diff --git a/MapboxNavigationTests/SnapshotTest+Mapbox.swift b/MapboxNavigationTests/SnapshotTest+Mapbox.swift index 4857a494e22..b1f436f41b7 100644 --- a/MapboxNavigationTests/SnapshotTest+Mapbox.swift +++ b/MapboxNavigationTests/SnapshotTest+Mapbox.swift @@ -5,8 +5,8 @@ extension SnapshotTest { enum Side { case top, bottom } + func constrain(_ child: UIView, to parent: UIView, side: Side = .top) { - let childSideAnchor = side == .top ? child.topAnchor : child.bottomAnchor let parentSideAnchor = side == .top ? parent.topAnchor : parent.bottomAnchor let constraints = [ diff --git a/MapboxNavigationTests/StepsViewControllerTests.swift b/MapboxNavigationTests/StepsViewControllerTests.swift index 04e469c1b3d..a48cb835bc5 100644 --- a/MapboxNavigationTests/StepsViewControllerTests.swift +++ b/MapboxNavigationTests/StepsViewControllerTests.swift @@ -4,16 +4,13 @@ import MapboxDirections @testable import MapboxCoreNavigation @testable import MapboxNavigation - class StepsViewControllerTests: XCTestCase { - struct Constants { static let jsonRoute = (response["routes"] as! [AnyObject]).first as! [String: Any] static let accessToken = "nonsense" } lazy var dependencies: (stepsViewController: StepsViewController, routeController: RouteController, firstLocation: CLLocation, lastLocation: CLLocation) = { - let bogusToken = "pk.feedCafeDeadBeefBadeBede" let directions = Directions(accessToken: bogusToken) let dataSource = RouteControllerDataSourceFake() @@ -40,7 +37,6 @@ class StepsViewControllerTests: XCTestCase { }() func testRebuildStepsInstructionsViewDataSource() { - let stepsViewController = dependencies.stepsViewController XCTAssertNotNil(stepsViewController.view, "StepsViewController not initiated properly") @@ -55,7 +51,6 @@ class StepsViewControllerTests: XCTestCase { /// NOTE: This test is disabled pending https://github.com/mapbox/mapbox-navigation-ios/issues/1468 func x_testUpdateCellPerformance() { - let stepsViewController = dependencies.stepsViewController // Test that Steps ViewController viewLoads @@ -72,17 +67,16 @@ class StepsViewControllerTests: XCTestCase { } } } - } extension StepsViewControllerTests { fileprivate func location(at coordinate: CLLocationCoordinate2D) -> CLLocation { - return CLLocation(coordinate: coordinate, - altitude: 5, + return CLLocation(coordinate: coordinate, + altitude: 5, horizontalAccuracy: 10, - verticalAccuracy: 5, - course: 20, - speed: 15, - timestamp: Date()) + verticalAccuracy: 5, + course: 20, + speed: 15, + timestamp: Date()) } } diff --git a/MapboxCoreNavigationTests/MD5Tests.swift b/MapboxNavigationTests/StringTests.swift similarity index 71% rename from MapboxCoreNavigationTests/MD5Tests.swift rename to MapboxNavigationTests/StringTests.swift index 20199e24e0a..7345af7eb4c 100644 --- a/MapboxCoreNavigationTests/MD5Tests.swift +++ b/MapboxNavigationTests/StringTests.swift @@ -1,60 +1,59 @@ import XCTest -@testable import MapboxCoreNavigation +@testable import MapboxNavigation -class Tests: XCTestCase { - +class StringTests: XCTestCase { func testMD5() { - XCTAssertEqual("hello".md5(), + XCTAssertEqual("hello".md5, "5d41402abc4b2a76b9719d911017c592") - XCTAssertEqual("world".md5(), + XCTAssertEqual("world".md5, "7d793037a0760186574b0282f2f435e7") - XCTAssertEqual("https://www.google.com".md5(), + XCTAssertEqual("https://www.google.com".md5, "8ffdefbdec956b595d257f0aaeefd623") - XCTAssertEqual("https://www.google.com/logos/doodles/2016/parents-day-in-korea-5757703554072576-hp2x.jpg".md5(), + XCTAssertEqual("https://www.google.com/logos/doodles/2016/parents-day-in-korea-5757703554072576-hp2x.jpg".md5, "0dfb10e8d2ae771b3b3ed4544139644e") - XCTAssertEqual("https://unsplash.it/600/300/?image=1".md5(), + XCTAssertEqual("https://unsplash.it/600/300/?image=1".md5, "d59e956ebb1be415970f04ec77f4c875") - XCTAssertEqual("".md5(), + XCTAssertEqual("".md5, "d41d8cd98f00b204e9800998ecf8427e") - XCTAssertEqual("ABCDEFGHIJKLMNOPQRSTWXYZ1234567890".md5(), + XCTAssertEqual("ABCDEFGHIJKLMNOPQRSTWXYZ1234567890".md5, "b8f4f38629ec4f4a23f5dcc6086f8035") - XCTAssertEqual("abcdefghijklmnopqrstwxyz1234567890".md5(), + XCTAssertEqual("abcdefghijklmnopqrstwxyz1234567890".md5, "b2e875f4d53ccf6cefb5cda3f86fc542") - XCTAssertEqual("0123456789".md5(), + XCTAssertEqual("0123456789".md5, "781e5e245d69b566979b86e28d23f2c7") - XCTAssertEqual("0".md5(), + XCTAssertEqual("0".md5, "cfcd208495d565ef66e7dff9f98764da") - XCTAssertEqual("https://twitter.com/_HairForceOne/status/745235759460810752".md5(), + XCTAssertEqual("https://twitter.com/_HairForceOne/status/745235759460810752".md5, "40c2bfa3d7bfc7a453013ecd54022255") - XCTAssertEqual("Det er et velkjent faktum at lesere distraheres av lesbart innhold på en side når man ser på dens layout. Poenget med å bruke Lorem Ipsum er at det har en mer eller mindre normal fordeling av bokstaver i ord, i motsetning til 'Innhold her, innhold her', og gir inntrykk av å være lesbar tekst. Mange webside- og sideombrekkingsprogrammer bruker nå Lorem Ipsum som sin standard for provisorisk tekst".md5(), + XCTAssertEqual("Det er et velkjent faktum at lesere distraheres av lesbart innhold på en side når man ser på dens layout. Poenget med å bruke Lorem Ipsum er at det har en mer eller mindre normal fordeling av bokstaver i ord, i motsetning til 'Innhold her, innhold her', og gir inntrykk av å være lesbar tekst. Mange webside- og sideombrekkingsprogrammer bruker nå Lorem Ipsum som sin standard for provisorisk tekst".md5, "6b2880bcc7554cf07e72db9c99bf3284") - XCTAssertEqual("\\".md5(), + XCTAssertEqual("\\".md5, "28d397e87306b8631f3ed80d858d35f0") - XCTAssertEqual("http://res.cloudinary.com/demo/image/upload/w_300,h_200,c_crop/sample.jpg".md5(), + XCTAssertEqual("http://res.cloudinary.com/demo/image/upload/w_300,h_200,c_crop/sample.jpg".md5, "6e30d9cc4c08be4eea49076328d4c1f0") - XCTAssertEqual("http://res.cloudinary.com/demo/image/upload/x_355,y_410,w_300,h_200,c_crop/brown_sheep.jpg".md5(), + XCTAssertEqual("http://res.cloudinary.com/demo/image/upload/x_355,y_410,w_300,h_200,c_crop/brown_sheep.jpg".md5, "019e9d72b5af84ef114868875c1597ed") - XCTAssertEqual("http://www.w3schools.com/tags/html_form_submit.asp?text=Hello+G%C3%BCnter".md5(), + XCTAssertEqual("http://www.w3schools.com/tags/html_form_submit.asp?text=Hello+G%C3%BCnter".md5, "c89a2146cd3df34ecda86b6e0709b3fd") - XCTAssertEqual("!%40%23%24%25%5E%26*()%2C.%3C%3E%5C'1234567890-%3D".md5(), + XCTAssertEqual("!%40%23%24%25%5E%26*()%2C.%3C%3E%5C'1234567890-%3D".md5, "09a1790760693160e74b9d6fcec7ef64") - XCTAssertEqual("🛡".md5(), + XCTAssertEqual("🛡".md5, "a11d9c95b5bcb5687f10bad109131f20") } func testMD5_Data() { let data = "https://www.google.com".data(using: String.Encoding.utf8) - XCTAssertEqual(String(data: data!, encoding: String.Encoding.utf8)!.md5(), + XCTAssertEqual(String(data: data!, encoding: String.Encoding.utf8)!.md5, "8ffdefbdec956b595d257f0aaeefd623") } func testNaughtyStrings() { - let path = Bundle(for: Tests.self).path(forResource: "md5_crazy_strings", ofType: "txt")! + let path = Bundle(for: StringTests.self).path(forResource: "md5_crazy_strings", ofType: "txt")! let content = try! String(contentsOfFile: path, encoding: .utf8) let lines = content.components(separatedBy: .newlines) lines.forEach { line in - let md5 = line.md5() + let md5 = line.md5 XCTAssertEqual(md5.count, 32) } } diff --git a/MapboxNavigationTests/StyleManagerTests.swift b/MapboxNavigationTests/StyleManagerTests.swift index 8d1029a7e1f..57bb7ad5fb5 100644 --- a/MapboxNavigationTests/StyleManagerTests.swift +++ b/MapboxNavigationTests/StyleManagerTests.swift @@ -9,7 +9,6 @@ struct Location { } class StyleManagerTests: XCTestCase { - var location = Location.london var styleManager: StyleManager! @@ -133,8 +132,8 @@ class StyleManagerTests: XCTestCase { let dayExpectation = expectation(forNotification: .styleManagerDidApplyStyle, object: nil) { (notification) -> Bool in let userInfo = notification.userInfo - let style = userInfo?[MBStyleManagerNotificationUserInfoKey.styleKey] as? Style - let styleManager = userInfo?[MBStyleManagerNotificationUserInfoKey.styleManagerKey] as? StyleManager + let style = userInfo?[StyleManagerNotificationUserInfoKey.styleKey] as? Style + let styleManager = userInfo?[StyleManagerNotificationUserInfoKey.styleManagerKey] as? StyleManager XCTAssertNotNil(style) XCTAssertNotNil(styleManager) return style?.styleType == StyleType.day @@ -142,8 +141,8 @@ class StyleManagerTests: XCTestCase { let nightExpectation = expectation(forNotification: .styleManagerDidApplyStyle, object: nil) { (notification) -> Bool in let userInfo = notification.userInfo - let style = userInfo?[MBStyleManagerNotificationUserInfoKey.styleKey] as? Style - let styleManager = userInfo?[MBStyleManagerNotificationUserInfoKey.styleManagerKey] as? StyleManager + let style = userInfo?[StyleManagerNotificationUserInfoKey.styleKey] as? Style + let styleManager = userInfo?[StyleManagerNotificationUserInfoKey.styleManagerKey] as? StyleManager XCTAssertNotNil(style) XCTAssertNotNil(styleManager) return style?.styleType == StyleType.night @@ -157,11 +156,10 @@ class StyleManagerTests: XCTestCase { } extension StyleManagerTests: StyleManagerDelegate { - @objc public func styleManagerDidRefreshAppearance(_ styleManager: StyleManager) { } - @objc(styleManager:didApplyStyle:) + public func styleManagerDidRefreshAppearance(_ styleManager: StyleManager) { } + public func styleManager(_ styleManager: StyleManager, didApply style: Style) { } - @objc(locationForStyleManager:) public func location(for styleManager: StyleManager) -> CLLocation? { return location } diff --git a/MapboxNavigationTests/Support/FBSnapshotTestCase.swift b/MapboxNavigationTests/Support/FBSnapshotTestCase.swift index 68ed3a589bb..9b86aabe144 100644 --- a/MapboxNavigationTests/Support/FBSnapshotTestCase.swift +++ b/MapboxNavigationTests/Support/FBSnapshotTestCase.swift @@ -4,7 +4,6 @@ import FBSnapshotTestCase fileprivate let suffix: NSOrderedSet = ["_64"] @nonobjc extension FBSnapshotTestCase { - func verify(_ view: UIView, overallTolerance: CGFloat = 0.05) { FBSnapshotVerifyView(view, suffixes: suffix, overallTolerance: overallTolerance) } diff --git a/MapboxNavigationTests/Support/ImageDownloadOperationSpy.swift b/MapboxNavigationTests/Support/ImageDownloadOperationSpy.swift index 7dda961db4b..324c207f1e2 100644 --- a/MapboxNavigationTests/Support/ImageDownloadOperationSpy.swift +++ b/MapboxNavigationTests/Support/ImageDownloadOperationSpy.swift @@ -5,7 +5,6 @@ import Foundation * This class can be used as a replacement for the `ImageDownloader`'s default download operation class, for spying on url download requests as well as returning canned responses ad hoc. */ class ImageDownloadOperationSpy: Operation, ImageDownload { - private static var operations: [URL: ImageDownloadOperationSpy] = [:] private(set) var request: URLRequest? diff --git a/MapboxNavigationTests/Support/ImageLoadingURLProtocolSpy.swift b/MapboxNavigationTests/Support/ImageLoadingURLProtocolSpy.swift index 42385567543..be99c82b2a9 100644 --- a/MapboxNavigationTests/Support/ImageLoadingURLProtocolSpy.swift +++ b/MapboxNavigationTests/Support/ImageLoadingURLProtocolSpy.swift @@ -5,7 +5,6 @@ import XCTest * This class stubs out the URL loading for any request url registered in `registerData(_, forURL:)` and records requests for a given URL for inspection. Note that unstubbed URLs will continue to load as normal. */ class ImageLoadingURLProtocolSpy: URLProtocol { - private static var responseData: [URL: Data] = [:] private static var activeRequests: [URL: URLRequest] = [:] private static var pastRequests: [URL: URLRequest] = [:] @@ -118,5 +117,4 @@ class ImageLoadingURLProtocolSpy: URLProtocol { class func resumeImageLoading() { ImageLoadingURLProtocolSpy.imageLoadingSemaphore.signal() } - } diff --git a/MapboxNavigationTests/Support/NavigationViewControllerTestDoubles.swift b/MapboxNavigationTests/Support/NavigationViewControllerTestDoubles.swift index 6aec431edd8..27daae94208 100644 --- a/MapboxNavigationTests/Support/NavigationViewControllerTestDoubles.swift +++ b/MapboxNavigationTests/Support/NavigationViewControllerTestDoubles.swift @@ -12,7 +12,6 @@ class TestableDayStyle: DayStyle { } class RouteVoiceControllerStub: RouteVoiceController { - override func speak(_ instruction: SpokenInstruction) { //no-op } @@ -23,7 +22,6 @@ class RouteVoiceControllerStub: RouteVoiceController { } class NavigationLocationManagerStub: NavigationLocationManager { - override func startUpdatingLocation() { return } diff --git a/README.md b/README.md index 0957544c5db..c4d3775d616 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Get up and running in a few minutes with our drop-in turn-by-turn navigation `Na ## Requirements -The Mapbox Navigation SDK and Core Navigation are compatible with applications written in Swift or Objective-C in Xcode 10.2 and above. The Mapbox Navigation and Mapbox Core Navigation frameworks run on iOS 10.0 and above. +The Mapbox Navigation SDK and Core Navigation are compatible with applications written in Swift 5 in Xcode 10.2 and above. The Mapbox Navigation and Mapbox Core Navigation frameworks run on iOS 10.0 and above. The Mapbox Navigation SDK is also available [for Android](https://github.com/mapbox/mapbox-navigation-android/). @@ -92,7 +92,7 @@ Consult the [API reference](https://docs.mapbox.com/ios/api/navigation/) for fur The [API reference](https://docs.mapbox.com/ios/api/navigation/) includes example code for accomplishing common tasks. You can run these examples as part of the [navigation-ios-examples](https://github.com/mapbox/navigation-ios-examples) project. -This repository also contains [Swift and Objective-C testbeds](https://github.com/mapbox/mapbox-navigation-ios/tree/master/Example) that exercise a variety of navigation SDK features: +This repository also contains [a testbed](https://github.com/mapbox/mapbox-navigation-ios/tree/master/Example) that exercises a variety of navigation SDK features: 1. Clone the repository or download the [.zip file](https://github.com/mapbox/mapbox-navigation-ios/archive/master.zip) 1. Run `carthage update --platform ios` to build just the iOS dependencies. @@ -109,7 +109,6 @@ You can customize the appearance in order to blend in with the rest of your app. ```swift class CustomStyle: DayStyle { - required init() { super.init() mapStyleURL = URL(string: "mapbox://styles/mapbox/satellite-streets-v9")! @@ -143,3 +142,4 @@ We welcome feedback and code contributions! Please see [CONTRIBUTING.md](./CONTR Mapbox Navigation SDK for iOS is released under the ISC License. See [LICENSE.md](https://github.com/mapbox/mapbox-navigation-ios/blob/master/LICENSE.md) for details. Mapbox Navigation SDK uses [Mapbox Navigator](https://github.com/mapbox/mapbox-navigation-ios/blob/master/Cartfile#L2), a private binary, as a dependency. The Mapbox Navigator binary may be used with a Mapbox account and under the [Mapbox TOS](https://www.mapbox.com/tos/). If you do not wish to use this binary, make sure you swap out this dependency in the [Cartfile](https://github.com/mapbox/mapbox-navigation-ios/blob/master/Cartfile#L2). Code in this repository is released under the [MIT license](./LICENSE.md). + diff --git a/TestHelper/CoreLocation.swift b/TestHelper/CoreLocation.swift index f12a0055a66..d104c97fb17 100644 --- a/TestHelper/CoreLocation.swift +++ b/TestHelper/CoreLocation.swift @@ -2,7 +2,6 @@ import Foundation import CoreLocation import Turf - enum DecodingError: Error { case missingData } @@ -20,7 +19,6 @@ enum DecodingError: Error { "timestamp": (Int or String as ISO8601) */ public struct Location: Codable { - enum CodingKeys: String, CodingKey { // coordinate can be represented as GeoJSON [lon, lat] case coordinate @@ -44,7 +42,6 @@ public struct Location: Codable { let speed: CLLocationSpeed let timestamp: Date - public init(_ location: CLLocation) { self.coordinate = location.coordinate self.altitude = location.altitude @@ -56,7 +53,6 @@ public struct Location: Codable { } public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) if let coordinate = try? container.decode(CLLocationCoordinate2D.self, forKey: .coordinate) { @@ -91,7 +87,6 @@ public struct Location: Codable { } public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(coordinate.latitude, forKey: .lat) @@ -106,7 +101,6 @@ public struct Location: Codable { } extension CLLocation { - public convenience init(_ location: Location) { self.init(coordinate: location.coordinate, altitude: location.altitude, horizontalAccuracy: location.horizontalAccuracy, verticalAccuracy: location.verticalAccuracy, course: location.course, speed: location.speed, timestamp: location.timestamp) } @@ -133,13 +127,12 @@ extension Date { } extension Array where Element == CLLocation { - // Shifts the [CLLocation]’s first location to now and offsets the remaining locations by one second after the prior. public func shiftedToPresent() -> [CLLocation] { return shifted(to: Date()) } - // Shifts the [CLLocation]’s first location to the given timestamp and offsets the remaining locations by one second after the prior. + // Shifts the [CLLocation]’s first location to the given timestamp and offsets the remaining locations by one second after the prior. public func shifted(to timestamp: Date) -> [CLLocation] { return enumerated().map { CLLocation(coordinate: $0.element.coordinate, altitude: $0.element.altitude, diff --git a/TestHelper/Date.swift b/TestHelper/Date.swift index d90706f1e6c..a4e0a92c722 100644 --- a/TestHelper/Date.swift +++ b/TestHelper/Date.swift @@ -1,8 +1,6 @@ import Foundation - extension Date { - public static func +(lhs: Date, rhs: Int) -> Date { return lhs.addingTimeInterval(TimeInterval(rhs)) } diff --git a/TestHelper/DirectionsSpy.swift b/TestHelper/DirectionsSpy.swift index ff97777cd95..4eab601222c 100644 --- a/TestHelper/DirectionsSpy.swift +++ b/TestHelper/DirectionsSpy.swift @@ -1,9 +1,7 @@ import Foundation import MapboxDirections -@objc(MBDirectionsSpy) public class DirectionsSpy: Directions { - public var lastCalculateOptionsCompletion: RouteCompletionHandler? override public func calculate(_ options: MatchOptions, completionHandler: @escaping Directions.MatchCompletionHandler) -> URLSessionDataTask { diff --git a/TestHelper/DummyLocationManager.swift b/TestHelper/DummyLocationManager.swift index 05074a818ae..8d6572231df 100644 --- a/TestHelper/DummyLocationManager.swift +++ b/TestHelper/DummyLocationManager.swift @@ -1,9 +1,7 @@ import CoreLocation import MapboxCoreNavigation - public class DummyLocationManager: NavigationLocationManager { - override public func startUpdatingLocation() { // Do nothing } diff --git a/TestHelper/Fixture.swift b/TestHelper/Fixture.swift index a3b844e2527..cade0ab9b40 100644 --- a/TestHelper/Fixture.swift +++ b/TestHelper/Fixture.swift @@ -3,8 +3,6 @@ import CoreLocation import MapboxDirections @testable import MapboxCoreNavigation - -@objc(MBFixture) public class Fixture: NSObject { public class func stringFromFileNamed(name: String) -> String { guard let path = Bundle(for: self).path(forResource: name, ofType: "json") else { @@ -19,7 +17,6 @@ public class Fixture: NSObject { } } - @objc public class func JSONFromFileNamed(name: String) -> [String: Any] { guard let path = Bundle(for: Fixture.self).path(forResource: name, ofType: "json") else { assert(false, "Fixture \(name) not found.") @@ -73,7 +70,6 @@ public class Fixture: NSObject { return locations.map { CLLocation($0) } } - @objc(routeFromJSONFileName:) public class func route(from jsonFile: String) -> Route { let response = JSONFromFileNamed(name: jsonFile) let waypoints = Fixture.waypoints(from: jsonFile) @@ -127,7 +123,6 @@ public class Fixture: NSObject { } public class func generateTrace(for route: Route, speedMultiplier: Double = 1) -> [CLLocation] { - let traceCollector = TraceCollector() let locationManager = SimulatedLocationManager(route: route) locationManager.delegate = traceCollector @@ -142,7 +137,6 @@ public class Fixture: NSObject { } class TraceCollector: NSObject, CLLocationManagerDelegate { - var locations = [CLLocation]() func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { diff --git a/TestHelper/MMEEventsManager+Spy.h b/TestHelper/MMEEventsManager+Spy.h index e2699ad7ef3..1b5c828ff5b 100644 --- a/TestHelper/MMEEventsManager+Spy.h +++ b/TestHelper/MMEEventsManager+Spy.h @@ -1,7 +1,6 @@ #import @import MapboxMobileEvents; - NS_ASSUME_NONNULL_BEGIN @interface MMEEventsManager (Spy) diff --git a/TestHelper/NavigationEventsManagerTestDoubles.swift b/TestHelper/NavigationEventsManagerTestDoubles.swift index c2367b644ef..0ff293ec7b6 100644 --- a/TestHelper/NavigationEventsManagerTestDoubles.swift +++ b/TestHelper/NavigationEventsManagerTestDoubles.swift @@ -4,7 +4,6 @@ import MapboxMobileEvents import MapboxDirections public class NavigationEventsManagerSpy: NavigationEventsManager { - var mobileEventsManagerSpy: MMEEventsManagerSpy! var _enqueuedEvents: [FakeTelemetryEvent] { @@ -22,7 +21,7 @@ public class NavigationEventsManagerSpy: NavigationEventsManager { super.init(dataSource: nil, accessToken: "fake token", mobileEventsManager: mobileEventsManagerSpy) } - @objc required convenience public init(dataSource source: EventsManagerDataSource?, accessToken possibleToken: String?, mobileEventsManager: MMEEventsManager) { + required convenience public init(dataSource source: EventsManagerDataSource?, accessToken possibleToken: String?, mobileEventsManager: MMEEventsManager) { self.init() } @@ -74,7 +73,6 @@ public class NavigationEventsManagerSpy: NavigationEventsManager { typealias FakeTelemetryEvent = (name: String, attributes: [String: Any]) class MMEEventsManagerSpy: MMEEventsManager { - var enqueuedEvents = [FakeTelemetryEvent]() var flushedEvents = [FakeTelemetryEvent]() diff --git a/TestHelper/NavigationPlotter.swift b/TestHelper/NavigationPlotter.swift index 48dda8846e4..01e792575e1 100644 --- a/TestHelper/NavigationPlotter.swift +++ b/TestHelper/NavigationPlotter.swift @@ -146,7 +146,6 @@ extension LinePlotter { } public class NavigationPlotter: UIView { - var mapView: NavigationMapView? var coordinateBounds: MGLCoordinateBounds? public var routePlotters: [RoutePlotter]? { didSet { setNeedsDisplay() } } @@ -286,7 +285,7 @@ extension UIView { ] let boundingRect = text.boundingRect(with: CGSize(width: textRect.width, height: CGFloat.infinity), - options: .usesLineFragmentOrigin, attributes: attributes, context: nil) + options: .usesLineFragmentOrigin, attributes: attributes, context: nil) context.saveGState() let rect = CGRect(x: point.x - boundingRect.midX + Constants.dotSize.width / 2, @@ -329,7 +328,6 @@ extension UIView { } extension Array where Element == CLLocationCoordinate2D { - fileprivate var bounds: MGLCoordinateBounds { var maximumLatitude: CLLocationDegrees = -80 var minimumLatitude: CLLocationDegrees = 80 diff --git a/TestHelper/NavigationServiceTestDoubles.swift b/TestHelper/NavigationServiceTestDoubles.swift index 74fb4d8d2f3..56f201b0b92 100644 --- a/TestHelper/NavigationServiceTestDoubles.swift +++ b/TestHelper/NavigationServiceTestDoubles.swift @@ -3,7 +3,6 @@ import MapboxCoreNavigation import MapboxDirections public class RouteControllerDataSourceFake: RouterDataSource { - let manager = NavigationLocationManager() public var location: CLLocation? { diff --git a/TestHelper/SpeechAPISpy.swift b/TestHelper/SpeechAPISpy.swift index c62390ed666..a0269b3a8e2 100644 --- a/TestHelper/SpeechAPISpy.swift +++ b/TestHelper/SpeechAPISpy.swift @@ -3,10 +3,8 @@ import MapboxSpeech import AVKit @testable import MapboxNavigation /** - * This class can be used as a substitute for SpeechSynthesizer under test, in order to verify whether expected calls were made. + This class can be used as a substitute for SpeechSynthesizer under test, in order to verify whether expected calls were made. */ - - public class SpeechAPISpy: SpeechSynthesizer { public struct AudioDataCall { public static let sound = NSDataAsset(name: "reroute-sound", bundle: .mapboxNavigation)! diff --git a/docs/cover.md b/docs/cover.md index 65997011857..49e081e231c 100644 --- a/docs/cover.md +++ b/docs/cover.md @@ -4,7 +4,7 @@ The Mapbox Navigation SDK gives you all the tools you need to add turn-by-turn navigation to your application. It takes just a few minutes to drop a full-fledged turn-by-turn navigation view controller into your application. Or use the Core Navigation framework directly to build something truly custom. -The Mapbox Navigation SDK and Core Navigation are compatible with applications written in Swift 4 or Objective-C in Xcode 9.0. The Mapbox Navigation and Mapbox Core Navigation frameworks run on iOS 9.0 and above. +The Mapbox Navigation SDK and Core Navigation are compatible with applications written in Swift 5 in Xcode 10.2. The Mapbox Navigation and Mapbox Core Navigation frameworks run on iOS 10.0 and above. ## Installation @@ -77,6 +77,6 @@ This SDK is divided into two frameworks: the Mapbox Navigation framework (`Mapbo ### Core Navigation -`MapboxNavigationService` is responsible for receiving user location updates and determining their relation to the route line. If you build a completely custom navigation UI, this is the class your code would interact with directly. The `NavigationServiceDelegate` protocol allows your application to react to location-related events as they occur. Corresponding `Notification`s from the `NavigationService`'s `RouteController` are also posted to the shared `NotificationCenter`. These notifications indicate the current state of the application in the form of a `RouteProgress` object. +`MapboxNavigationService` is responsible for receiving user location updates and determining their relation to the route line. If you build a completely custom navigation UI, this is the class your code would interact with directly. The `NavigationServiceDelegate` protocol allows your application to react to location-related events as they occur. Corresponding `Notification`s from the `NavigationService`'s `RouteController` are also posted to the shared `NotificationCenter`. These notifications indicate the current state of the application in the form of a `RouteProgress` object. For further details, consult the guides and examples included with this API reference. If you have any questions, please see [our help page](https://docs.mapbox.com/help/). We welcome your [bug reports, feature requests, and contributions](https://github.com/mapbox/mapbox-navigation-ios/blob/master/CONTRIBUTING.md).