Gokon: iOS Event Planning App (SwiftUI + Flask)

Gokon is an event planning app I built for iPhone. The client is a SwiftUI app with SwiftData local storage and actor-based networking, and the server is a Flask API with PostgreSQL. The core idea is simple: profile + friends + events + RSVP, all synced through one snapshot endpoint.
Client SwiftUI, SwiftData, Swift Concurrency actors
Server Flask, SQLAlchemy, Flask-Migrate, PostgreSQL
Sync Model Snapshot pull: profile + friends + organized/participating events

What Gokon Does

Gokon handles a small but complete social event flow: create your profile, share an invite code to add friends, create events, add participants, and track RSVP states (Pending, Accepted, Declined). The iOS app keeps a local SwiftData graph so the UI stays fast while still syncing with server state.

Repositories: gokon-ios-client, gokon-server.

System Architecture

[iPhone SwiftUI App] -> actor APIClient (Bearer token from Keychain) -> Flask REST API (Blueprint routes) -> SQLAlchemy models -> PostgreSQL Sync path: /snapshot/<uuid> -> profile + friends + organizedEvents + participatingEvents -> merge into SwiftData -> SwiftUI @Query updates UI

iOS App Architecture (SwiftUI + SwiftData + Actors)

The app entry point sets a SwiftData model container for User, Event, and EventResponse. The main screen is a tab layout for Events, Friends, and My Profile. If no local/authenticated user exists yet, the profile tab shows the first-user creation form.

The networking layer is intentionally strict: APIClient is an actor, endpoint definitions are centralized in APIEndpoint, and typed DTOs in Networking/Models/APIModels.swift keep request/response boundaries explicit.

App auth state is stored in Keychain via AuthService and KeychainHelper (token, user UUID, invite code). That keeps credentials out of plain persisted app models.

Backend Architecture (Flask + SQLAlchemy + PostgreSQL)

The Flask server is set up with an app factory and split into blueprints: user, friend, event, and RSVP routes. Token auth is handled through the Authorization: Bearer ... header and looked up against the user auth token in storage.

Data modeling is relation-heavy by design: users have self-referential many-to-many friendships, events have many-to-many organizers and participants, and RSVP responses are separate rows connecting users and events with an enum status.

For local/dev deployment, I run PostgreSQL + Flask (gunicorn) + Nginx using Docker Compose, with migrations handled by Flask-Migrate/Alembic.

Snapshot Sync Strategy

Instead of many client fetch calls, the app uses a single snapshot endpoint (/snapshot/<uuid>) to retrieve the full current user state: profile, friends, organized events, and participating events (including the caller’s RSVP view for those events).

SyncService orchestrates the flow, and UserRepository.syncSnapshotToSwiftData merges DTOs into existing SwiftData objects. This simplifies consistency rules in the UI and makes manual sync/pull-to-refresh straightforward.

Auth and Security

The project currently uses server-issued bearer tokens and keychain-backed storage on device. Profile/event mutation endpoints enforce either ownership or organizer permissions server-side. Invite-code friend adds are simple by design for quick onboarding.

Photo upload is wired with multipart form-data and currently stores a placeholder URL path on the server side, with comments showing where object storage would be inserted for production.

Deployment/Dev Stack

Backend: Python 3.11, Flask, SQLAlchemy, gunicorn, PostgreSQL 16, Nginx, Docker Compose. Client: SwiftUI + SwiftData with native concurrency and typed API DTOs. The same API shape powers event creation, participant management, and RSVP updates across the app.

What I’d Improve Next

  1. Add delta sync and conflict resolution so full snapshots are not required every time.
  2. Add queued offline writes (with replay) for RSVP/profile edits when connectivity drops.
  3. Harden auth with token expiry/refresh and clearer session invalidation behavior.
  4. Move photo handling to real object storage and signed URL delivery.