Skip to content

Instantly share code, notes, and snippets.

@gshaw
Created March 19, 2026 22:23
Show Gist options
  • Select an option

  • Save gshaw/95d6feb4994f5b8bb8c8932b0636a0e0 to your computer and use it in GitHub Desktop.

Select an option

Save gshaw/95d6feb4994f5b8bb8c8932b0636a0e0 to your computer and use it in GitHub Desktop.

CurrentRouteState: points vs waypoints Assessment

Summary

DrawingPoint (persisted source of truth) and Waypoint (derived view-model) serve distinct roles. Merging them is not recommended.

How They Relate

CurrentRouteState.points is [DrawingPoint]. Whenever points is set, waypoints is automatically rebuilt via a didSet:

var points: [DrawingPoint] = [] {
    didSet {
        waypoints = BuildWaypoints.call(routePoints: points)
        isSaved = false
        objectWillChange.send()
    }
}

What Each Type Contains

Property DrawingPoint Waypoint
id / drawingPointID ✅ (mirrors DrawingPoint id)
coordinate ✅ (copied)
name ✅ (copied)
index
kind (start/turn/end) ✅ (derived from position)
pinLabel ("A", "B") ✅ (derived from index)
pinDescription ✅ (derived from name + kind)
pinColor ✅ (currently always .track)
Codable

Why Merging Would Be Difficult

  1. DrawingPoint is Codable and persisted — saved to the database and to JSON. Adding derived display properties would mean either making them Codable too (wasteful) or adding custom coding logic.

  2. DrawingPoint is shared with tracksDrawing uses DrawingPoint for both routes and tracks. The Waypoint concept (start/turn/end, pin labels) is route-specific. Merging would pollute the track model.

  3. Waypoint is used as an Equatable change trigger.onChange(of: currentRouteState.waypoints) in MapViewMainMapView fires HandleWaypointsChanged to rebuild routes. This works because waypoints are rebuilt as new value types on every points change. Computed properties would break this change-detection mechanism.

  4. 14+ consumers read waypoints for display — carousel, table, map pins, navigation logic all expect the Waypoint type.

Known Pitfall: Derived State Not Rebuilt on Restore

navigableLine is derived from segments (via BuildNavigableLine) and was not being rebuilt when CurrentRouteState.restore() loaded persisted data. This caused ShowOnMap.flyAndZoomToCurrentRoute to silently fail after app restart because navigableCoordinates returned [].

Fix: Call rebuildNavigableLine() at the end of restore() to reconstruct the LineString from the already-persisted segments.

Conclusion

The two-type split is the right design. DrawingPoint is the persisted, shared, mutable data model. Waypoint is a route-specific, derived, immutable view-model. The didSet derivation pattern is non-obvious but correct — the key is ensuring all derived state (like navigableLine) is also rebuilt on restore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment