Skip to content
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

Integrated new directions API fields with the tertiary instruction and lane indications #1514

Merged
merged 57 commits into from
Jul 17, 2018

Conversation

vincethecoder
Copy link
Contributor

These updates make sure we rely on the new directions LaneIndicationComponents class for lane information. This information is usually retrieved from the tertiary instruction -- which can either be a LaneIndicationComponent or VisualInstructionComponent. The instructions banner and integrated tests were also modified to reflect the new changes.

Relies on mapbox/mapbox-directions-swift#258

@vincethecoder vincethecoder self-assigned this Jun 21, 2018
}
let step = currentLegProgress.currentStep

guard durationRemaining < RouteControllerMediumAlertInterval,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bsudekum do you think we should reduce the time threshold for tertiaryInstructions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not actually be doing any check like this now. The server should tell us exactly when and what to show.

@@ -40,7 +40,7 @@ Pod::Spec.new do |s|
s.requires_arc = true
s.module_name = "MapboxCoreNavigation"

s.dependency "MapboxDirections.swift", "~> 0.21.0"
s.dependency "MapboxDirections.swift", :git => "https://github.com/mapbox/MapboxDirections.swift.git", :branch => "then"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, something like this does not work. Only published pods are able to be pulled in as deps here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😞

stackView.addArrangedSubview(laneView)
}
let step = currentLegProgress.currentStep
guard let firstInstruction = step.instructionsDisplayedAlongStep?.first,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if there is more than one visual instruction on the step?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need something like what we have for the currentSpokenInstruction but for visual instruction:

@objc public var currentSpokenInstruction: SpokenInstruction? {
guard let instructionsSpokenAlongStep = step.instructionsSpokenAlongStep else { return nil }
guard spokenInstructionIndex < instructionsSpokenAlongStep.count else { return nil }
return instructionsSpokenAlongStep[spokenInstructionIndex]
}

We have all the pieces slightly above this computed variable to create currentVisualInstruction.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. That was an oversight. I will make the necessary changes to account for multiple visual instructions.

} else {
hide()
}
let subviews = lanes.map { LaneView(component: $0, direction: step.maneuverDirection) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to init new LaneView's? Shouldn't we already have these objects?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we are given the LaneIndicationComponents from the API.

return
}

let components: [ComponentRepresentable] = instructionsDisplayedAlongStep
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vincethecoder I'm still not seeing how we're finding the current visual instruction that should be displayed. If a step has 3 different tertiary instructions, how do we know which one to show?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bsudekum smh - using the current spoken instruction index. Will update in a moment.

} else {
hide()
let currentInstruction = visualInstructions[spokenInstructionIndex]
guard let lanes: [LaneIndicationComponent] = currentInstruction.tertiaryInstruction?.components.compactMap({ component in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to pull this out into an optional variable? I think it would make this guard easier to read.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!

show()
} else {
hide()
let currentInstruction = visualInstructions[spokenInstructionIndex]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we're still using spokenInstructionIndex instead of the visual index.

@vincethecoder vincethecoder added the ⚠️ DO NOT MERGE PR should not be merged! label Jun 25, 2018
@vincethecoder
Copy link
Contributor Author

vincethecoder commented Jun 25, 2018

Waiting on dependent PR mapbox/mapbox-directions-swift#258 to be merged and published. Then this PR can pass the CI tests before it's merged into master.

@bsudekum
Copy link
Contributor

Started to test a few test cases, here is one that I'm looking at, request, visual link.

However, it looks like something is stuck in an infinite loop:

image

@vincethecoder
Copy link
Contributor Author

@bsudekum Upon changes made here, here is the new memory report.

/cc @akitchen - decided to post the memory report, as the current function is not unit testable at the moment.

Copy link
Contributor Author

@vincethecoder vincethecoder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akitchen we definitely need to develop a level of abstraction to ensure that the cache key fo exit label works. Since the current implementation is not a viable candidate for a unit test, I've decided to share the improvement in memory performance as a result of my changes here.

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind capturing some current screenshots of the new UI in action? I’m particularly curious to see turn lanes with multiple usable lanes, “then” banners around arrival steps, and intermediate waypoint steps.

@@ -141,4 +141,10 @@ class ExitView: StylableView {
let labelTrailing = trailingAnchor.constraint(equalTo: exitNumberLabel.trailingAnchor, constant: 8)
return [imageLeading, imageLabelSpacing, labelTrailing]
}

static func criticalHash(side: ExitSide, dataSource: DataSource) -> String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this called “critical”? Is there something important about this method and similarly named methods that needs to be documented somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JThramer could you chime in to elucidate the essence of this refactor for ExitView?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still unclear on this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes -- the reason why it's called criticalHash is because the side: and dataSource: arguments contain critical values that affect the appearance of the ExitView.

Considering that we're caching views by their rendered images, any property that affects how an ExitView might appear needs to be accounted for in the cache key logic.

@@ -93,7 +93,7 @@ open class BaseInstructionsBannerView: UIControl {
/**
Updates the instructions banner info with a given `VisualInstructionBanner`.
*/
public func updateInstruction(_ instruction: VisualInstructionBanner?) {
public func update(for instruction: VisualInstructionBanner?) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this method is public, we should mention in the changelog that it has been renamed. Also, it appears to be inaccessible to Objective-C.

if stackView.arrangedSubviews.count > 0 {
show()
} else {
let laneIndications: [LaneIndicationComponent]? = tertiaryInstruction.components.compactMap({ component in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: we can use trailing closure syntax here, since it isn’t confusing.

show()
} else {
let laneIndications: [LaneIndicationComponent]? = tertiaryInstruction.components.compactMap({ component in
guard let lane = component as? LaneIndicationComponent else { return nil }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be written more succinctly as { component as? LaneIndicationComponent }.

Bobby Sudekum and others added 6 commits July 13, 2018 08:28
@bsudekum
Copy link
Contributor

@vincethecoder I think we're close, just need a few screenshots that @1ec5 is asking for and we can merge.

@vincethecoder
Copy link
Contributor Author

vincethecoder commented Jul 16, 2018

@1ec5

screen2

screen1

screen3

screen4

/cc @bsudekum

CHANGELOG.md Outdated
@@ -11,8 +11,9 @@
* Fixed an issue when selecting a step from the steps list, you could be brought to the wrong step. [#1524](https://github.com/mapbox/mapbox-navigation-ios/pull/1524/)
* `StyleManager.locationFor(styleManager:)` now allows for an optional CLLocation to be returned. [#1523](https://github.com/mapbox/mapbox-navigation-ios/pull/1523)
* NavigationViewController now uses the recommended way `.preferredStatusBarStyle` to set the style of the status bar. [#1535](https://github.com/mapbox/mapbox-navigation-ios/pull/1535)
* Renamed `InstructionsBannerView.updateInstruction(_:)` to `InstructionsBannerView.update(for:)`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link to this PR from any new changeset entries, so that developers can get the full story from the PR description (which should be expanded to explain the API changes too).

CHANGELOG.md Outdated
* Deleted `InstructionsBannerView.update(_:)`, which previously updated the banner instruction and the current distance.
* This is replaced with `InstructionsBannerView.updateDistance(for:)` and `InstructionsBannerView.updateInstruction(_:)`. `InstructionsBannerView.updateDistance(for:)` should be called on every location update, while `InstructionsBannerView.updateInstruction(_:)` should be called when `NSNotification.Name. routeControllerDidPassVisualInstructionPoint` is emitted.
* This is replaced with `InstructionsBannerView.updateDistance(for:)` and `InstructionsBannerView.update(for:)`. `InstructionsBannerView.updateDistance(for:)` should be called on every location update, while `InstructionsBannerView.update(for:)` should be called when `NSNotification.Name. routeControllerDidPassVisualInstructionPoint` is emitted.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These three points can potentially be combined into one with less repetition.

@@ -93,7 +93,7 @@ open class BaseInstructionsBannerView: UIControl {
/**
Updates the instructions banner info with a given `VisualInstructionBanner`.
*/
public func updateInstruction(_ instruction: VisualInstructionBanner?) {
@objc public func update(for instruction: VisualInstructionBanner?) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Objective-C name of this method is -updateWithFor:. It should be -updateForVisualInstructionBanner:. (×3)

@@ -166,7 +167,10 @@ class InstructionPresenter {
}

private func instructionHasDownloadedAllShields() -> Bool {
for component in instruction.textComponents {
let textComponents = instruction.components.compactMap { $0 as? VisualInstructionComponent }
guard !textComponents.isEmpty else { return false }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be written more succinctly as:

guard let _ = instruction.components.contains(where: { $0 is VisualInstructionComponent }) else { return false }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

guard instruction.components.contains(where: { $0 is VisualInstructionComponent }) else { return false } 😏

Copy link
Contributor Author

@vincethecoder vincethecoder Jul 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@1ec5 Indeed that is succinct. However, for the purpose of this function scope, we need the variable and also an early exit if it's empty. Essentially, we need the contents of textComponent to iterate at this line

https://github.com/mapbox/mapbox-navigation-ios/pull/1514/files/13f2d71321b7d5e320721e611c983e6411a2d904#diff-af48d70309ee195e34cc5e6cc6b27caaR173

1ec5
1ec5 previously approved these changes Jul 16, 2018
@@ -141,4 +141,10 @@ class ExitView: StylableView {
let labelTrailing = trailingAnchor.constraint(equalTo: exitNumberLabel.trailingAnchor, constant: 8)
return [imageLeading, imageLabelSpacing, labelTrailing]
}

static func criticalHash(side: ExitSide, dataSource: DataSource) -> String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still unclear on this.

@1ec5 1ec5 dismissed their stale review July 16, 2018 20:30

Misclicked

@@ -93,7 +93,8 @@ open class BaseInstructionsBannerView: UIControl {
/**
Updates the instructions banner info with a given `VisualInstructionBanner`.
*/
public func updateInstruction(_ instruction: VisualInstructionBanner?) {
@objc(updateForVisualInstruction:)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be -updateForVisualInstructionBanner:, given the parameter’s type.

Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing documentation, plus that “critical hash” thing in #1514 (comment), but otherwise good to go.

guard !currentLegProgress.userHasArrivedAtWaypoint else { return }
let durationRemaining = currentLegProgress.currentStepProgress.durationRemaining

@objc(updateForVisualInstructionBanner:)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a documentation comment. (×3)

}
}

@objc public var drivingSide: DrivingSide = .right {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two properties are missing documentation comments.

@@ -141,4 +141,13 @@ class ExitView: StylableView {
let labelTrailing = trailingAnchor.constraint(equalTo: exitNumberLabel.trailingAnchor, constant: 8)
return [imageLeading, imageLabelSpacing, labelTrailing]
}

/**
`criticalHash(side:dataSource:)` generates the cache key needed to hold the `ExitView`'s `imageRepresentation` in the `ImageCache` caching engine.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Possessive its' is with the apostrophe at the end.

Copy link
Contributor

@akitchen akitchen Jul 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Possessive its' is with the apostrophe at the end.

wait, what? where?

"its" is the possessive form (think "his")
"it's" is the contraction for "it is"

There is no such thing as its'

Perhaps you are thinking of the plural possessive? For example, "Our engineers' computers are too slow"

but I don't see how any of those apply here.

</grammar police>

@vincethecoder vincethecoder dismissed bsudekum’s stale review July 17, 2018 15:07

Has been addressed.

@vincethecoder vincethecoder merged commit dfe9df7 into master Jul 17, 2018
@1ec5 1ec5 added the backwards incompatible changes that break backwards compatibility of public API label Dec 18, 2018
@1ec5 1ec5 added this to the v0.19.0 milestone Dec 18, 2018
@1ec5 1ec5 deleted the navigation/tertiary_instructions_lane_update branch December 5, 2019 18:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backwards incompatible changes that break backwards compatibility of public API
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants