diff --git a/.circleci/config.yml b/.circleci/config.yml index c19c2e35eb5..ddfe1de73cf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -98,7 +98,6 @@ jobs: steps: - run: find . -path '*.podspec' -exec perl -pi -e 's/.+\.social_media_url.+//' {} \; - run: pod lib lint MapboxCoreNavigation.podspec - - run: pod lib lint MapboxNavigation.podspec - *save-cache-podmaster - *save-cache-cocoapods @@ -175,19 +174,19 @@ workflows: workflow: jobs: - build-job: - name: "Xcode_10.3_iOS_12.2" - xcode: "10.3.0" - iOS: "12.2" + name: "Xcode_11.1_iOS_13.1" + xcode: "11.1.0" + iOS: "13.1" + device: "iPhone 8 Plus" - build-job: name: "Xcode_10.3_iOS_12.1" xcode: "10.3.0" iOS: "12.1" codecoverage: true - build-job: - name: "Xcode_10.1_iOS_9.3" + name: "Xcode_10.1_iOS_10.3.1" xcode: "10.1.0" - iOS: "9.3" - delete_private_deps: true + iOS: "10.3.1" test: false - pod-job: name: "Xcode_10.2_iOS_12.2_CP_install" diff --git a/CHANGELOG.md b/CHANGELOG.md index b4b959e13c6..1befd607d1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ # Changes to the Mapbox Navigation SDK for iOS ## master + +* This library now requires a minimum deployment target of iOS 10.0 or above. iOS 9._x_ is no longer supported. ([#2206](https://github.com/mapbox/mapbox-navigation-ios/pull/2206)) +* Lock screen notifications are presented more reliably and more closely resemble instruction banners. ([#2206](https://github.com/mapbox/mapbox-navigation-ios/pull/2206)) * Fixed an issue where manually incrementing `RouteProgress.legIndex` could lead to undefined behavior. ([#2229](https://github.com/mapbox/mapbox-navigation-ios/pull/2229)) +* `DistanceFormatter` now inherits directly from `Formatter` rather than `LengthFormatter`. ([#2206](https://github.com/mapbox/mapbox-navigation-ios/pull/2206)) +* Fixed an issue where `DistanceFormatter.attributedString(for:withDefaultAttributes:)` set `NSAttributedString.Key.quantity` to the original distance value rather than the potentially rounded value represented by the attributed string. ([#2206](https://github.com/mapbox/mapbox-navigation-ios/pull/2206)) ## v0.37.0 diff --git a/Example/ViewController.swift b/Example/ViewController.swift index 665c8e0dbdb..ed552e42054 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -139,11 +139,9 @@ class ViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - if #available(iOS 10.0, *) { - UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { _,_ in - DispatchQueue.main.async { - CLLocationManager().requestWhenInUseAuthorization() - } + UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { _, _ in + DispatchQueue.main.async { + CLLocationManager().requestWhenInUseAuthorization() } } } diff --git a/MapboxCoreNavigation.podspec b/MapboxCoreNavigation.podspec index e5dc4071469..d2d1757dc87 100644 --- a/MapboxCoreNavigation.podspec +++ b/MapboxCoreNavigation.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - s.ios.deployment_target = "9.0" + s.ios.deployment_target = "10.0" # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # diff --git a/MapboxCoreNavigation/DistanceFormatter.swift b/MapboxCoreNavigation/DistanceFormatter.swift index 43debd9c15c..95722c33103 100644 --- a/MapboxCoreNavigation/DistanceFormatter.swift +++ b/MapboxCoreNavigation/DistanceFormatter.swift @@ -1,248 +1,241 @@ import CoreLocation -extension CLLocationDistance { - - static let metersPerMile: CLLocationDistance = 1_609.344 - static let feetPerMeter: CLLocationDistance = 3.28084 - - // Returns the distance converted to miles - var miles: Double { - return self / .metersPerMile - } - - // Returns the distance converted to feet - var feet: Double { - return self * .feetPerMeter - } - - // Returns the distance converted to yards - var yards: Double { - return feet / 3 - } - - // Returns the distance converted to kilometers - var kilometers: Double { - return self / 1000 - } - - // Returns the distance in meters converted from miles - func inMiles() -> Double { - return self * .metersPerMile - } - - // Returns the distance in meters converted from yards - func inYards() -> Double { - return self * .feetPerMeter / 3 - } - - func converted(to unit: LengthFormatter.Unit) -> Double { - switch unit { - case .millimeter: - return self / 1_000 - case .centimeter: - return self / 100 - case .meter: - return self - case .kilometer: - return kilometers - case .inch: - return feet * 12 - case .foot: - return feet - case .yard: - return yards - case .mile: - return miles - } - } -} - struct RoundingTable { struct Threshold { - let maximumDistance: CLLocationDistance + let maximumDistance: Measurement let roundingIncrement: Double - let unit: LengthFormatter.Unit let maximumFractionDigits: Int - @available(iOS 10.0, *) func measurement(of distance: CLLocationDistance) -> Measurement { - let unitLength: UnitLength - switch unit { - case .millimeter: - unitLength = .millimeters - case .centimeter: - unitLength = .centimeters - case .meter: - unitLength = .meters - case .kilometer: - unitLength = .kilometers - case .inch: - unitLength = .inches - case .foot: - unitLength = .feet - case .yard: - unitLength = .yards - case .mile: - unitLength = .miles - } - var measurement = Measurement(value: distance, unit: .meters).converted(to: unitLength) + var measurement = Measurement(value: distance, unit: .meters).converted(to: maximumDistance.unit) measurement.value.round(roundingIncrement: roundingIncrement) measurement.value.round(precision: pow(10, Double(maximumFractionDigits))) return measurement } - - func localizedDistanceString(for distance: CLLocationDistance, using formatter: DistanceFormatter) -> String { - switch unit { - case .mile: - return formatter.string(fromValue: distance.miles, unit: unit) - case .foot: - return formatter.string(fromValue: distance.feet, unit: unit) - case .yard: - return formatter.string(fromValue: distance.yards, unit: unit) - case .kilometer: - return formatter.string(fromValue: distance.kilometers, unit: unit) - default: - return formatter.string(fromValue: distance, unit: unit) - } - } } let thresholds: [Threshold] + /** + Returns the most applicable threshold for the given distance, falling back to the last threshold. + */ func threshold(for distance: CLLocationDistance) -> Threshold { - for threshold in thresholds { - if distance < threshold.maximumDistance { - return threshold - } + return thresholds.first { + distance < $0.maximumDistance.distance + } ?? thresholds.last! + } + + static var metric: RoundingTable = RoundingTable(thresholds: [ + .init(maximumDistance: Measurement(value: 25, unit: .meters), roundingIncrement: 5, maximumFractionDigits: 0), + .init(maximumDistance: Measurement(value: 100, unit: .meters), roundingIncrement: 25, maximumFractionDigits: 0), + .init(maximumDistance: Measurement(value: 999, unit: .meters), roundingIncrement: 50, maximumFractionDigits: 0), + // The rounding increment is a small value rather than 0 because of floating-point imprecision that causes 0.5 to round down. + .init(maximumDistance: Measurement(value: 3, unit: .kilometers), roundingIncrement: 0.0001, maximumFractionDigits: 1), + .init(maximumDistance: Measurement(value: 5, unit: .kilometers), roundingIncrement: 0.0001, maximumFractionDigits: 0) + ]) + + static var uk: RoundingTable = RoundingTable(thresholds: [ + .init(maximumDistance: Measurement(value: 20, unit: .yards), roundingIncrement: 10, maximumFractionDigits: 0), + .init(maximumDistance: Measurement(value: 100, unit: .yards), roundingIncrement: 25, maximumFractionDigits: 0), + .init(maximumDistance: Measurement(value: 0.1, unit: .miles).converted(to: .yards), roundingIncrement: 50, maximumFractionDigits: 1), + .init(maximumDistance: Measurement(value: 3, unit: .miles), roundingIncrement: 0.1, maximumFractionDigits: 1), + .init(maximumDistance: Measurement(value: 5, unit: .miles), roundingIncrement: 0.0001, maximumFractionDigits: 0) + ]) + + static var us: RoundingTable = RoundingTable(thresholds: [ + .init(maximumDistance: Measurement(value: 0.1, unit: .miles).converted(to: .feet), roundingIncrement: 50, maximumFractionDigits: 0), + .init(maximumDistance: Measurement(value: 3, unit: .miles), roundingIncrement: 0.1, maximumFractionDigits: 1), + .init(maximumDistance: Measurement(value: 5, unit: .miles), roundingIncrement: 0.0001, maximumFractionDigits: 0) + ]) +} + +extension Measurement where UnitType == UnitLength { + /** + Initializes a measurement from the given distance. + + - parameter distance: The distance being measured. + */ + public init(distance: CLLocationDistance) { + self.init(value: distance, unit: .meters) + } + + /** + The distance in meters. + */ + public var distance: CLLocationDistance { + return converted(to: .meters).value + } + + /** + Returns a length measurement equivalent to the receiver but converted to the most appropriate unit based on the given locale and rounded based on the unit. + + - parameter locale: The locale that determines the chosen unit. + */ + public func localized(into locale: Locale = .nationalizedCurrent) -> Measurement { + let threshold: RoundingTable + if NavigationSettings.shared.usesMetric { + threshold = .metric + } else if locale.languageCode == "en" && locale.regionCode == "GB" { + threshold = .uk + } else { + threshold = .us } - return thresholds.last! + return threshold.threshold(for: distance).measurement(of: distance) } } extension NSAttributedString.Key { + /** + An `NSNumber` containing the numeric quantity represented by the localized substring. + */ public static let quantity = NSAttributedString.Key(rawValue: "MBQuantity") } -/// Provides appropriately formatted, localized descriptions of linear distances. -@objc(MBDistanceFormatter) -open class DistanceFormatter: LengthFormatter { - /// True to favor brevity over precision. - var approx: Bool - - let nonFractionalLengthFormatter = LengthFormatter() - - /// Indicates the most recently used unit - public private(set) var unit: LengthFormatter.Unit = .millimeter - - // Rounding tables for metric, imperial, and UK measurement systems. The last threshold is used as a default. - lazy var roundingTableMetric: RoundingTable = { - return RoundingTable(thresholds: [.init(maximumDistance: 25, roundingIncrement: 5, unit: .meter, maximumFractionDigits: 0), - .init(maximumDistance: 100, roundingIncrement: 25, unit: .meter, maximumFractionDigits: 0), - .init(maximumDistance: 999, roundingIncrement: 50, unit: .meter, maximumFractionDigits: 0), - .init(maximumDistance: 3_000, roundingIncrement: 0, unit: .kilometer, maximumFractionDigits: 1), - .init(maximumDistance: 5_000, roundingIncrement: 0, unit: .kilometer, maximumFractionDigits: 0)]) - }() +/** + A formatter that provides localized representations of distance units and measurements. + + This class is limited to `UnitLength` and its behavior is more specific to distances than `MeasurementFormatter`. By default, the class automatically localizes and rounds the measurement using `Measurement.localized(into:)` and `Locale.nationalizedCurrent`. Measurements can be formatted into either strings or attributed strings. + */ +open class DistanceFormatter: Formatter, NSSecureCoding { + public static var supportsSecureCoding = true - lazy var roundingTableUK: RoundingTable = { - return RoundingTable(thresholds: [.init(maximumDistance: 20.inYards(), roundingIncrement: 10, unit: .yard, maximumFractionDigits: 0), - .init(maximumDistance: 100.inYards(), roundingIncrement: 25, unit: .yard, maximumFractionDigits: 0), - .init(maximumDistance: 0.1.inMiles(), roundingIncrement: 50, unit: .yard, maximumFractionDigits: 1), - .init(maximumDistance: 3.inMiles(), roundingIncrement: 0.1, unit: .mile, maximumFractionDigits: 1), - .init(maximumDistance: 5.inMiles(), roundingIncrement: 0, unit: .mile, maximumFractionDigits: 0)]) - }() - - lazy var roundingTableImperial: RoundingTable = { - return RoundingTable(thresholds: [.init(maximumDistance: 0.1.inMiles(), roundingIncrement: 50, unit: .foot, maximumFractionDigits: 0), - .init(maximumDistance: 3.inMiles(), roundingIncrement: 0.1, unit: .mile, maximumFractionDigits: 1), - .init(maximumDistance: 5.inMiles(), roundingIncrement: 0, unit: .mile, maximumFractionDigits: 0)]) - }() + /** + Options for choosing and formatting the unit. + + - seealso: `MeasurementFormatter.unitOptions` + */ + open var unitOptions: MeasurementFormatter.UnitOptions { + get { + return measurementFormatter.unitOptions + } + set { + measurementFormatter.unitOptions = newValue + } + } /** - Intializes a new `DistanceFormatter`. + The unit style. - - parameter approximate: approximates the distances. + - seealso: `MeasurementFormatter.unitStyle` */ - @objc public init(approximate: Bool = false) { - self.approx = approximate - super.init() - self.numberFormatter.locale = .nationalizedCurrent + open var unitStyle: Formatter.UnitStyle { + get { + return measurementFormatter.unitStyle + } + set { + measurementFormatter.unitStyle = newValue + } } - public required init?(coder decoder: NSCoder) { - approx = decoder.decodeBool(forKey: "approximate") - super.init(coder: decoder) + /** + The locale that determines the chosen unit, name of the unit, and number formatting. + + - seealso: `MeasurementFormatter.locale` + */ + open var locale: Locale { + get { + return measurementFormatter.locale + } + set { + measurementFormatter.locale = newValue + } } - open override func encode(with aCoder: NSCoder) { - super.encode(with: aCoder) - aCoder.encode(approx, forKey: "approximate") + /** + The underlying measurement formatter. + */ + @NSCopying open var measurementFormatter = MeasurementFormatter() + + public override init() { + super.init() + unitOptions = .providedUnit + locale = .nationalizedCurrent } - func threshold(for distance: CLLocationDistance) -> RoundingTable.Threshold { - if NavigationSettings.shared.usesMetric { - return roundingTableMetric.threshold(for: distance) - } else if numberFormatter.locale.identifier == "en-GB" { - return roundingTableUK.threshold(for: distance) - } else { - return roundingTableImperial.threshold(for: distance) - } + public required init?(coder decoder: NSCoder) { + super.init(coder: decoder) } /** - Returns a more human readable `String` from a given `CLLocationDistance`. + Creates and returns a localized, formatted string representation of the given distance in meters. - The user’s `Locale` is used here to set the units. - */ - @objc public func string(from distance: CLLocationDistance) -> String { - numberFormatter.positivePrefix = "" - numberFormatter.positiveSuffix = "" - numberFormatter.decimalSeparator = nonFractionalLengthFormatter.numberFormatter.decimalSeparator - numberFormatter.alwaysShowsDecimalSeparator = nonFractionalLengthFormatter.numberFormatter.alwaysShowsDecimalSeparator - numberFormatter.usesSignificantDigits = false - return formattedDistance(distance) - } - - @objc open override func string(fromMeters numberInMeters: Double) -> String { - return self.string(from: numberInMeters) + The distance is converted from meters to the most appropriate unit based on the locale and quantity. + + - parameter distance: The distance, measured in meters, to localize and format. + - returns: A localized, formatted representation of the distance. + - seealso: `MeasurementFormatter.string(from:)` + */ + open func string(from distance: CLLocationDistance) -> String { + return string(from: Measurement(distance: distance)) } - func formattedDistance(_ distance: CLLocationDistance) -> String { - let threshold = self.threshold(for: distance) - numberFormatter.maximumFractionDigits = threshold.maximumFractionDigits - numberFormatter.roundingIncrement = threshold.roundingIncrement as NSNumber - unit = threshold.unit - return threshold.localizedDistanceString(for: distance, using: self) + /** + Creates and returns a localized, formatted attributed string representation of the given distance in meters. + + The distance is converted from meters to the most appropriate unit based on the locale and quantity. `NSAttributedString.Key.quantity` is applied to the range representing the quantity. For example, the “5” in “5 km” has a quantity attribute set to 5. + + - parameter distance: The distance, measured in meters, to localize and format. + - parameter defaultAttributes: The default attributes to apply to the resulting attributed string. + - returns: A localized, formatted representation of the distance. + */ + open func attributedString(from distance: CLLocationDistance, defaultAttributes attributes: [NSAttributedString.Key: Any]? = nil) -> NSAttributedString { + return attributedString(from: Measurement(distance: distance), defaultAttributes: attributes) } - @available(iOS 10.0, *) - @objc(measurementOfDistance:) - public func measurement(of distance: CLLocationDistance) -> Measurement { - let threshold = self.threshold(for: distance) - return threshold.measurement(of: distance) + /** + Creates and returns a localized, formatted string representation of the given measurement. + + - parameter measurement: The measurement to localize and format. + - returns: A localized, formatted representation of the measurement. + - seealso: `MeasurementFormatter.string(from:)` + */ + open func string(from measurement: Measurement) -> String { + return measurementFormatter.string(from: measurement.localized(into: locale)) } /** - Returns an attributed string containing the formatted, converted distance. + Creates and returns a localized, formatted attributed string representation of the given measurement. + + `NSAttributedString.Key.quantity` is applied to the range representing the quantity. For example, the “5” in “5 km” has a quantity attribute set to 5. - `NSAttributedStringKey.quantity` is applied to the numeric quantity. + - parameter measurement: The measurement to localize and format. + - parameter defaultAttributes: The default attributes to apply to the resulting attributed string. + - returns: A localized, formatted representation of the measurement. */ - @objc open override func attributedString(for obj: Any, withDefaultAttributes attrs: [NSAttributedString.Key : Any]? = nil) -> NSAttributedString? { - guard let distance = obj as? CLLocationDistance else { - return nil - } + open func attributedString(from measurement: Measurement, defaultAttributes attributes: [NSAttributedString.Key: Any]? = nil) -> NSAttributedString { + let string = self.string(from: measurement) + let localizedMeasurement = measurement.localized(into: locale) - let string = self.string(from: distance) - let attributedString = NSMutableAttributedString(string: string, attributes: attrs) - let convertedDistance = distance.converted(to: threshold(for: distance).unit) - if let quantityString = numberFormatter.string(from: convertedDistance as NSNumber) { + let attributedString = NSMutableAttributedString(string: string, attributes: attributes) + if let quantityString = measurementFormatter.numberFormatter.string(from: localizedMeasurement.value as NSNumber) { // NSMutableAttributedString methods accept NSRange, not Range. let quantityRange = (string as NSString).range(of: quantityString) if quantityRange.location != NSNotFound { - attributedString.addAttribute(.quantity, value: distance as NSNumber, range: quantityRange) + attributedString.addAttribute(.quantity, value: localizedMeasurement.value as NSNumber, range: quantityRange) } } return attributedString } + + open override func string(for obj: Any?) -> String? { + if let distanceFromObj = obj as? CLLocationDistance { + return self.string(from: distanceFromObj) + } else if let measurementFromObj = obj as? Measurement { + return self.string(from: measurementFromObj) + } else { + return nil + } + } + + open override func attributedString(for obj: Any, withDefaultAttributes attrs: [NSAttributedString.Key : Any]? = nil) -> NSAttributedString? { + if let distanceFromObj = obj as? CLLocationDistance { + return self.attributedString(from: distanceFromObj, defaultAttributes: attrs) + } else if let measurementFromObj = obj as? Measurement { + return self.attributedString(from: measurementFromObj, defaultAttributes: attrs) + } else { + return nil + } + } } extension Double { diff --git a/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj b/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj index edd6b5a54c6..e21a8628e1d 100644 --- a/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj +++ b/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj @@ -231,8 +231,8 @@ "${PODS_ROOT}/Target Support Files/Pods-PodInstall/Pods-PodInstall-frameworks.sh", "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/Mapbox.framework", "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/Mapbox.framework.dSYM", - "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/3C37B131-DD4F-3BB9-A83C-83EE7CC444A6.bcsymbolmap", - "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/06669838-82B0-3595-976F-2071C1C51CA1.bcsymbolmap", + "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/F1CD9014-347B-3A12-985D-D2CF4457B073.bcsymbolmap", + "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/C3BF5D38-E18B-392C-BFDF-5551F3CC528B.bcsymbolmap", "${BUILT_PRODUCTS_DIR}/MapboxCoreNavigation/MapboxCoreNavigation.framework", "${BUILT_PRODUCTS_DIR}/MapboxDirections.swift/MapboxDirections.framework", "${BUILT_PRODUCTS_DIR}/MapboxMobileEvents/MapboxMobileEvents.framework", @@ -248,8 +248,8 @@ outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mapbox.framework", "${DWARF_DSYM_FOLDER_PATH}/Mapbox.framework.dSYM", - "${BUILT_PRODUCTS_DIR}/3C37B131-DD4F-3BB9-A83C-83EE7CC444A6.bcsymbolmap", - "${BUILT_PRODUCTS_DIR}/06669838-82B0-3595-976F-2071C1C51CA1.bcsymbolmap", + "${BUILT_PRODUCTS_DIR}/F1CD9014-347B-3A12-985D-D2CF4457B073.bcsymbolmap", + "${BUILT_PRODUCTS_DIR}/C3BF5D38-E18B-392C-BFDF-5551F3CC528B.bcsymbolmap", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCoreNavigation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxDirections.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMobileEvents.framework", diff --git a/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile b/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile index 750139c2ca3..c2b58a2426c 100644 --- a/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile +++ b/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile @@ -1,4 +1,4 @@ -platform :ios, '9.0' +platform :ios, '10.0' use_frameworks! target 'PodInstall' do diff --git a/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile.lock b/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile.lock index 0fd7e37f3ec..05f6f34b414 100644 --- a/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile.lock +++ b/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - Mapbox-iOS-SDK (5.3.0) + - Mapbox-iOS-SDK (5.3.2) - MapboxCoreNavigation (0.37.0): - MapboxDirections.swift (~> 0.30.0) - MapboxMobileEvents (~> 0.9.5) @@ -41,17 +41,17 @@ EXTERNAL SOURCES: :path: "../../../" SPEC CHECKSUMS: - Mapbox-iOS-SDK: 0ac8ea3c3ec70545d70f83afa1f1358bc4a0b148 - MapboxCoreNavigation: d77f31673d8d00b56f37863322ea8c16e3b5492b + Mapbox-iOS-SDK: 23c20a5da344234cafba5d13669af3f8bde8beaa + MapboxCoreNavigation: 3fab73d620f8787d234fa268619457efe2e611ec MapboxDirections.swift: 1c6df988c24b753888ebd9976d7c98632501a413 MapboxMobileEvents: f6c21b2e59066c5c7093585de7c15adae3b63da0 - MapboxNavigation: f450745664b5373cd9f957f99ecc29f945d1ac30 + MapboxNavigation: 6b76631fc24d28380d187cba5d912f5167898a70 MapboxNavigationNative: 11dc22140f4698d3f26989f2b6379dc81ef0d4c1 MapboxSpeech: 59b3984d3f433a443d24acf53097f918c5cc70f9 Polyline: 0e9890790292741c8186201a536b6bb6a78d02dd Solar: 2dc6e7cc39186cb0c8228fa08df76fb50c7d8f24 Turf: c6bdf62d6a70c647874f295dd1cf4eefc0c3e9e6 -PODFILE CHECKSUM: 55ae5a7edbc3dedda5149ba3d58b992ab7327f95 +PODFILE CHECKSUM: d4084fe664fbacd0cffd88ab45eaed1ad907ad1d COCOAPODS: 1.7.5 diff --git a/MapboxCoreNavigationTests/DistanceFormatterTests.swift b/MapboxCoreNavigationTests/DistanceFormatterTests.swift index 6eb739a4b11..eeb7fd1e97c 100644 --- a/MapboxCoreNavigationTests/DistanceFormatterTests.swift +++ b/MapboxCoreNavigationTests/DistanceFormatterTests.swift @@ -2,29 +2,22 @@ import XCTest import CoreLocation @testable import MapboxCoreNavigation -let oneMile: CLLocationDistance = .metersPerMile -let oneYard: CLLocationDistance = 0.9144 -let oneFeet: CLLocationDistance = 0.3048 - - class DistanceFormatterTests: XCTestCase { - var distanceFormatter = DistanceFormatter(approximate: true) + var distanceFormatter = DistanceFormatter() override func setUp() { super.setUp() } - func assertDistance(_ distance: CLLocationDistance, displayed: String, quantity: String) { - let displayedString = distanceFormatter.string(from: distance) + func assertDistance(_ measurement: Measurement, displayed: String, quantity: String) { + let displayedString = distanceFormatter.string(from: measurement) XCTAssertEqual(displayedString, displayed, "Displayed: '\(displayedString)' should be equal to \(displayed)") - if #available(iOS 10.0, *) { - let value = distanceFormatter.measurement(of: distance).value - XCTAssertEqual(distanceFormatter.numberFormatter.string(from: value as NSNumber), quantity) - } + let value = measurement.localized(into: distanceFormatter.locale).value + XCTAssertEqual(distanceFormatter.measurementFormatter.numberFormatter.string(from: value as NSNumber), quantity) - let attributedString = distanceFormatter.attributedString(for: distance as NSNumber) + let attributedString = distanceFormatter.attributedString(for: measurement.distance as NSNumber) XCTAssertEqual(attributedString?.string, displayed, "Displayed: '\(attributedString?.string ?? "")' should be equal to \(displayed)") guard let checkedAttributedString = attributedString else { return @@ -38,7 +31,7 @@ class DistanceFormatterTests: XCTestCase { var effectiveQuantityRange = NSRange(location: NSNotFound, length: 0) let quantityAttrs = checkedAttributedString.attributes(at: checkedQuantityRange.lowerBound.encodedOffset, effectiveRange: &effectiveQuantityRange) - XCTAssertEqual(quantityAttrs[.quantity] as? NSNumber, distance as NSNumber, "'\(quantity)' should have quantity \(distance)") + XCTAssertEqual(quantityAttrs[.quantity] as? NSNumber, value as NSNumber, "'\(quantity)' should have quantity \(measurement.distance)") XCTAssertEqual(effectiveQuantityRange.length, quantity.count) guard checkedQuantityRange.upperBound.encodedOffset < checkedAttributedString.length else { @@ -50,121 +43,116 @@ class DistanceFormatterTests: XCTestCase { func testDistanceFormatters_US() { NavigationSettings.shared.distanceUnit = .mile - distanceFormatter.numberFormatter.locale = Locale(identifier: "en-US") + distanceFormatter.locale = Locale(identifier: "en-US") - assertDistance(0, displayed: "0 ft", quantity: "0") - assertDistance(oneFeet*50, displayed: "50 ft", quantity: "50") - assertDistance(oneFeet*100, displayed: "100 ft", quantity: "100") - assertDistance(oneFeet*249, displayed: "250 ft", quantity: "250") - assertDistance(oneFeet*305, displayed: "300 ft", quantity: "300") - assertDistance(oneMile*0.1, displayed: "0.1 mi", quantity: "0.1") - assertDistance(oneMile*0.24, displayed: "0.2 mi", quantity: "0.2") - assertDistance(oneMile*0.251, displayed: "0.3 mi", quantity: "0.3") - assertDistance(oneMile*0.75, displayed: "0.8 mi", quantity: "0.8") - assertDistance(oneMile, displayed: "1 mi", quantity: "1") - assertDistance(oneMile*2.5, displayed: "2.5 mi", quantity: "2.5") - assertDistance(oneMile*2.9, displayed: "2.9 mi", quantity: "2.9") - assertDistance(oneMile*3, displayed: "3 mi", quantity: "3") - assertDistance(oneMile*3.5, displayed: "4 mi", quantity: "4") - assertDistance(oneMile*5.4, displayed: "5 mi", quantity: "5") + assertDistance(Measurement(value: 0, unit: .feet), displayed: "0 ft", quantity: "0") + assertDistance(Measurement(value: 50, unit: .feet), displayed: "50 ft", quantity: "50") + assertDistance(Measurement(value: 100, unit: .feet), displayed: "100 ft", quantity: "100") + assertDistance(Measurement(value: 249, unit: .feet), displayed: "250 ft", quantity: "250") + assertDistance(Measurement(value: 305, unit: .feet), displayed: "300 ft", quantity: "300") + assertDistance(Measurement(value: 0.1, unit: .miles), displayed: "0.1 mi", quantity: "0.1") + assertDistance(Measurement(value: 0.24, unit: .miles), displayed: "0.2 mi", quantity: "0.2") + assertDistance(Measurement(value: 0.251, unit: .miles), displayed: "0.3 mi", quantity: "0.3") + assertDistance(Measurement(value: 0.75, unit: .miles), displayed: "0.8 mi", quantity: "0.8") + assertDistance(Measurement(value: 1, unit: .miles), displayed: "1 mi", quantity: "1") + assertDistance(Measurement(value: 2.5, unit: .miles), displayed: "2.5 mi", quantity: "2.5") + assertDistance(Measurement(value: 2.9, unit: .miles), displayed: "2.9 mi", quantity: "2.9") + assertDistance(Measurement(value: 3, unit: .miles), displayed: "3 mi", quantity: "3") + assertDistance(Measurement(value: 3.5, unit: .miles), displayed: "4 mi", quantity: "4") + assertDistance(Measurement(value: 5.4, unit: .miles), displayed: "5 mi", quantity: "5") } func testDistanceFormatters_DE() { NavigationSettings.shared.distanceUnit = .kilometer - distanceFormatter.numberFormatter.locale = Locale(identifier: "de-DE") + distanceFormatter.locale = Locale(identifier: "de-DE") - assertDistance(0, displayed: "0 m", quantity: "0") - assertDistance(4, displayed: "5 m", quantity: "5") - assertDistance(11, displayed: "10 m", quantity: "10") - assertDistance(15, displayed: "15 m", quantity: "15") - assertDistance(24, displayed: "25 m", quantity: "25") - assertDistance(89, displayed: "100 m", quantity: "100") - assertDistance(226, displayed: "250 m", quantity: "250") - assertDistance(275, displayed: "300 m", quantity: "300") - assertDistance(500, displayed: "500 m", quantity: "500") - assertDistance(949, displayed: "950 m", quantity: "950") - assertDistance(951, displayed: "950 m", quantity: "950") - assertDistance(999, displayed: "1 km", quantity: "1") - assertDistance(1000, displayed: "1 km", quantity: "1") - assertDistance(1001, displayed: "1 km", quantity: "1") - assertDistance(2_500, displayed: "2.5 km", quantity: "2.5") - assertDistance(2_900, displayed: "2.9 km", quantity: "2.9") - assertDistance(3_000, displayed: "3 km", quantity: "3") - assertDistance(3_500, displayed: "4 km", quantity: "4") + assertDistance(Measurement(value: 0, unit: .meters), displayed: "0 m", quantity: "0") + assertDistance(Measurement(value: 4, unit: .meters), displayed: "5 m", quantity: "5") + assertDistance(Measurement(value: 11, unit: .meters), displayed: "10 m", quantity: "10") + assertDistance(Measurement(value: 15, unit: .meters), displayed: "15 m", quantity: "15") + assertDistance(Measurement(value: 24, unit: .meters), displayed: "25 m", quantity: "25") + assertDistance(Measurement(value: 89, unit: .meters), displayed: "100 m", quantity: "100") + assertDistance(Measurement(value: 226, unit: .meters), displayed: "250 m", quantity: "250") + assertDistance(Measurement(value: 275, unit: .meters), displayed: "300 m", quantity: "300") + assertDistance(Measurement(value: 500, unit: .meters), displayed: "500 m", quantity: "500") + assertDistance(Measurement(value: 949, unit: .meters), displayed: "950 m", quantity: "950") + assertDistance(Measurement(value: 951, unit: .meters), displayed: "950 m", quantity: "950") + assertDistance(Measurement(value: 999, unit: .meters), displayed: "1 km", quantity: "1") + assertDistance(Measurement(value: 1, unit: .kilometers), displayed: "1 km", quantity: "1") + assertDistance(Measurement(value: 1.001, unit: .kilometers), displayed: "1 km", quantity: "1") + assertDistance(Measurement(value: 2.5, unit: .kilometers), displayed: "2,5 km", quantity: "2,5") + assertDistance(Measurement(value: 2.9, unit: .kilometers), displayed: "2,9 km", quantity: "2,9") + assertDistance(Measurement(value: 3, unit: .kilometers), displayed: "3 km", quantity: "3") + assertDistance(Measurement(value: 3.5, unit: .kilometers), displayed: "4 km", quantity: "4") } func testDistanceFormatters_GB() { NavigationSettings.shared.distanceUnit = .mile - distanceFormatter.numberFormatter.locale = Locale(identifier: "en-GB") + distanceFormatter.locale = Locale(identifier: "en-GB") - assertDistance(0, displayed: "0 yd", quantity: "0") - assertDistance(oneYard*4, displayed: "0 yd", quantity: "0") - assertDistance(oneYard*5, displayed: "10 yd", quantity: "10") - assertDistance(oneYard*12, displayed: "10 yd", quantity: "10") - assertDistance(oneYard*24, displayed: "25 yd", quantity: "25") - assertDistance(oneYard*25, displayed: "25 yd", quantity: "25") - assertDistance(oneYard*38, displayed: "50 yd", quantity: "50") - assertDistance(oneYard*126, displayed: "150 yd", quantity: "150") - assertDistance(oneYard*150, displayed: "150 yd", quantity: "150") - assertDistance(oneYard*174, displayed: "150 yd", quantity: "150") - assertDistance(oneYard*175, displayed: "200 yd", quantity: "200") - assertDistance(oneMile/2, displayed: "0.5 mi", quantity: "0.5") - assertDistance(oneMile, displayed: "1 mi", quantity: "1") - assertDistance(oneMile*2.5, displayed: "2.5 mi", quantity: "2.5") - assertDistance(oneMile*3, displayed: "3 mi", quantity: "3") - assertDistance(oneMile*3.5, displayed: "4 mi", quantity: "4") + assertDistance(Measurement(value: 0, unit: .yards), displayed: "0 yd", quantity: "0") + assertDistance(Measurement(value: 4, unit: .yards), displayed: "0 yd", quantity: "0") + assertDistance(Measurement(value: 5, unit: .yards), displayed: "10 yd", quantity: "10") + assertDistance(Measurement(value: 12, unit: .yards), displayed: "10 yd", quantity: "10") + assertDistance(Measurement(value: 24, unit: .yards), displayed: "25 yd", quantity: "25") + assertDistance(Measurement(value: 25, unit: .yards), displayed: "25 yd", quantity: "25") + assertDistance(Measurement(value: 38, unit: .yards), displayed: "50 yd", quantity: "50") + assertDistance(Measurement(value: 126, unit: .yards), displayed: "150 yd", quantity: "150") + assertDistance(Measurement(value: 150, unit: .yards), displayed: "150 yd", quantity: "150") + assertDistance(Measurement(value: 174, unit: .yards), displayed: "150 yd", quantity: "150") + assertDistance(Measurement(value: 175, unit: .yards), displayed: "200 yd", quantity: "200") + assertDistance(Measurement(value: 0.5, unit: .miles), displayed: "0.5 mi", quantity: "0.5") + assertDistance(Measurement(value: 1, unit: .miles), displayed: "1 mi", quantity: "1") + assertDistance(Measurement(value: 2.5, unit: .miles), displayed: "2.5 mi", quantity: "2.5") + assertDistance(Measurement(value: 3, unit: .miles), displayed: "3 mi", quantity: "3") + assertDistance(Measurement(value: 3.5, unit: .miles), displayed: "4 mi", quantity: "4") } - + func testDistanceFormatters_he_IL() { NavigationSettings.shared.distanceUnit = .kilometer - distanceFormatter.numberFormatter.locale = Locale(identifier: "he-IL") + distanceFormatter.locale = Locale(identifier: "he-IL") - assertDistance(0, displayed: "0 מ׳", quantity: "0") - assertDistance(4, displayed: "5 מ׳", quantity: "5") - assertDistance(11, displayed: "10 מ׳", quantity: "10") - assertDistance(15, displayed: "15 מ׳", quantity: "15") - assertDistance(24, displayed: "25 מ׳", quantity: "25") - assertDistance(89, displayed: "100 מ׳", quantity: "100") - assertDistance(226, displayed: "250 מ׳", quantity: "250") - assertDistance(275, displayed: "300 מ׳", quantity: "300") - assertDistance(500, displayed: "500 מ׳", quantity: "500") - assertDistance(949, displayed: "950 מ׳", quantity: "950") - assertDistance(951, displayed: "950 מ׳", quantity: "950") - assertDistance(1000, displayed: "1 ק״מ", quantity: "1") - assertDistance(1001, displayed: "1 ק״מ", quantity: "1") - assertDistance(2_500, displayed: "2.5 ק״מ", quantity: "2.5") - assertDistance(2_900, displayed: "2.9 ק״מ", quantity: "2.9") - assertDistance(3_000, displayed: "3 ק״מ", quantity: "3") - assertDistance(3_500, displayed: "4 ק״מ", quantity: "4") + assertDistance(Measurement(value: 0, unit: .meters), displayed: "0 מ׳", quantity: "0") + assertDistance(Measurement(value: 4, unit: .meters), displayed: "5 מ׳", quantity: "5") + assertDistance(Measurement(value: 11, unit: .meters), displayed: "10 מ׳", quantity: "10") + assertDistance(Measurement(value: 15, unit: .meters), displayed: "15 מ׳", quantity: "15") + assertDistance(Measurement(value: 24, unit: .meters), displayed: "25 מ׳", quantity: "25") + assertDistance(Measurement(value: 89, unit: .meters), displayed: "100 מ׳", quantity: "100") + assertDistance(Measurement(value: 226, unit: .meters), displayed: "250 מ׳", quantity: "250") + assertDistance(Measurement(value: 275, unit: .meters), displayed: "300 מ׳", quantity: "300") + assertDistance(Measurement(value: 500, unit: .meters), displayed: "500 מ׳", quantity: "500") + assertDistance(Measurement(value: 949, unit: .meters), displayed: "950 מ׳", quantity: "950") + assertDistance(Measurement(value: 951, unit: .meters), displayed: "950 מ׳", quantity: "950") + assertDistance(Measurement(value: 1, unit: .kilometers), displayed: "1 ק״מ", quantity: "1") + assertDistance(Measurement(value: 1.001, unit: .kilometers), displayed: "1 ק״מ", quantity: "1") + assertDistance(Measurement(value: 2.5, unit: .kilometers), displayed: "2.5 ק״מ", quantity: "2.5") + assertDistance(Measurement(value: 2.9, unit: .kilometers), displayed: "2.9 ק״מ", quantity: "2.9") + assertDistance(Measurement(value: 3, unit: .kilometers), displayed: "3 ק״מ", quantity: "3") + assertDistance(Measurement(value: 3.5, unit: .kilometers), displayed: "4 ק״מ", quantity: "4") } - + func testDistanceFormatters_hi_IN() { NavigationSettings.shared.distanceUnit = .kilometer - distanceFormatter.numberFormatter.locale = Locale(identifier: "hi-IN") + // Hindi as written in India in Devanagari script with Devanagari numbers. + distanceFormatter.locale = Locale(identifier: "hi-Deva-IN-u-nu-deva") - assertDistance(0, displayed: "० मी॰", quantity: "०") - assertDistance(4, displayed: "५ मी॰", quantity: "५") - assertDistance(11, displayed: "१० मी॰", quantity: "१०") - assertDistance(15, displayed: "१५ मी॰", quantity: "१५") - assertDistance(24, displayed: "२५ मी॰", quantity: "२५") - assertDistance(89, displayed: "१०० मी॰", quantity: "१००") - assertDistance(226, displayed: "२५० मी॰", quantity: "२५०") - assertDistance(275, displayed: "३०० मी॰", quantity: "३००") - assertDistance(500, displayed: "५०० मी॰", quantity: "५००") - assertDistance(949, displayed: "९५० मी॰", quantity: "९५०") - assertDistance(951, displayed: "९५० मी॰", quantity: "९५०") - assertDistance(1000, displayed: "१ कि॰मी॰", quantity: "१") - assertDistance(1001, displayed: "१ कि॰मी॰", quantity: "१") - assertDistance(2_500, displayed: "२.५ कि॰मी॰", quantity: "२.५") - assertDistance(2_900, displayed: "२.९ कि॰मी॰", quantity: "२.९") - assertDistance(3_000, displayed: "३ कि॰मी॰", quantity: "३") - assertDistance(3_500, displayed: "४ कि॰मी॰", quantity: "४") - assertDistance(384_400_000, displayed: "३,८४,४०० कि॰मी॰", quantity: "३,८४,४००") - } - - func testInches() { - let oneMeter: CLLocationDistance = 1 - let oneMeterInInches = oneMeter.converted(to: .inch) - XCTAssertEqual(oneMeterInInches, 39.3700787, accuracy: 0.00001) + assertDistance(Measurement(value: 0, unit: .meters), displayed: "० मी॰", quantity: "०") + assertDistance(Measurement(value: 4, unit: .meters), displayed: "५ मी॰", quantity: "५") + assertDistance(Measurement(value: 11, unit: .meters), displayed: "१० मी॰", quantity: "१०") + assertDistance(Measurement(value: 15, unit: .meters), displayed: "१५ मी॰", quantity: "१५") + assertDistance(Measurement(value: 24, unit: .meters), displayed: "२५ मी॰", quantity: "२५") + assertDistance(Measurement(value: 89, unit: .meters), displayed: "१०० मी॰", quantity: "१००") + assertDistance(Measurement(value: 226, unit: .meters), displayed: "२५० मी॰", quantity: "२५०") + assertDistance(Measurement(value: 275, unit: .meters), displayed: "३०० मी॰", quantity: "३००") + assertDistance(Measurement(value: 500, unit: .meters), displayed: "५०० मी॰", quantity: "५००") + assertDistance(Measurement(value: 949, unit: .meters), displayed: "९५० मी॰", quantity: "९५०") + assertDistance(Measurement(value: 951, unit: .meters), displayed: "९५० मी॰", quantity: "९५०") + assertDistance(Measurement(value: 1, unit: .kilometers), displayed: "१ कि॰मी॰", quantity: "१") + assertDistance(Measurement(value: 1.001, unit: .kilometers), displayed: "१ कि॰मी॰", quantity: "१") + assertDistance(Measurement(value: 2.5, unit: .kilometers), displayed: "२.५ कि॰मी॰", quantity: "२.५") + assertDistance(Measurement(value: 2.9, unit: .kilometers), displayed: "२.९ कि॰मी॰", quantity: "२.९") + assertDistance(Measurement(value: 3, unit: .kilometers), displayed: "३ कि॰मी॰", quantity: "३") + assertDistance(Measurement(value: 3.5, unit: .kilometers), displayed: "४ कि॰मी॰", quantity: "४") + assertDistance(Measurement(value: 384.4, unit: .megameters), displayed: "३,८४,४०० कि॰मी॰", quantity: "३,८४,४००") } } diff --git a/MapboxNavigation.podspec b/MapboxNavigation.podspec index a7ea32bfe15..40ab3ef1980 100644 --- a/MapboxNavigation.podspec +++ b/MapboxNavigation.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - s.ios.deployment_target = "9.0" + s.ios.deployment_target = "10.0" # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # diff --git a/MapboxNavigation.xcodeproj/project.pbxproj b/MapboxNavigation.xcodeproj/project.pbxproj index e7906e55fd7..5b620c4e3cf 100644 --- a/MapboxNavigation.xcodeproj/project.pbxproj +++ b/MapboxNavigation.xcodeproj/project.pbxproj @@ -3439,7 +3439,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -3497,7 +3497,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/MapboxNavigation/BottomBannerViewController.swift b/MapboxNavigation/BottomBannerViewController.swift index 64ace4e261d..d5bb8edf57d 100644 --- a/MapboxNavigation/BottomBannerViewController.swift +++ b/MapboxNavigation/BottomBannerViewController.swift @@ -73,7 +73,7 @@ open class BottomBannerViewController: UIViewController, NavigationComponent { let dateFormatter = DateFormatter() let dateComponentsFormatter = DateComponentsFormatter() - let distanceFormatter = DistanceFormatter(approximate: true) + let distanceFormatter = DistanceFormatter() var verticalCompactConstraints = [NSLayoutConstraint]() var verticalRegularConstraints = [NSLayoutConstraint]() diff --git a/MapboxNavigation/CarPlayManager.swift b/MapboxNavigation/CarPlayManager.swift index 7e9c8fc69cf..4d1506be0f3 100644 --- a/MapboxNavigation/CarPlayManager.swift +++ b/MapboxNavigation/CarPlayManager.swift @@ -551,8 +551,7 @@ extension CarPlayManager: CPMapTemplateDelegate { let mapView = carPlayMapViewController.mapView let route = routeChoice.userInfo as! Route - let distanceFormatter = DistanceFormatter(approximate: true) - let estimates = CPTravelEstimates(distanceRemaining: distanceFormatter.measurement(of: route.distance), + let estimates = CPTravelEstimates(distanceRemaining: Measurement(distance: route.distance).localized(), timeRemaining: route.expectedTravelTime) mapTemplate.updateEstimates(estimates, for: trip) diff --git a/MapboxNavigation/CarPlayNavigationViewController.swift b/MapboxNavigation/CarPlayNavigationViewController.swift index adf044a9cfa..08d3e8d2628 100644 --- a/MapboxNavigation/CarPlayNavigationViewController.swift +++ b/MapboxNavigation/CarPlayNavigationViewController.swift @@ -72,8 +72,6 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie } } - let distanceFormatter = DistanceFormatter(approximate: true) - var edgePadding: UIEdgeInsets { let padding:CGFloat = 15 return UIEdgeInsets(top: view.safeAreaInsets.top + padding, @@ -277,12 +275,12 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie let congestionLevel = routeProgress.averageCongestionLevelRemainingOnLeg ?? .unknown guard let maneuver = carSession.upcomingManeuvers.first else { return } - let routeDistance = distanceFormatter.measurement(of: routeProgress.distanceRemaining) + let routeDistance = Measurement(distance: routeProgress.distanceRemaining).localized() let routeEstimates = CPTravelEstimates(distanceRemaining: routeDistance, timeRemaining: routeProgress.durationRemaining) mapTemplate.update(routeEstimates, for: carSession.trip, with: congestionLevel.asCPTimeRemainingColor) let stepProgress = routeProgress.currentLegProgress.currentStepProgress - let stepDistance = distanceFormatter.measurement(of: stepProgress.distanceRemaining) + let stepDistance = Measurement(distance: stepProgress.distanceRemaining).localized() let stepEstimates = CPTravelEstimates(distanceRemaining: stepDistance, timeRemaining: stepProgress.durationRemaining) carSession.updateEstimates(stepEstimates, for: maneuver) @@ -324,7 +322,7 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie let step = navigationService.routeProgress.currentLegProgress.currentStep let primaryManeuver = CPManeuver() - let distance = distanceFormatter.measurement(of: step.distance) + let distance = Measurement(distance: step.distance).localized() primaryManeuver.initialTravelEstimates = CPTravelEstimates(distanceRemaining: distance, timeRemaining: step.expectedTravelTime) // Just incase, set some default text @@ -379,7 +377,7 @@ public class CarPlayNavigationViewController: UIViewController, NavigationMapVie } if let upcomingStep = navigationService.routeProgress.currentLegProgress.upcomingStep { - let distance = distanceFormatter.measurement(of: upcomingStep.distance) + let distance = Measurement(distance: upcomingStep.distance).localized() tertiaryManeuver.initialTravelEstimates = CPTravelEstimates(distanceRemaining: distance, timeRemaining: upcomingStep.expectedTravelTime) } diff --git a/MapboxNavigation/InstructionsBannerView.swift b/MapboxNavigation/InstructionsBannerView.swift index 7bcb4715135..e19c411574a 100644 --- a/MapboxNavigation/InstructionsBannerView.swift +++ b/MapboxNavigation/InstructionsBannerView.swift @@ -82,7 +82,7 @@ open class BaseInstructionsBannerView: UIControl { var centerYConstraints = [NSLayoutConstraint]() var baselineConstraints = [NSLayoutConstraint]() - let distanceFormatter = DistanceFormatter(approximate: true) + let distanceFormatter = DistanceFormatter() public var distance: CLLocationDistance? { didSet { diff --git a/MapboxNavigation/NSAttributedString.swift b/MapboxNavigation/NSAttributedString.swift index 9b64b7b0a35..6d5e048b223 100644 --- a/MapboxNavigation/NSAttributedString.swift +++ b/MapboxNavigation/NSAttributedString.swift @@ -10,7 +10,6 @@ extension NSAttributedString { } extension NSMutableAttributedString { - @available(iOS 10.0, *) func canonicalizeAttachments(maximumImageSize: CGSize, imageRendererFormat: UIGraphicsImageRendererFormat) { enumerateAttribute(.attachment, in: NSRange(location: 0, length: length), options: []) { (value, range, stop) in guard let attachment = value as? NSTextAttachment, type(of: attachment) != NSTextAttachment.self else { diff --git a/MapboxNavigation/NavigationViewController.swift b/MapboxNavigation/NavigationViewController.swift index 8fabe9be8db..0247b1a9ef1 100644 --- a/MapboxNavigation/NavigationViewController.swift +++ b/MapboxNavigation/NavigationViewController.swift @@ -3,6 +3,8 @@ import MapboxCoreNavigation import MapboxDirections import MapboxSpeech import AVFoundation +import UserNotifications +import MobileCoreServices import Mapbox /** @@ -323,28 +325,31 @@ open class NavigationViewController: UIViewController, NavigationStatusPresenter // MARK: Route controller notifications - func scheduleLocalNotification(about step: RouteStep) { + func scheduleLocalNotification(about step: RouteStep, identifier: String) { guard sendsNotifications else { return } guard UIApplication.shared.applicationState == .background else { return } - guard let text = step.instructionsSpokenAlongStep?.last?.text else { return } + guard let instruction = step.instructionsDisplayedAlongStep?.last else { return } - let notification = UILocalNotification() - notification.alertBody = text - notification.fireDate = Date() + let content = UNMutableNotificationContent() + if let primaryText = instruction.primaryInstruction.text { + content.title = primaryText + } + if let secondaryText = instruction.secondaryInstruction?.text { + content.subtitle = secondaryText + } - clearStaleNotifications() + if let image = instruction.primaryInstruction.maneuverImage(side: instruction.drivingSide, color: .black, size: CGSize(width: 72, height: 72)), + let imageData = image.pngData() { + let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent("com.mapbox.navigation.notification-icon.png") + do { + try imageData.write(to: temporaryURL) + let iconAttachment = try UNNotificationAttachment(identifier: "maneuver", url: temporaryURL, options: [UNNotificationAttachmentOptionsTypeHintKey: kUTTypePNG]) + content.attachments = [iconAttachment] + } catch {} + } - UIApplication.shared.cancelAllLocalNotifications() - UIApplication.shared.scheduleLocalNotification(notification) - } - - func clearStaleNotifications() { - guard sendsNotifications else { return } - // Remove all outstanding notifications from notification center. - // This will only work if it's set to 1 and then back to 0. - // This way, there is always just one notification. - UIApplication.shared.applicationIconBadgeNumber = 1 - UIApplication.shared.applicationIconBadgeNumber = 0 + let notificationRequest = UNNotificationRequest(identifier: identifier, content: content, trigger: nil) + UNUserNotificationCenter.current().add(notificationRequest, withCompletionHandler: nil) } public func showStatus(title: String, spinner: Bool, duration: TimeInterval, animated: Bool, interactive: Bool) { @@ -504,10 +509,14 @@ extension NavigationViewController: NavigationServiceDelegate { component.navigationService?(service, didPassSpokenInstructionPoint: instruction, routeProgress: routeProgress) } - clearStaleNotifications() + // Remove any notification about an already complete maneuver, even if there isn’t another notification to replace it with yet. + let notificationIdentifier = "instruction" + UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notificationIdentifier]) + UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [notificationIdentifier]) - if routeProgress.currentLegProgress.currentStepProgress.durationRemaining <= RouteControllerHighAlertInterval { - scheduleLocalNotification(about: routeProgress.currentLegProgress.currentStep) + let legProgress = routeProgress.currentLegProgress + if legProgress.currentStepProgress.currentSpokenInstruction == legProgress.currentStep.instructionsSpokenAlongStep?.last { + scheduleLocalNotification(about: legProgress.currentStep, identifier: notificationIdentifier) } } diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 90596b84080..2a16711d233 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -125,7 +125,7 @@ class RouteMapViewController: UIViewController { } } var router: Router { return navService.router } - let distanceFormatter = DistanceFormatter(approximate: true) + let distanceFormatter = DistanceFormatter() var arrowCurrentStep: RouteStep? var isInOverviewMode = false { didSet { @@ -207,8 +207,6 @@ class RouteMapViewController: UIViewController { mapView.localizeLabels() } - distanceFormatter.numberFormatter.locale = .nationalizedCurrent - makeGestureRecognizersResetFrameRate() navigationView.overviewButton.addTarget(self, action: Actions.overview, for: .touchUpInside) navigationView.muteButton.addTarget(self, action: Actions.mute, for: .touchUpInside) diff --git a/MapboxNavigation/RouteVoiceController.swift b/MapboxNavigation/RouteVoiceController.swift index c8cc5d414df..1613d099426 100644 --- a/MapboxNavigation/RouteVoiceController.swift +++ b/MapboxNavigation/RouteVoiceController.swift @@ -6,7 +6,6 @@ import MapboxCoreNavigation extension NSAttributedString { - @available(iOS 10.0, *) public func pronounced(_ pronunciation: String) -> NSAttributedString { let phoneticWords = pronunciation.components(separatedBy: " ") let phoneticString = NSMutableAttributedString() @@ -25,7 +24,6 @@ extension NSAttributedString { } extension SpokenInstruction { - @available(iOS 10.0, *) func attributedText(for legProgress: RouteLegProgress) -> NSAttributedString { let attributedText = NSMutableAttributedString(string: text) if let step = legProgress.upcomingStep, @@ -171,26 +169,15 @@ open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate { let audioSession = AVAudioSession.sharedInstance() if #available(iOS 12.0, *) { try audioSession.setCategory(.playback, mode: .voicePrompt, options: [.duckOthers, .mixWithOthers]) - } else if #available(iOS 10.0, *) { - try audioSession.setCategory(.ambient, mode: .spokenAudio, options: [.duckOthers, .mixWithOthers]) } else { - try audioSession.setMode(.spokenAudio) - audioSession.perform(Selector("setCategory:withOptions:error:" as String), - with: AVAudioSession.Category.ambient.rawValue, - with: [AVAudioSession.CategoryOptions.duckOthers.rawValue, - AVAudioSession.CategoryOptions.mixWithOthers.rawValue]) + try audioSession.setCategory(.ambient, mode: .spokenAudio, options: [.duckOthers, .mixWithOthers]) } try audioSession.setActive(true) } func mixAudio() throws { let audioSession = AVAudioSession.sharedInstance() - if #available(iOS 10.0, *) { - try audioSession.setCategory(.ambient, mode: audioSession.mode) - } else { - audioSession.perform(Selector("setCategory:error:" as String), - with: AVAudioSession.Category.ambient.rawValue) - } + try audioSession.setCategory(.ambient, mode: audioSession.mode) try audioSession.setActive(true) } @@ -236,10 +223,8 @@ open class RouteVoiceController: NSObject, AVSpeechSynthesizerDelegate { let modifiedInstruction = voiceControllerDelegate?.voiceController?(self, willSpeak: instruction, routeProgress: routeProgress!) ?? instruction - if #available(iOS 10.0, *), utterance?.voice == nil { + if utterance?.voice == nil { utterance = AVSpeechUtterance(attributedString: modifiedInstruction.attributedText(for: routeProgress!.currentLegProgress)) - } else { - utterance = AVSpeechUtterance(string: modifiedInstruction.text) } // Only localized languages will have a proper fallback voice diff --git a/MapboxNavigation/VisualInstruction.swift b/MapboxNavigation/VisualInstruction.swift index d7531959a84..15a7a710514 100644 --- a/MapboxNavigation/VisualInstruction.swift +++ b/MapboxNavigation/VisualInstruction.swift @@ -9,6 +9,17 @@ extension VisualInstruction { public var containsLaneIndications: Bool { return components.contains(where: { $0 is LaneIndicationComponent }) } + + func maneuverImage(side: DrivingSide, color: UIColor, size: CGSize) -> UIImage? { + let mv = ManeuverView() + mv.frame = CGRect(origin: .zero, size: size) + mv.primaryColor = color + mv.backgroundColor = .clear + mv.scale = UIScreen.main.scale + mv.visualInstruction = self + let image = mv.imageRepresentation + return shouldFlipImage(side: side) ? image?.withHorizontallyFlippedOrientation() : image + } #if canImport(CarPlay) /// Returns a `CPImageSet` representing the maneuver. @@ -16,14 +27,7 @@ extension VisualInstruction { public func maneuverImageSet(side: DrivingSide) -> CPImageSet? { let colors: [UIColor] = [.black, .white] let blackAndWhiteManeuverIcons: [UIImage] = colors.compactMap { (color) in - let mv = ManeuverView() - mv.frame = CGRect(x: 0, y: 0, width: 30, height: 30) - mv.primaryColor = color - mv.backgroundColor = .clear - mv.scale = UIScreen.main.scale - mv.visualInstruction = self - let image = mv.imageRepresentation - return shouldFlipImage(side: side) ? image?.withHorizontallyFlippedOrientation() : image + return maneuverImage(side: side, color: color, size: CGSize(width: 30, height: 30)) } guard blackAndWhiteManeuverIcons.count == 2 else { return nil } return CPImageSet(lightContentImage: blackAndWhiteManeuverIcons[1], darkContentImage: blackAndWhiteManeuverIcons[0]) diff --git a/MapboxNavigationTests/CarPlayManagerTests.swift b/MapboxNavigationTests/CarPlayManagerTests.swift index e7c5e24bad6..40f73e41233 100644 --- a/MapboxNavigationTests/CarPlayManagerTests.swift +++ b/MapboxNavigationTests/CarPlayManagerTests.swift @@ -376,6 +376,7 @@ class CarPlayManagerSpec: QuickSpec { //simulate starting a fake trip manager!.mapTemplate(fakeTemplate, startedTrip: fakeTrip, using: fakeRouteChoice) + manager?.currentNavigator?.navigationService.start() } context("When configured to simulate", { diff --git a/MapboxNavigationTests/CarPlayNavigationViewControllerTests.swift b/MapboxNavigationTests/CarPlayNavigationViewControllerTests.swift index 4e0eda12826..2aab4f2a22e 100644 --- a/MapboxNavigationTests/CarPlayNavigationViewControllerTests.swift +++ b/MapboxNavigationTests/CarPlayNavigationViewControllerTests.swift @@ -81,7 +81,7 @@ fileprivate class CarPlayNavigationViewControllerTests: XCTestCase { } // establish a point of truth and fetch the answer from the update - let distanceTruth = subject.distanceFormatter.measurement(of: progress.distanceRemaining) + let distanceTruth = Measurement(distance: progress.distanceRemaining).localized() let estimateTruth = CPTravelEstimates(distanceRemaining: distanceTruth, timeRemaining: progress.durationRemaining) let answer = update.0 diff --git a/MapboxNavigationTests/InstructionsBannerViewSnapshotTests.swift b/MapboxNavigationTests/InstructionsBannerViewSnapshotTests.swift index 63f51aa5eb0..3f945e19534 100644 --- a/MapboxNavigationTests/InstructionsBannerViewSnapshotTests.swift +++ b/MapboxNavigationTests/InstructionsBannerViewSnapshotTests.swift @@ -219,7 +219,7 @@ class InstructionsBannerViewSnapshotTests: FBSnapshotTestCase { view.maneuverView.isStart = true NavigationSettings.shared.distanceUnit = .kilometer - view.distanceFormatter.numberFormatter.locale = Locale(identifier: "zh-Hans") + view.distanceFormatter.locale = Locale(identifier: "zh-Hans") view.distance = 1000 * 999 let primary = [VisualInstructionComponent(type: .text, text: "中国 安徽省 宣城市 郎溪县", imageURL: nil, abbreviation: nil, abbreviationPriority: NSNotFound)] @@ -233,7 +233,7 @@ class InstructionsBannerViewSnapshotTests: FBSnapshotTestCase { styleInstructionsView(view) NavigationSettings.shared.distanceUnit = .mile - view.distanceFormatter.numberFormatter.locale = Locale(identifier: "sv-se") + view.distanceFormatter.locale = Locale(identifier: "sv-se") view.distance = 1000 * 999 let primary = [VisualInstructionComponent(type: .text, text: "Lorem Ipsum / Dolor Sit Amet", imageURL: nil, abbreviation: nil, abbreviationPriority: NSNotFound)] @@ -247,7 +247,7 @@ class InstructionsBannerViewSnapshotTests: FBSnapshotTestCase { styleInstructionsView(view) NavigationSettings.shared.distanceUnit = .mile - view.distanceFormatter.numberFormatter.locale = Locale(identifier: "uk-UA") + view.distanceFormatter.locale = Locale(identifier: "uk-UA") view.distance = 1000 * 999 let primary = [VisualInstructionComponent(type: .text, text: "Lorem Ipsum / Dolor Sit Amet", imageURL: nil, abbreviation: nil, abbreviationPriority: NSNotFound)] diff --git a/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.BottomBannerSnapshotTests/testBottomBannerViewController_iPhone_X_Portrait_iOS_13.1@3x.png b/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.BottomBannerSnapshotTests/testBottomBannerViewController_iPhone_X_Portrait_iOS_13.1@3x.png new file mode 100644 index 00000000000..767f7dbaf38 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.BottomBannerSnapshotTests/testBottomBannerViewController_iPhone_X_Portrait_iOS_13.1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.GuidanceCardsSnapshotTests/testLanesManeuver_iPhone6Plus_Portrait_iOS_13.1@3x.png b/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.GuidanceCardsSnapshotTests/testLanesManeuver_iPhone6Plus_Portrait_iOS_13.1@3x.png new file mode 100644 index 00000000000..58d3e5a8693 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.GuidanceCardsSnapshotTests/testLanesManeuver_iPhone6Plus_Portrait_iOS_13.1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.GuidanceCardsSnapshotTests/testRegularManeuver_iPhone6Plus_Portrait_iOS_13.1@3x.png b/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.GuidanceCardsSnapshotTests/testRegularManeuver_iPhone6Plus_Portrait_iOS_13.1@3x.png new file mode 100644 index 00000000000..8f1b5801211 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.GuidanceCardsSnapshotTests/testRegularManeuver_iPhone6Plus_Portrait_iOS_13.1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.GuidanceCardsSnapshotTests/testTertiaryManeuver_iPhone6Plus_Portrait_iOS_13.1@3x.png b/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.GuidanceCardsSnapshotTests/testTertiaryManeuver_iPhone6Plus_Portrait_iOS_13.1@3x.png new file mode 100644 index 00000000000..99e2aafada6 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages/MapboxNavigationTests.GuidanceCardsSnapshotTests/testTertiaryManeuver_iPhone6Plus_Portrait_iOS_13.1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.CarPlayCompassViewTests/testEN@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.CarPlayCompassViewTests/testEN@3x.png index a5f661592ec..695c9d7dd97 100644 Binary files a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.CarPlayCompassViewTests/testEN@3x.png and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.CarPlayCompassViewTests/testEN@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.CarPlayCompassViewTests/testHE@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.CarPlayCompassViewTests/testHE@3x.png index 1245ba5a447..6dfc1597b8a 100644 Binary files a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.CarPlayCompassViewTests/testHE@3x.png and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.CarPlayCompassViewTests/testHE@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAbbreviateInstructionsIncludingDelimiter_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAbbreviateInstructionsIncludingDelimiter_iPhone_13_1@3x.png new file mode 100644 index 00000000000..b82602e0994 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAbbreviateInstructionsIncludingDelimiter_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAbbreviateInstructions_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAbbreviateInstructions_iPhone_13_1@3x.png new file mode 100644 index 00000000000..ca8ac7396b2 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAbbreviateInstructions_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAbbreviateWestFremontAvenue_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAbbreviateWestFremontAvenue_iPhone_13_1@3x.png new file mode 100644 index 00000000000..b29adbdd45f Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAbbreviateWestFremontAvenue_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAdjacentShields_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAdjacentShields_iPhone_13_1@3x.png new file mode 100644 index 00000000000..f63dc908f79 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testAdjacentShields_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testExitShields_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testExitShields_iPhone_13_1@3x.png new file mode 100644 index 00000000000..e889d70cc22 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testExitShields_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testGenericShields_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testGenericShields_iPhone_13_1@3x.png new file mode 100644 index 00000000000..d681d6cd590 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testGenericShields_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testInstructionsAndNextInstructions_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testInstructionsAndNextInstructions_iPhone_13_1@3x.png new file mode 100644 index 00000000000..d29f62c5097 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testInstructionsAndNextInstructions_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testLongDistance_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testLongDistance_iPhone_13_1@3x.png new file mode 100644 index 00000000000..c05cb567d44 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testLongDistance_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testMultilinePrimary_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testMultilinePrimary_iPhone_13_1@3x.png new file mode 100644 index 00000000000..311c4b2bbf4 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testMultilinePrimary_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testPrimaryShieldAndSecondary_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testPrimaryShieldAndSecondary_iPhone_13_1@3x.png new file mode 100644 index 00000000000..65efa52c107 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testPrimaryShieldAndSecondary_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testSinglelinePrimaryAndSecondary_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testSinglelinePrimaryAndSecondary_iPhone_13_1@3x.png new file mode 100644 index 00000000000..4829f1cc24d Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testSinglelinePrimaryAndSecondary_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testSinglelinePrimary_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testSinglelinePrimary_iPhone_13_1@3x.png new file mode 100644 index 00000000000..2107a437c8d Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testSinglelinePrimary_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testSweEngLongDistance_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testSweEngLongDistance_iPhone_13_1@3x.png new file mode 100644 index 00000000000..497cf83ad1c Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testSweEngLongDistance_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testUkrainianLongDistance_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testUkrainianLongDistance_iPhone_13_1@3x.png new file mode 100644 index 00000000000..89d125a676d Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.InstructionsBannerViewSnapshotTests/testUkrainianLongDistance_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.LaneTests/testAllLanes30x30_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.LaneTests/testAllLanes30x30_iPhone_13_1@3x.png new file mode 100644 index 00000000000..ce201c0c5b0 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.LaneTests/testAllLanes30x30_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.LaneTests/testAllLanes90x90_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.LaneTests/testAllLanes90x90_iPhone_13_1@3x.png new file mode 100644 index 00000000000..30c096bfd83 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.LaneTests/testAllLanes90x90_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverArrowTests/testManeuverArrowHandlesWaypointsCorrectly@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverArrowTests/testManeuverArrowHandlesWaypointsCorrectly@3x.png index 4ff6cb0d62e..21a2996fd57 100644 Binary files a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverArrowTests/testManeuverArrowHandlesWaypointsCorrectly@3x.png and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverArrowTests/testManeuverArrowHandlesWaypointsCorrectly@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testArriveNone_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testArriveNone_iPhone_13_1@3x.png new file mode 100644 index 00000000000..1f87409a1b8 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testArriveNone_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testArrive_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testArrive_iPhone_13_1@3x.png new file mode 100644 index 00000000000..8f28f5f8120 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testArrive_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testLeftUTurn_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testLeftUTurn_iPhone_13_1@3x.png new file mode 100644 index 00000000000..e18560f36b2 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testLeftUTurn_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testMergeRight_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testMergeRight_iPhone_13_1@3x.png new file mode 100644 index 00000000000..12d20140699 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testMergeRight_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testRightUTurn_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testRightUTurn_iPhone_13_1@3x.png new file mode 100644 index 00000000000..e0f0d1ac17b Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testRightUTurn_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testRoundaboutTurnLeft_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testRoundaboutTurnLeft_iPhone_13_1@3x.png new file mode 100644 index 00000000000..ac55c92c645 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testRoundaboutTurnLeft_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testRoundabout_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testRoundabout_iPhone_13_1@3x.png new file mode 100644 index 00000000000..f46317bebf6 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testRoundabout_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testStraightRoundabout_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testStraightRoundabout_iPhone_13_1@3x.png new file mode 100644 index 00000000000..7e89ebc3637 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testStraightRoundabout_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testTurnRight_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testTurnRight_iPhone_13_1@3x.png new file mode 100644 index 00000000000..45d3efc9446 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testTurnRight_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testTurnSlightRight_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testTurnSlightRight_iPhone_13_1@3x.png new file mode 100644 index 00000000000..81e275c0c42 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.ManeuverViewTests/testTurnSlightRight_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.RouteControllerSnapshotTests/testRouteSnappingOvershooting_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.RouteControllerSnapshotTests/testRouteSnappingOvershooting_iPhone_13_1@3x.png new file mode 100644 index 00000000000..e0ec2e49081 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.RouteControllerSnapshotTests/testRouteSnappingOvershooting_iPhone_13_1@3x.png differ diff --git a/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.SimulatedLocationManagerTests/testSimulateRouteDoublesBack_iPhone_13_1@3x.png b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.SimulatedLocationManagerTests/testSimulateRouteDoublesBack_iPhone_13_1@3x.png new file mode 100644 index 00000000000..53734864676 Binary files /dev/null and b/MapboxNavigationTests/ReferenceImages_64/MapboxNavigationTests.SimulatedLocationManagerTests/testSimulateRouteDoublesBack_iPhone_13_1@3x.png differ diff --git a/README.md b/README.md index 0697d16c2ab..fc0f790775f 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ 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 4.2 or Objective-C in Xcode 10.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 4.2 or Objective-C in Xcode 10.0. The Mapbox Navigation and Mapbox Core Navigation frameworks run on iOS 10.0 and above. -The last release compatible with Swift 3.2 was v0.10.1. +The last release compatible with Swift 3.2 was v0.10.1. The last release compatible with iOS 9._x_ was v0.36.0. The Mapbox Navigation SDK is also available [for Android](https://github.com/mapbox/mapbox-navigation-android/).