Architecture

CityScout Build Thread / Part 2 of 4

CityScout Architecture: Native iOS, FastAPI, and a Shared AI Backend

April 27, 20269 min read

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.

Reading note

A clearer write-up of the product thinking, system choices, and tradeoffs behind the build.

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: trip state, saved places, and user decisions live on device first, then sync through the backend where it makes sense. The point is reducing friction, not building 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 boundary also supports shared-secret auth, so the client never has to carry sensitive credentials. It's a practical security choice that also keeps the mobile app simpler: the app sends intent, and the backend decides how to turn that into itinerary generation or guide chat.

Structured Endpoints Beat Free-Form Output

CityScout now has dedicated itinerary generation and guide chat endpoints — the right shape for a product that needs both structure and explanation. Itineraries aren't just conversation transcripts. They're durable objects you can save, review, and reuse.

  • 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 only works if the contract stays stable — if the backend schema drifts, the product loses the simplicity that makes the current split between client, server, and model manageable.