Skip to content

RouteController should get navigation statuses based on a consistent timestamp #2583

@1ec5

Description

@1ec5

RouteController currently calls Navigator.status(at:) (a wrapper around Navigator.getStatusForTimestamp(_:)) with inconsistent or user-defined timestamps. Historically, the expectation was that the navigator would be able to march to and fro along the timeline of the route. However, this has never quite been the case in MapboxNavigationNative. A forthcoming version of MapboxNavigationNative will remove the ability to ask for the navigation status at an arbitrary time, to avoid misleading clients into thinking it has a capability that doesn’t quite exist.

Projected location

For forward compatibility, we need to remove RouteController.projectedLocation(for:), which allows applications to request the location at an arbitrary time:

/**
Returns an estimated location at a given timestamp. The timestamp must be
a future timestamp compared to the last location received by the location manager.
*/
public func projectedLocation(for timestamp: Date) -> CLLocation {
return CLLocation(navigator.status(at:timestamp).location)
}

Current location

RouteController internally gets navigation statuses based on the time of the last Core Location update, which accounts for potential interruptions in location updates:

let status = navigator.status(at: location.timestamp)

However, a very commonly used property of RouteController gets the status as of the current time, which can easily fall after the last Core Location update:

https://github.com/mapbox/mapbox-navigation-ios/blob/893f57f0bbb5d6f03fac0fe8ed1408519bb150fe/MapboxCoreNavigation/RouteController.swift#L89-L90

This is likely the usual trigger of #2113. A straightforward fix would be to remember the last location’s timestamp and get the status based on that timestamp.

Leg advancing

RouteController.advanceLegIndex(location:) is also problematic because it can take an arbitrary location and passes its timestamp to the navigator:

public func advanceLegIndex(location: CLLocation) {
let status = navigator.status(at: location.timestamp)
routeProgress.legIndex = Int(status.legIndex)
}

This method is required by the Router protocol, but it’s only used internally by LegacyRouteController to increment RouteProgress.legIndex. The example application used to call this method, too, to demonstrate manually advancing to the next leg after presenting an arrival interstitial at an intermediate waypoint. However, nowadays, it increments the leg index directly and restarts the navigation service:

guard router.route.legs.count > router.routeProgress.legIndex + 1 else { return }
router.routeProgress.legIndex += 1
navService.start()

I’m unsure if that works quite as it’s supposed to. It would be better to encapsulate those details in RouteController.advanceLegIndex(location:), except that the method should take no parameters and always increment the leg index by one. RouteController can easily implement that functionality by calling RouteController.updateRouteLeg(to:).

/cc @mapbox/navigation-ios @mapbox/navnative @d-prukop

Metadata

Metadata

Assignees

Labels

backwards incompatiblechanges that break backwards compatibility of public APIop-exRefactoring, Tech Debt or any other operational excellence work.release blockerNeeds to be resolved before the release.topic: location

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions