-
Notifications
You must be signed in to change notification settings - Fork 313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pair route with route index #2591
Conversation
9a09b0f
to
5193625
Compare
It’s expected that the Xcode_11.4.1_iOS_12.2_CP_update and Xcode_11.4.1_iOS_12.2_CP_install build bots would fail, because this PR depends on mapbox/mapbox-directions-swift#420, which hasn’t been released yet. |
893f57f
to
e0df11d
Compare
9357cdb
to
a303150
Compare
* The `NavigationViewController.route` and `NavigationService.route` properties are now read-only. To change the route that the user is traveling along, set the `NavigationViewController.indexedRoute` or `NavigationService.indexedRoute` property instead, pairing the route with the index of the route in the original `RouteResponse` object. ([#2366](https://github.com/mapbox/mapbox-navigation-ios/pull/2366)) | ||
* The following methods now require a route index to be passed in as an argument ([#2366](https://github.com/mapbox/mapbox-navigation-ios/pull/2366)): | ||
* `NavigationViewController(for:routeIndex:routeOptions:navigationOptions:)` | ||
* `NavigationViewController(route:routeIndex:routeOptions:navigationService:)` | ||
* `CarPlayManagerDelegate.carPlayManager(_:navigationServiceAlong:routeIndex:routeOptions:desiredSimulationMode:)` | ||
* `MapboxNavigationService(route:routeIndex:routeOptions:)` | ||
* `MapboxNavigationService(route:routeIndex:routeOptions:directions:locationSource:eventsManagerType:simulating:routerType:)` | ||
* `RouteProgress(route:routeIndex:options:legIndex:spokenInstructionIndex:)` | ||
* `RouteProgress(route:routeIndex:options:legIndex:spokenInstructionIndex:)` | ||
* `Router(along:routeIndex:options:directions:dataSource:)` | ||
* `RouteController(along:routeIndex:options:directions:dataSource:)` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This blurb nicely encapsulates why I’m nervous about this PR. Adding these extra arguments is pretty safe from a runtime stability standpoint, other than in some of the CarPlay code that relies on casting Any
into tuples, but it’s always a big deal to add complexity to the basic setup workflow in NavigationViewController particularly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm not mistaken in all public API methods which were modified to contain routeIndex
RouteOptions
argument is also present. Would it work if we add routeIndex
as part of options?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A single RouteOptions may be associated with more than one route, so I don’t think that would work. Unless you mean NavigationOptions?
guard let response = response, let route = response.routes?.first, case let .route(routeOptions) = response.options else { return } | ||
|
||
let service = navigationService(route: route, options: routeOptions) | ||
let service = navigationService(route: route, routeIndex: 0, options: routeOptions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In many cases, the developer will be able to hard-code 0 as the route index after getting the first
route out of the route array. But I made the argument required because I didn’t want any code that deals with multiple routes to go unnoticed during an upgrade.
The user info dictionary contains the key `RouteController.NotificationUserInfoKey.routeProgressKey`. | ||
*/ | ||
static let routeControllerDidRefreshRoute: Notification.Name = .init(rawValue: "RouteControllerDidRefreshRoute") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it weird that a notification about refreshing a route comes with a route progress object as user info? The idea is that the route progress object contains the route as well as other members that are affected by refreshing, so it’s handy to have immediate access to the whole route progress object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's not really obvious, I'd expect route Route object to be there. We also have routeControllerProgressDidChange
, but it doesn't contain only RouteProgress
updates.
@@ -30,7 +30,7 @@ public protocol NavigationServiceDelegate: class, UnimplementedLogging { | |||
/** | |||
Called immediately before the navigation service calculates a new route. | |||
|
|||
This method is called after `navigationService(_:shouldRerouteFrom:)` is called, simultaneously with the `NavigationServiceWillReroute` notification being posted, and before `navigationService(_:didRerouteAlong:)` is called. | |||
This method is called after `navigationService(_:shouldRerouteFrom:)` is called, simultaneously with the `Notification.Name.routeControllerWillReroute` notification being posted, and before `navigationService(_:didRerouteAlong:)` is called. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bunch of these references were always inaccurate. I don’t think we’ve ever prefixed the notification names with NavigationService
.
/** | ||
A route and its index in a `RouteResponse` that sorts routes from most optimal to least optimal. | ||
*/ | ||
public typealias IndexedRoute = (Route, Int) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the most generic place I could find for a type that ends up getting used all over the place. The “indexed” pattern is inspired by Turf for Swift. The idea is to force code that sets the route to a new value to also set the route index to a potentially new value at the same time, avoiding stale state or race conditions.
let info: (Route, Int, RouteOptions) = (route: route, routeIndex: routeIndex, options: routeOptions) | ||
routeChoice.userInfo = info |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s frustrating that the only way to associate arbitrary data with UI elements that CarPlay controls is to stuff it in an untyped property and cast it later. As tail work, it might be better to define a set of dictionary keys and set this property to a dictionary, similar to when posting a Notification.
@@ -199,7 +199,7 @@ class CarPlayManagerTests: XCTestCase { | |||
CLLocationCoordinate2D(latitude: 37.764793, longitude: -122.463161), | |||
CLLocationCoordinate2D(latitude: 34.054081, longitude: -118.243412), | |||
]) | |||
choice.userInfo = (Fixture.route(from: "route-with-banner-instructions", options: options), options) | |||
choice.userInfo = (Fixture.route(from: "route-with-banner-instructions", options: options), 0, options) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The route index is almost always 0 in the unit tests and integration tests, because Fixture.route(from:options:)
always returns the first route, and few of the fixtures come with multiple routes anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes look good to me.
@@ -525,6 +535,14 @@ extension NavigationViewController: NavigationServiceDelegate { | |||
delegate?.navigationViewController(self, didFailToRerouteWith: error) | |||
} | |||
|
|||
public func navigationService(_ service: NavigationService, didRefresh routeProgress: RouteProgress) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in this place when should redraw all congestion segments (if they were updated) by calling mapView?.updateRoute(routeProgress)
. At the same time current implementation of congestion segments calculation re-uses already existing lineGradient
stops which means that route in most recent RouteProgress
will not be used. Previously I was aiming to prevent re-calculation because for routes with many congestion segments such logic might cause performance drawbacks, on the other hand in current situation there seems no other way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean in RouteMapViewController?
Would it be a good idea to call updateRoute(_:)
right after showing the route? We only attempt to fetch a refreshed route every 2 minutes (as determined by RouteControllerProactiveReroutingInterval
, which is publicly configurable), which is the same interval used for proactively finding a faster route. So I’m not terribly concerned about runtime performance unless calculating the segments is very poor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think most recent update in RouteMapViewController
should work (removing all route layers, showing them for traveled distance = 0.0, updating them for traveled distance from RouteProgress
). On the other hand it defats the idea of reusing layers (I think the only data we need from RouteProgress
in such case is shape
). We can leave current implementation, but revise it in future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, let’s deal with any performance optimizations as tail work.
a303150
to
fc7cf89
Compare
1bd8497
to
ac01728
Compare
e0df11d
to
6103287
Compare
ac01728
to
d29ee08
Compare
Keep track of the route’s index in the overall route response and use it when refreshing the route. Added a route index argument to a litany of public and private methods and initializers. Explicitly merge the skeletal refreshed route into the original route.
Post a notification and inform the navigation service and navigation view controller delegates whenever the route is refreshed. On refresh, update the route line to reflect the new traffic congestion data.
d29ee08
to
e979932
Compare
Cherry picked from c945bcf.
This PR updates #2366 to work with the latest changes in mapbox/mapbox-directions-swift#420 that decapsulate route refreshing a bit. I’ve opened a separate PR because the changes are massive and gangly, and I’m open to reconsidering the approach to make it less disruptive. See the changes to CHANGELOG.md for details.
With this PR, classes that previously kept track of the route now also keep track of the route’s index in the overall route response and use it when refreshing the route. This entailed adding a required route index argument to a litany of public and private methods and initializers, though it was possible to hard-code 0 in many places due to the prior use of
routes.first
. With these changes, it becomes clear how we’d transition the public API to treating the route as merely an indexed item in the overall route response: #2589. We could bite that off here too; it all comes down to whether the (route, route index) tuple would be easier or harder to transition to.Additionally and critically, the code to merge the skeletal refreshed route into the original route has moved from mapbox/mapbox-directions-swift#420 into this PR.
/cc @mapbox/navigation-ios