Skip to content

Architecture

Samuel Yanez edited this page Oct 11, 2026 · 8 revisions

Architecture

Chirpy uses SwiftUI and Observation for the app, an injected HTTP client for networking, and Supabase Edge Functions backed by Postgres and Storage.

Request flow

flowchart TD
    Views["Views"] --> Client["ChirpyClient"]
    Client --> HTTP["AuthenticatedHTTPClient + URLSession"]
    HTTP --> API["Supabase Edge Functions"]
    API --> Database["Postgres functions and tables"]
Loading

ChirpyApp creates one client. ChirpyRootView gives Home, Search, and Profile separate navigation stacks and shares a CurrentProfileStore through the environment. That store loads the server-selected identity and supplies the Profile header and deletion ownership checks.

App state

The observable stores and view models run on the main actor. Each tab owns its loaded posts; sharing the current profile does not synchronize post lists across tabs.

Piece Responsibility
CurrentProfileStore Load, retry, and refresh the shared identity
FeedViewModel Home loading, refresh, pagination, and likes
SearchViewModel Results for one fixed query, pagination, and likes
ProfileViewModel Posts for the profile ID supplied by the shared store
PostListContent Deduplicate pages, apply likes, remove posts, and detect the pagination threshold
PostComposerView Draft, text validation, and submission errors
PostDeletionModel Deletion request state and errors for one detail screen

Search waits 300 milliseconds after input changes. A new query gets a new model, so an older response cannot replace its results. Successful creation similarly replaces Home's model and reloads the feed.

The app requests 20 posts per page and starts loading more near the last five rows. The backend orders posts by timestamp and UUID and returns an opaque cursor. Request guards prevent overlapping loads; cancellation and cursor checks keep stale results from being applied. Failed refreshes keep existing content, and invalid cursors trigger a first-page refresh.

Profile loads its header from the shared store and its posts separately. A posts failure keeps the header visible. Pull-to-refresh refreshes the identity, then its posts.

Post detail resolves a UUID from the originating tab's loaded list. Likes use the server's returned state and count, so the row and detail agree. Deletion requires confirmation, removes the post from that list, and excludes it from older in-flight responses. Other tabs pick up changes on refresh.

Networking and backend

ChirpyServicing lets views, tests, and previews use interchangeable clients. HTTPClient separates requests from transport. AuthenticatedHTTPClient adds X-Chirpy-API-Key, and ChirpyClient handles JSON, timestamps, and API errors. AppConfiguration provides the base URL and reads the key from a Git-ignored plist.

The feed, posts, search, profile, and social-feed Edge Functions share a handler and repository. The handler validates requests; the repository reads the current profile and calls restricted Postgres functions for feed, search, creation, deletion, and likes. The service-role key stays on the server. Storage hosts public avatars and post images.

Code and tests

  • Features — screens and view models.
  • Models — posts, profiles, shared identity, and list content.
  • Networking — API client and HTTP transport.
  • ChirpyTests — Swift Testing with fake clients.
  • supabase — functions, migrations, SQL checks, and demo assets.

See Development and Backend for test commands and setup.

Clone this wiki locally