Document public API for drag-drop integration: - isValidPosition() for position validation - validDayRange() for precomputing valid days - barrierGames() for visual highlighting - Integration patterns for ItineraryTableViewController Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
4.5 KiB
4.5 KiB
ItineraryConstraints API
Location: SportsTime/Core/Models/Domain/ItineraryConstraints.swift
Verified by: 22 tests in SportsTimeTests/Domain/ItineraryConstraintsTests.swift
Overview
ItineraryConstraints validates item positions during drag-drop operations. It enforces:
- Games cannot move (CONS-01)
- Travel segments have day range limits (CONS-02)
- Travel segments must respect game sortOrder on same day (CONS-03)
- Custom items have no constraints (CONS-04)
Construction
let constraints = ItineraryConstraints(
tripDayCount: days.count,
items: allItineraryItems // All items including games
)
Parameters:
tripDayCount: Total days in trip (1-indexed, so a 5-day trip has days 1-5)items: All itinerary items (games, travel, custom). Games are used to calculate constraints for travel items.
Public API
isValidPosition(for:day:sortOrder:) -> Bool
Check if a specific position is valid for an item.
func isValidPosition(for item: ItineraryItem, day: Int, sortOrder: Double) -> Bool
Usage during drag:
// On each drag position update
let dropPosition = calculateDropPosition(at: touchLocation)
let isValid = constraints.isValidPosition(
for: draggedItem,
day: dropPosition.day,
sortOrder: dropPosition.sortOrder
)
if isValid {
showValidDropIndicator()
} else {
showInvalidDropIndicator()
}
Returns:
true: Position is valid, allow dropfalse: Position is invalid, reject drop (snap back)
Rules by item type:
| Item Type | Day Constraint | SortOrder Constraint |
|---|---|---|
.game |
Always false |
Always false |
.travel |
Within valid day range | After departure games, before arrival games |
.custom |
Any day 1...tripDayCount | Any sortOrder |
validDayRange(for:) -> ClosedRange<Int>?
Get the valid day range for a travel item (for visual feedback).
func validDayRange(for item: ItineraryItem) -> ClosedRange<Int>?
Usage at drag start:
// When drag begins, precompute valid range
guard case .travel = draggedItem.kind,
let validRange = constraints.validDayRange(for: draggedItem) else {
// Not a travel item or impossible constraints
return
}
// Use range to dim invalid days
for day in 1...tripDayCount {
if !validRange.contains(day) {
dimDay(day)
}
}
Returns:
ClosedRange<Int>: Valid day range (e.g.,2...4)nil: Constraints are impossible (e.g., departure game after arrival game)
barrierGames(for:) -> [ItineraryItem]
Get games that constrain a travel item (for visual highlighting).
func barrierGames(for item: ItineraryItem) -> [ItineraryItem]
Usage for visual feedback:
// Highlight barrier games during drag
let barriers = constraints.barrierGames(for: travelItem)
for barrier in barriers {
highlightAsBarrier(barrier) // e.g., gold border
}
Returns:
- Array of game items: Last departure city game + first arrival city game
- Empty array: Not a travel item or no constraining games
Integration Points
ItineraryTableViewController (existing)
// In reloadData()
self.constraints = ItineraryConstraints(tripDayCount: tripDayCount, items: itineraryItems)
// In drag handling
if constraints.isValidPosition(for: draggedItem, day: targetDay, sortOrder: targetSortOrder) {
// Allow drop
} else {
// Reject drop, snap back
}
Phase 4 Implementation Notes
-
Drag Start:
- Check
item.isReorderable(games returnfalse) - Call
validDayRange(for:)to precompute valid days - Call
barrierGames(for:)to identify visual barriers
- Check
-
Drag Move:
- Calculate target (day, sortOrder) from touch position
- Call
isValidPosition(for:day:sortOrder:)for real-time feedback - Update insertion line (valid) or red indicator (invalid)
-
Drag End:
- Final
isValidPosition(for:day:sortOrder:)check - Valid: Update item's day/sortOrder, animate settle
- Invalid: Animate snap back, haptic feedback
- Final
Test Coverage
| Requirement | Tests | Verified |
|---|---|---|
| CONS-01 (games cannot move) | 2 | Yes |
| CONS-02 (travel day range) | 3 | Yes |
| CONS-03 (travel sortOrder) | 5 | Yes |
| CONS-04 (custom flexibility) | 2 | Yes |
| Edge cases | 8 | Yes |
| Success criteria | 3 | Yes |
| Barrier games | 1 | Yes |
| Total | 22 | 100% |
API documented: Phase 02 Ready for: Phase 04 (Drag Interaction)