Repository navigation
Architecture
Chirpy uses SwiftUI and Observation for the app, an injected HTTP client for networking, and Supabase Edge Functions backed by Postgres and Storage.
flowchart TD
Views["Views"] --> Client["ChirpyClient"]
Client --> HTTP["AuthenticatedHTTPClient + URLSession"]
HTTP --> API["Supabase Edge Functions"]
API --> Database["Postgres functions and tables"]
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.
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.
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.
- 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.