A SwiftUI travel app with a chat feature for modifying your itinerary. The app talks to Claude for schedule changes — and the interesting part is what Claude does without being told to.
"Move the Seine River Cruise to 9 AM."
⚠️ This conflicts with the Eiffel Tower visit at 9:00 AM. Consider rescheduling one of these activities.
"Move the Seine River Cruise to 9 PM."
✓ Moving the Seine River Cruise to 9:00 PM. This works well — your evening is free after dinner.
Conflict detection, confirmation logic, rebooking suggestions — none of that is in the code.
The modification schema (Wanderlust/Services/ClaudeService.swift, lines 83-101):
{
"type": "modify" | "add" | "delete",
"changes": {
"time": "<new time or null>",
"place": "<new place or null>",
"what": "<new description or null>",
"priority": "mustDo" | "flexible" | "skip" | null
},
"warnings": ["<any concerns about the request>"]
}The code that applies a time change (ChatView.swift:228):
if let time = modification.changes.time { activity.time = time }No validation. No time comparison. Just assigns the value.
- Self-describing field names.
timeandplacearen't random keys — the model understands what they represent. - Full schedule as context. The app sends the entire day's itinerary with each message, so the model can cross-reference.
- Open-ended warnings. The
warningsarray says "any concerns about the request." It doesn't define what a concern is. The model decides.
The schema is a contract for structure. The intelligence comes from the model reasoning over the data it's given.
grep -ri "conflict|overlap|collision|clash" Wanderlust/
grep -ri "isBefore|isAfter|overlaps|intersects" Wanderlust/
grep -ri "detect|check.*time|validate.*time" Wanderlust/"conflict" only appears in code comments about naming collisions. No time comparison logic anywhere. The only warning-related code is displaying warnings from the API — not generating them.
| File | What It Does |
|---|---|
Wanderlust/Services/ClaudeService.swift |
Claude API integration. Prompt at lines 76-110. |
Wanderlust/Models/DTOs.swift |
The data structures Claude sees. |
Wanderlust/Views/Screens/ChatView.swift |
Chat UI and modification handling. |
- Xcode 16+
- A Claude API key
- Clone this repo
- Create
Wanderlust/Secrets.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CLAUDE_API_KEY</key>
<string>your-key-here</string>
</dict>
</plist>- Build and run
Go to the Chat tab and try these:
The key test (Day 1 - Paris):
- "Move the Seine River Cruise to 9 AM" → should warn (Eiffel Tower conflict)
- "Move the Seine River Cruise to 9 PM" → should confirm it's fine
Other conflicts:
- "Move the Louvre to 9 AM" (conflicts with Eiffel Tower)
- "Move the Van Gogh Museum to 9 AM" (Day 4 - conflicts with Rijksmuseum)
Schedule changes:
- "I'm tired, skip the canal cruise"
- "Make the Tower of London a must-do"
- "What if I want to sleep in on Day 2?"
Stress tests:
- "Move everything to 3 PM"
- "Delete all the museums"
- "Add a trip to the moon"
MIT Licensed.