Architecture
CityScout Build Thread / Part 2 of 3
CityScout Architecture: Native iOS, FastAPI, and a Shared AI Backend
The current shape of CityScout is intentionally split: SwiftUI and SwiftData on device, FastAPI on the server, and OpenAI calls kept server-side. That gives the product a clearer security boundary and makes the AI behavior easier to change.
Contents
The app should hold the trip; the backend should hold the contract.
Native iOS As The Primary Surface
SwiftUI and SwiftData are a good fit for the core product because they keep the mobile experience close to the user's data. CityScout is designed as a travel companion, so the iPhone app needs to feel immediate, local, and dependable when the user is on the move.
That local-first shape matters. The app should be able to keep trip state, saved places, and user decisions on device first, then synchronize through the backend where it makes sense. The point is to reduce friction, not to build a cloud-first planning system that happens to run on a phone.
FastAPI Owns The AI Boundary
FastAPI sits between the app and OpenAI so the model never becomes a client-side dependency. That lets the server manage prompts, validation, retries, logging, and response shaping in one place, instead of scattering those concerns across the device.
The current backend boundary also supports shared-secret auth, which keeps the client from carrying sensitive credentials. That is a practical security choice, and it also keeps the mobile app simpler. The app sends intent; the backend decides how to turn that intent into itinerary generation or guide chat.
Structured Endpoints Beat Free-Form Output
CityScout now has dedicated itinerary generation and guide chat endpoints, which is the right shape for a product that needs both structure and explanation. Itineraries are not just conversation transcripts. They are durable objects that can be saved, reviewed, and reused.
- Itinerary output should stay structured enough to serialize and validate.
- Guide chat should explain and support, not replace the trip model.
- Seeded JSON city content should make the app useful before generation runs.
- The backend should normalize model output into a stable contract for the client.
What Stays Future Work
The planned Next.js web planning layer belongs in the future, not the current shipped product. It makes sense as a secondary surface for planning and sharing, but it should reuse the same API contract instead of creating a separate version of the truth.
That future layer only works if the contract stays stable. If the backend schema drifts, the product starts to lose the simplicity that makes the current split between client, server, and model manageable.