-
Notifications
You must be signed in to change notification settings - Fork 90
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
Add then component #258
Add then component #258
Conversation
var thenText: String? | ||
var thenTextComponents: [VisualInstructionComponent]? | ||
if let thenTextComponent = json["then"] as? JSONDictionary { | ||
thenText = thenTextComponent["text"] as? String |
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 key is being renamed to sub
.
# Conflicts: # MapboxDirections/MBVisualInstruction.swift
…nt with directions and active properties. Modified the mapping of text components for the visual instructions initializer.
|
||
If the value is `[.left", .straight]`, the driver can go straight or left from that lane. This is only set when the `component` is a lane. | ||
*/ | ||
@objc public var directions: [Any]? |
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.
Any
was used instead of ManeuverDirection
-- since this objc
declared property is an array of enum.
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.
How about renaming this and making it a lane indication: https://github.com/mapbox/MapboxDirections.swift/blob/master/MapboxDirections/MBLaneIndication.h
self.text = text | ||
self.imageURL = imageURL | ||
self.type = type | ||
self.abbreviation = abbreviation | ||
self.abbreviationPriority = abbreviationPriority | ||
if let directions = directions { | ||
self.directions = directions.map { ManeuverDirection(description: $0) ?? .none } |
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 don't think directions
is an array of ManeuverDirctions. Try using the LaneIndication
initializer here.
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.
@bsudekum yes (according to the recently published api-docs), when type == .lane
we do have an array of directions.
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.
@bsudekum checkout the new structure for lane and new directions field.
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.
@vincethecoder I'm saying that internally in this library, these are LaneIndications and not ManeuverDirections.
|
||
If multiple lanes are active, then it used to complete the upcoming maneuver. This value is set to `false` by default. | ||
*/ | ||
@objc public var active: Bool = false |
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 property name does not really convey the link between it's name and whether or not the lane usable.
/** | ||
The step does not have a particular visual instruction component type associated with it. | ||
|
||
This visual instruction component type is used as a workaround for bridging to Objective-C which does not support nullable enumeration-typed values. |
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.
No need to state this is work around, we do this elsewhere with other enums. Fine to say there is no visual component in this case.
…variable name, active. Removed unnecessary comment in visual instruction type.
|
||
If the value is `[.left", .straight]`, the driver can go straight or left from that lane. This is only set when the `component` is a lane. | ||
*/ | ||
@objc public var directions: LaneIndication |
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 should be an array of LaneIndication
.
edit: Oh it is, internally.
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.
Let's also rename this indications
to follow other patterns in this lib.
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.
@bsudekum Yep, it's already an array... 😸
/** | ||
The information about the next maneuver (the one after the upcoming maneuver). This detail gives the driver heads up when the current step is short. | ||
*/ | ||
@objc public let subInstruction: VisualInstruction? |
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.
Rename this property to subinstruction
(and elsewhere in this PR). Precedents for this include NSCompoundPredicate.subpredicates
.
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.
@1ec5 I understand your preference for subinstruction
based off, NSCompoundPredicate.subpredicates
.
We should go for consistency here.
In this context, it feels out of place when
subinstruction
variable reads differently from,primaryInstruction
andsecondaryInstruction
.
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.
“Sub” isn’t a word.
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.
@1ec5 okay, if that's your notion, then we're better off with a real English word like tertiary
--> tertiaryInstruction
. Which makes this consistent.
PS: The initial lowercase of the entire variable is less favorable.
@@ -21,6 +21,11 @@ open class VisualInstructionBanner: NSObject, NSSecureCoding { | |||
*/ | |||
@objc public let secondaryInstruction: VisualInstruction? | |||
|
|||
/** | |||
The information about the next maneuver (the one after the upcoming maneuver). This detail gives the driver heads up when the current step is short. |
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 visual instruction that is presented simultaneously to provide information about an additional maneuver that occurs in rapid succession.
/** | ||
An array indicating which directions you can go from a lane (left, right, or straight). | ||
|
||
If the value is `[.left", .straight]`, the driver can go left or straight from that lane. This is only set when the `component` is a `lane`. |
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.
There are a couple typos in the first sentence. How about:
If the value contains both
LaneIndication.left
andLaneIndication.straightAhead
…
/** | ||
An array indicating which directions you can go from a lane (left, right, or straight). | ||
|
||
If the value is `[.left", .straight]`, the driver can go left or straight from that lane. This is only set when the `component` is a `lane`. |
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 property is set to
nil
unless thetype
property is set toVisualInstructionComponentType.lane
.
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.
@1ec5 FYI, LaneIndication
is an array of lane indications. For obj-C compatibility, we shouldn't set this to an optional array.
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.
LaneIndication is an OptionSet, not an array. It just happens that Swift uses the same syntax for option sets as it does for arrays. We should say the property is set to the empty set (or 0 in Objective-C) in this case, and we should describe the property as an option set, not an array.
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.
Okay. Noted. In which case, we will indicate that this OptionSet is by default an empty set. Hence, we shall proceed to keep this as a type isActiveLane
as a type LaneIndication
.
|
||
If multiple lanes are active, then they can all be used to complete the upcoming maneuver. This value is set to `false` by default. | ||
*/ | ||
@objc public var isLaneActive: Bool = false |
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 we should strive for more similarities with how lane indications are normally exposed. It’s poor API design to expose multiple mutually exclusive properties that are optionally set depending on each other. Consider making VisualInstructionComponent an umbrella class or protocol for subclasses such as LaneInstructionComponent – isActive
can then be a property on that subclass.
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.
Better yet, instead of a bunch of lane instruction components, build up a new Intersection
object that can have a bunch of approachLanes
and usableApproachLanes
.
|
||
If multiple lanes are active, then they can all be used to complete the upcoming maneuver. This value is set to `false` by default. | ||
*/ | ||
@objc public var isLaneActive: Bool = false |
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.
Rename this property to isActiveLane
, or isActive
if you end up refactoring the class. Also refine the getter and setter names in Objective-C: -activeLane
should be the getter, and -setActiveLane:
should be the setter.
*/ | ||
@objc public init(type: VisualInstructionComponentType, text: String?, imageURL: URL?, abbreviation: String?, abbreviationPriority: Int) { | ||
@objc public init(type: VisualInstructionComponentType, text: String?, imageURL: URL?, abbreviation: String?, abbreviationPriority: Int, indications: LaneIndication = LaneIndication(), isLaneActive: Bool = false) { |
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 a lot of arguments for an initializer. Since these parameters aren’t always relevant, remove them and make the corresponding properties non-constant, so that the caller can set the properties after initializing the object.
@@ -6,6 +6,11 @@ import Foundation | |||
@objc(MBVisualInstructionComponentType) | |||
public enum VisualInstructionComponentType: Int, CustomStringConvertible { | |||
|
|||
/** | |||
The step does not have a particular visual instruction component type associated with it. |
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.
What is the developer supposed to do with a none
instruction? Hide it?
…d to the VisualInstructionComponent.
// MARK: Component/NSSecureCoding Protocols Variables | ||
@objc public var text: String? | ||
|
||
@objc public var type: VisualInstructionComponentType |
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.
These properties could use some documentation.
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.
Looks pretty good, but there are some things I thought I would point out.
let componentsDictionary = json["components"] as? [JSONDictionary] | ||
var components = [MBComponentRepresentable]() | ||
|
||
var maneuverType: ManeuverType = .none |
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 works, and it's not really a huge deal or anything, but IMHO argument requirements should be treated exactly the same when you initialize through coder as if you were to initialize for MBVisualInstruction.init(text:maneuverType:maneuverDirection:components:degrees)
.
Instead of assuming default values, I would consider making MBVisualInstruction.init(json:)
a fail-able initializer that returns nil if we don't find a value in the encoder for the required arguments of the designated initializer.
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.
@JThramer this pre-existing and basically refactored from https://github.com/mapbox/MapboxDirections.swift/pull/258/files/916531f27e054235e599a9f6fb9e9a8955533655#diff-361a2ac3a3f3ee2f7a0fe4bbdd15f1deL59
The deduction from the existing logic is, even if we provided a fail-able initializer, the nil value will also always return .none
-- so it's a choice between being proactive vs. reactive.
The component representable protocol that comprises what the instruction banner should display. | ||
*/ | ||
@objc(MBComponentRepresentable) | ||
public protocol MBComponentRepresentable: class, NSSecureCoding { } |
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.
Type prefixes in swift are somewhat of an anti-pattern, unless your team has a special reason (and convention) for using them. They were a convention in Objective-C because all namespaces in that language are global, but with swift's introduction of modules, this pattern is more or less deprecated. This is because even if you call your class UIViewController
, MyApp.UIViewController()
is different from UIKit.UIViewController()
.
Naming the OBJ-C bridge MBComponentRepresentable
is a good call though and should be kept.
/** | ||
A visual instruction that is presented simultaneously to provide information about an additional maneuver that occurs in rapid succession. | ||
*/ | ||
@objc public let tertiaryInstruction: VisualInstruction? |
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 good (abstracted because it's either a lane, or a turn component), but it may be nice to specifically call out how this property is used in our current implementation.
@objc public var indications: LaneIndication | ||
|
||
/** | ||
The boolean that indicates whether the component is a lane and can be used to complete the upcoming maneuver. |
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 since this is a LaneIndicationComponent, we already know it's a lane. I think this is more about whether the lane can be used to complete the maneuver.
} | ||
|
||
public func encode(with coder: NSCoder) { | ||
coder.encode(indications, forKey: "directions") |
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.
Rename this to coder.encode(indications, forKey: "indications")
let maneuverDirection = ManeuverDirection(description: json["modifier"] as! String) ?? .none | ||
let textComponents = (json["components"] as! [JSONDictionary]).map { | ||
VisualInstructionComponent(json: $0) | ||
let componentsDictionary = json["components"] as? [JSONDictionary] |
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.
Inline this line into the if let down below instead of pulling this out into a variable.
/** | ||
A visual instruction that is presented simultaneously to provide information about an additional maneuver that occurs in rapid succession. | ||
|
||
This instruction could either contain the visual layout information or the lane information about the next maneuver after the upcoming maneuver. |
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 instruction could either contain the visual layout information or the lane information about the upcoming maneuver.
XCTAssertEqual(visualInstructions?.first?.tertiaryInstruction?.maneuverDirection, .right) | ||
XCTAssertEqual(laneIndicationComponent.isUsable, true) | ||
XCTAssertEqual(laneIndicationComponent.indications, [.left, .straightAhead]) | ||
XCTAssertEqual(visualInstructions?.first?.drivingSide, .right) |
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.
So does this step have both lane information and then information? I think the implementation on the server prevents this from ever occurring.
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.
Will re-check the JSON response and relay my finding.
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.
Looking good, a few minor comments.
…ructions with Lane indications.
let step = route!.legs.first!.steps.first! | ||
let visualInstructions = step.instructionsDisplayedAlongStep | ||
|
||
let tertiaryInstruction = visualInstructions?.first?.tertiaryInstruction |
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.
@vincethecoder getting close! I think the final piece is to now add a test for a tertiaryInstruction where it's properties are non-nil. IE test for a then instruction instead of a lane instruction.
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.
…ructions. Refactored function name testTertiaryInstructions.
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 can't approve my own PR, but I approve.
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.
LGTM /cc @bsudekum
This parses out thethen
text component for displaying information about the upcoming step.UPDATES
This parses out the
sub
component for displaying information about the upcoming step. Also, the newlane
component type here provides additional lane information to assist visual representation./cc @lyzidiamond @danpaz @mapbox/navigation-ios