Skip to content

Commit

Permalink
Deprecate default style, instead pass in style URL
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelkirk committed May 22, 2024
1 parent 9c5b637 commit 71b505a
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 43 deletions.
4 changes: 2 additions & 2 deletions MapboxNavigation/CarPlayMapViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ class CarPlayMapViewController: UIViewController, MLNMapViewDelegate {
super.viewDidLoad()

self.styleManager = StyleManager(self)
self.styleManager.styles = [DayStyle(), NightStyle()]
self.styleManager.styles = [DayStyle(demoStyle: ()), NightStyle(demoStyle: ())]

self.resetCamera(animated: false, altitude: CarPlayMapViewController.defaultAltitude)
self.mapView.setUserTrackingMode(.followWithCourse, animated: true, completionHandler: nil)
}
Expand Down
4 changes: 2 additions & 2 deletions MapboxNavigation/CarPlayNavigationViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ public class CarPlayNavigationViewController: UIViewController, MLNMapViewDelega
view.addSubview(mapView)

self.styleManager = StyleManager(self)
self.styleManager.styles = [DayStyle(), NightStyle()]
self.styleManager.styles = [DayStyle(demoStyle: ()), NightStyle(demoStyle: ())]

self.resumeNotifications()
self.routeController.resume()
mapView.recenterMap()
Expand Down
13 changes: 5 additions & 8 deletions MapboxNavigation/DayStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,12 @@ private extension UIFont {
*/
@objc(MBDayStyle)
open class DayStyle: Style {
public required init() {
super.init()
mapStyleURL = MLNStyle.defaultStyle().url
@objc public required init(mapStyleURL: URL) {
super.init(mapStyleURL: mapStyleURL)
styleType = .day
statusBarStyle = .default
}

override open func apply() {
super.apply()

Expand Down Expand Up @@ -166,10 +165,8 @@ open class DayStyle: Style {
*/
@objc(MBNightStyle)
open class NightStyle: DayStyle {
public required init() {
super.init()
mapStyleURL = MLNStyle.defaultStyle().url
previewMapStyleURL = MLNStyle.defaultStyle().url
public required init(mapStyleURL: URL) {
super.init(mapStyleURL: mapStyleURL)
styleType = .night
statusBarStyle = .lightContent
}
Expand Down
2 changes: 1 addition & 1 deletion MapboxNavigation/NavigationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ open class NavigationView: UIView {

override open func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
DayStyle().apply()
DayStyle(demoStyle: ()).apply()
[self.mapView, self.instructionsBannerView, self.lanesView, self.bottomBannerView, self.nextBannerView].forEach { $0.prepareForInterfaceBuilder() }
self.wayNameView.text = "Street Label"
}
Expand Down
85 changes: 75 additions & 10 deletions MapboxNavigation/NavigationViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -345,32 +345,97 @@ open class NavigationViewController: UIViewController {
super.init(coder: aDecoder)
}

/// Initializes a `NavigationViewController` that provides turn by turn navigation for the given route. A optional `directions` object is needed for potential rerouting.
/// Initializes a `NavigationViewController` that provides turn by turn navigation for the given route.
///
/// ```
/// var dayStyle = DayStyle()
/// dayStyle.mapStyleURL = styleURL
/// let dayStyle = DayStyle(mapStyleURL: styleURL)
/// let vc = NavigationViewController(route: route, styles: [dayStyle])
/// self.presentViewController(vc, animated: true)
/// ```
/// - Parameters:
/// - route: The route to follow.
/// - directions: Used when recomputing a new route, for example if the user takes a wrong turn and needs re-routing.
/// - directions: Used when recomputing a new route, for example if the user takes a wrong turn and needs re-routing. If unspecified, a default will be used.
/// - styles: The `[dayStyle]` or `[dayStyle, nightStyle]` styles used to render the map. If nil, the default styles will be used.
/// - routeController: Used to monitor the route and notify of changes to the route. If nil, a default will be used.
/// - locationManager: Tracks the users location along the route. If nil, a default will be used.
/// - voiceController: Produces voice instructions for route navigation. If nil, a default will be used.
///
/// See [Mapbox Directions](https://mapbox.github.io/mapbox-navigation-ios/directions/) for further information.
@available(*, deprecated, message: "Use `init(for:directions:dayStyle:...) or init(for:directions:dayStyleURL:...)` instead.")
@objc(initWithRoute:directions:styles:routeController:locationManager:voiceController:)
public convenience init(for route: Route,
directions: Directions = Directions.shared,
styles: [Style]? = [DayStyle(), NightStyle()],
routeController: RouteController? = nil,
locationManager: NavigationLocationManager? = nil,
voiceController: RouteVoiceController? = nil) {
let styles = styles ?? []
assert(styles.count <= 2, "Having more than two styles is undefined.")
let dayStyle = styles.first ?? DayStyle(demoStyle: ())
let nightStyle = styles.count > 1 ? styles[1] : NightStyle(mapStyleURL: dayStyle.mapStyleURL)

self.init(for: route, dayStyle: dayStyle, nightStyle: nightStyle, directions: directions, routeController: routeController, locationManager: locationManager, voiceController: voiceController)
}

/// Initializes a `NavigationViewController` that provides turn by turn navigation for the given route.
///
/// - Parameters:
/// - route: The route to follow.
/// - dayStyleURL: URL for the style rules used to render the map during daylight hours.
/// - nightStyleURL: URL for the style rules used to render the map during nighttime hours. If nil, `dayStyleURL` will be used at night as well.
/// - directions: Used when recomputing a new route, for example if the user takes a wrong turn and needs re-routing. If unspecified, a default will be used.
/// - routeController: Used to monitor the route and notify of changes to the route. If nil, a default will be used.
/// - locationManager: Tracks the users location along the route. If nil, a default will be used.
/// - voiceController: Produces voice instructions for route navigation. If nil, a default will be used.
///
/// See [Mapbox Directions](https://mapbox.github.io/mapbox-navigation-ios/directions/) for further information.
@objc(initWithRoute:dayStyleURL:nightStyleURL:directions:routeController:locationManager:voiceController:)
public convenience init(for route: Route,
dayStyleURL: URL,
nightStyleURL: URL? = nil,
directions: Directions = Directions.shared,
routeController: RouteController? = nil,
locationManager: NavigationLocationManager? = nil,
voiceController: RouteVoiceController? = nil) {
let dayStyle = DayStyle(mapStyleURL: dayStyleURL)
let nightStyle = NightStyle(mapStyleURL: nightStyleURL ?? dayStyleURL)
self.init(for: route, dayStyle: dayStyle, nightStyle: nightStyle, directions: directions, routeController: routeController, locationManager: locationManager, voiceController: voiceController)
}

/// Initializes a `NavigationViewController` that provides turn by turn navigation for the given route.
///
/// - Parameters:
/// - route: The route to follow.
/// - dayStyle: Style used to render the map during daylight hours.
/// - nightStyle: Style used to render the map during nighttime hours. If nil, `dayStyle` will be used at night as well.
/// - directions: Used when recomputing a new route, for example if the user takes a wrong turn and needs re-routing. If unspecified, a default will be used.
/// - routeController: Used to monitor the route and notify of changes to the route. If nil, a default will be used.
/// - locationManager: Tracks the users location along the route. If nil, a default will be used.
/// - voiceController: Produces voice instructions for route navigation. If nil, a default will be used.
///
/// See [Mapbox Directions](https://mapbox.github.io/mapbox-navigation-ios/directions/) for further information.
@objc(initWithRoute:dayStyle:nightStyle:directions:routeController:locationManager:voiceController:)
public required init(for route: Route,
dayStyle: Style,
nightStyle: Style? = nil,
directions: Directions = Directions.shared,
styles: [Style]? = [DayStyle(), NightStyle()],
routeController: RouteController? = nil,
locationManager: NavigationLocationManager? = nil,
voiceController: RouteVoiceController? = nil) {
let nightStyle = {
if let nightStyle {
return nightStyle
}

let dayCopy: DayStyle = dayStyle.copy() as! DayStyle

Check failure on line 430 in MapboxNavigation/NavigationViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

testDestinationAnnotationUpdatesUponReroute, -[MBDayStyle copyWithZone:]: unrecognized selector sent to instance 0x60000212e490 (NSInvalidArgumentException)

Check failure on line 430 in MapboxNavigation/NavigationViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

testNavigationShouldNotCallStyleManagerDidRefreshAppearanceMoreThanOnceWithOneStyle, -[MBDayStyle copyWithZone:]: unrecognized selector sent to instance 0x60000212ff20 (NSInvalidArgumentException)

Check failure on line 430 in MapboxNavigation/NavigationViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

testNavigationShouldNotCallStyleManagerDidRefreshAppearanceMoreThanOnceWithTwoStyles, -[MBDayStyle copyWithZone:]: unrecognized selector sent to instance 0x600002132df0 (NSInvalidArgumentException)

Check failure on line 430 in MapboxNavigation/NavigationViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

testNavigationShouldNotCallStyleManagerDidRefreshAppearanceWhenOnlyOneStyle, -[MBDayStyle copyWithZone:]: unrecognized selector sent to instance 0x6000021345f0 (NSInvalidArgumentException)

Check failure on line 430 in MapboxNavigation/NavigationViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

testNavigationViewControllerDelegateRoadNameAtLocationEmptyString, -[MBDayStyle copyWithZone:]: unrecognized selector sent to instance 0x600002139540 (NSInvalidArgumentException)

Check failure on line 430 in MapboxNavigation/NavigationViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

testNavigationViewControllerDelegateRoadNameAtLocationImplemented, -[MBDayStyle copyWithZone:]: unrecognized selector sent to instance 0x600002135310 (NSInvalidArgumentException)

Check failure on line 430 in MapboxNavigation/NavigationViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

testNavigationViewControllerDelegateRoadNameAtLocationUmimplemented, -[MBDayStyle copyWithZone:]: unrecognized selector sent to instance 0x6000021376b0 (NSInvalidArgumentException)
dayCopy.styleType = .night
return dayCopy
}()

assert(dayStyle.styleType == .day)
assert(nightStyle.styleType == .night)

super.init(nibName: nil, bundle: nil)

self.locationManager = locationManager ?? NavigationLocationManager()
let routeController = routeController ?? RouteController(along: route, directions: directions, locationManager: self.locationManager)
self.routeController = routeController
Expand Down Expand Up @@ -399,8 +464,8 @@ open class NavigationViewController: UIViewController {
mapViewController.reportButton.isHidden = !self.showsReportFeedback

self.styleManager = StyleManager(self)
self.styleManager.styles = styles ?? [DayStyle(), NightStyle()]
self.styleManager.styles = [dayStyle, nightStyle]

if !(route.routeOptions is NavigationRouteOptions) {
print("`Route` was created using `RouteOptions` and not `NavigationRouteOptions`. Although not required, this may lead to a suboptimal navigation experience. Without `NavigationRouteOptions`, it is not guaranteed you will get congestion along the route line, better ETAs and ETA label color dependent on congestion.")
}
Expand Down Expand Up @@ -527,8 +592,8 @@ open class NavigationViewController: UIViewController {
let locationManager = routeController.locationManager.copy() as! NavigationLocationManager
let directions = routeController.directions
let route = routeController.routeProgress.route
let navigationViewController = NavigationViewController(for: route, directions: directions, routeController: routeController, locationManager: locationManager)
let navigationViewController = NavigationViewController(for: route, dayStyle: DayStyle(demoStyle: ()), directions: directions, routeController: routeController, locationManager: locationManager)

window.rootViewController?.topMostViewController()?.present(navigationViewController, animated: true, completion: {
navigationViewController.isUsedInConjunctionWithCarPlayWindow = true
})
Expand Down
15 changes: 13 additions & 2 deletions MapboxNavigation/Style.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,19 @@ open class Style: NSObject {
Applies the style for all changed properties.
*/
@objc open func apply() {}

@objc override public required init() {}

@available(*, deprecated, message: "Use `init(mapStyleURL:)` to specify your map style. If you want to try the demo maplibre tiles, use init(demoStyle: ()).")
@objc override public convenience init() {
self.init(demoStyle: ())
}

@objc public required init(mapStyleURL: URL) {
self.mapStyleURL = mapStyleURL
}

@objc public convenience init(demoStyle: ()) {
self.init(mapStyleURL: MLNStyle.defaultStyle().url)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class NavigationViewControllerTests: XCTestCase {
lazy var dependencies: (navigationViewController: NavigationViewController, startLocation: CLLocation, poi: [CLLocation], endLocation: CLLocation, voice: RouteVoiceController) = {
let voice = FakeVoiceController()
let nav = NavigationViewController(for: initialRoute,
dayStyle: DayStyle(demoStyle: ()),
directions: Directions(accessToken: "garbage", host: nil),
voiceController: voice)

Expand Down Expand Up @@ -88,7 +89,10 @@ class NavigationViewControllerTests: XCTestCase {
}

func testNavigationShouldNotCallStyleManagerDidRefreshAppearanceMoreThanOnceWithOneStyle() {
let navigationViewController = NavigationViewController(for: initialRoute, directions: fakeDirections, styles: [DayStyle()], voiceController: FakeVoiceController())
let navigationViewController = NavigationViewController(for: initialRoute,
dayStyle: DayStyle(demoStyle: ()),
directions: fakeDirections,
voiceController: FakeVoiceController())
let routeController = navigationViewController.routeController!
navigationViewController.styleManager.delegate = self

Expand All @@ -104,7 +108,9 @@ class NavigationViewControllerTests: XCTestCase {

// If tunnel flags are enabled and we need to switch styles, we should not force refresh the map style because we have only 1 style.
func testNavigationShouldNotCallStyleManagerDidRefreshAppearanceWhenOnlyOneStyle() {
let navigationViewController = NavigationViewController(for: initialRoute, directions: fakeDirections, styles: [NightStyle()], voiceController: FakeVoiceController())
// REVIEW: is this right? Does it make sense that there would ever be *only* a night style?
// let navigationViewController = NavigationViewController(for: initialRoute, directions: fakeDirections, styles: [NightStyle()], voiceController: FakeVoiceController())
let navigationViewController = NavigationViewController(for: initialRoute, dayStyle: DayStyle(demoStyle: ()), directions: fakeDirections, voiceController: FakeVoiceController())
let routeController = navigationViewController.routeController!
navigationViewController.styleManager.delegate = self

Expand All @@ -119,7 +125,7 @@ class NavigationViewControllerTests: XCTestCase {
}

func testNavigationShouldNotCallStyleManagerDidRefreshAppearanceMoreThanOnceWithTwoStyles() {
let navigationViewController = NavigationViewController(for: initialRoute, directions: fakeDirections, styles: [DayStyle(), NightStyle()], voiceController: FakeVoiceController())
let navigationViewController = NavigationViewController(for: initialRoute, dayStyle: DayStyle(demoStyle: ()), nightStyle: NightStyle(demoStyle: ()), directions: fakeDirections, voiceController: FakeVoiceController())
let routeController = navigationViewController.routeController!
navigationViewController.styleManager.delegate = self

Expand Down Expand Up @@ -173,8 +179,8 @@ class NavigationViewControllerTests: XCTestCase {

func testDestinationAnnotationUpdatesUponReroute() {
let styleLoaded = XCTestExpectation(description: "Style Loaded")
let navigationViewController = NavigationViewControllerTestable(for: initialRoute, styles: [TestableDayStyle()], styleLoaded: styleLoaded)
let navigationViewController = NavigationViewControllerTestable(for: initialRoute, dayStyle: DayStyle.blankStyleForTesting, styleLoaded: styleLoaded)

// wait for the style to load -- routes won't show without it.
wait(for: [styleLoaded], timeout: 5)
navigationViewController.route = self.initialRoute
Expand Down Expand Up @@ -248,21 +254,19 @@ private extension NavigationViewControllerTests {

class NavigationViewControllerTestable: NavigationViewController {
var styleLoadedExpectation: XCTestExpectation

required init(for route: Route,
directions: Directions = Directions(accessToken: "abc", host: ""),
styles: [Style]? = [DayStyle(), NightStyle()],
locationManager: NavigationLocationManager? = NavigationLocationManager(),
dayStyle: Style,
styleLoaded: XCTestExpectation) {
self.styleLoadedExpectation = styleLoaded
super.init(for: route, directions: directions, styles: styles, locationManager: locationManager, voiceController: FakeVoiceController())
super.init(for: route, dayStyle: dayStyle, directions: Directions(accessToken: "abc", host: ""), voiceController: FakeVoiceController())
}

@objc(initWithRoute:directions:styles:routeController:locationManager:voiceController:)
required init(for route: Route, directions: Directions, styles: [Style]?, routeController: RouteController?, locationManager: NavigationLocationManager?, voiceController: RouteVoiceController?) {
fatalError("init(for:directions:styles:routeController:locationManager:voiceController:) is not supported in this testing subclass.")
@objc(initWithRoute:dayStyle:nightStyle:directions:routeController:locationManager:voiceController:)
required init(for route: Route, dayStyle: Style, nightStyle: Style? = nil, directions: Directions = Directions.shared, routeController: RouteController? = nil, locationManager: NavigationLocationManager? = nil, voiceController: RouteVoiceController? = nil) {
fatalError("init(for:directions:dayStyle:nightStyle:routeController:locationManager:voiceController:) has not been implemented")
}

func mapView(_ mapView: MLNMapView, didFinishLoading style: MLNStyle) {
self.styleLoadedExpectation.fulfill()
}
Expand All @@ -273,10 +277,9 @@ class NavigationViewControllerTestable: NavigationViewController {
}
}

class TestableDayStyle: DayStyle {
required init() {
super.init()
mapStyleURL = Fixture.blankStyle
extension DayStyle {
static var blankStyleForTesting: Self {
Self(mapStyleURL: Fixture.blankStyle)
}
}

Expand Down

0 comments on commit 71b505a

Please sign in to comment.