Dev Patel
Nov 2025 → Feb 2026Source

Epiphany & ShakeRage

Two SwiftUI apps built back to back — one widget-first capture tool, one motion-driven SceneKit game.

ShakeRage — CoreMotion turns shake intensity and duration into a score.
Type
Artifact
Where
Personal
When
Nov 2025 → Feb 2026
Duration
3 mo
Status
Shipped
Source
GitHub
The problem

The problem

I had written a lot of backend and very little native mobile. Two apps in one push was a deliberate choice: one that lives mostly outside the app, and one that is nothing but the app.

What I built

What I built

Epiphany is built around WidgetKit — the point is capturing a thought from the home screen without opening anything. SwiftData handles local persistence and App Groups share it with the widget, so content syncs even when the app is never foregrounded. A structured ThemeModel drives custom visual themes, and StoreKit 2 runs a full paywall with purchase restoration and entitlement verification behind a freemium tier.

ShakeRage is the opposite. CoreMotion's CMMotionManager reads real-time device acceleration and turns shake intensity and duration into a competitive score, rendered through SceneKit. Supabase provides auth with JWT session handling, token refresh and Keychain-backed credential storage, and a live leaderboard that syncs over realtime subscriptions rather than polling.

Both follow MVVM over ObservableObject and StateObject, which is the part that actually made two apps in one cycle possible.

Screens

Screens

Sign-in, with Supabase auth and Keychain-backed credentials.
Sign-in, with Supabase auth and Keychain-backed credentials.
Epiphany — one thought at a time, the same view WidgetKit renders on the home screen.
Epiphany — one thought at a time, the same view WidgetKit renders on the home screen.
Epiphany's premium tier, with the free version left intact behind it.
Epiphany's premium tier, with the free version left intact behind it.
Results

Results

2apps shipped
<100msleaderboard queries
100+curated entries
  • Two native apps shipped from an empty Xcode project inside one development cycle.
  • Widget capture works without ever foregrounding the app, via App Groups and SwiftData.
  • Median leaderboard read under 100 ms, from indexed queries plus client-side caching, synced over realtime subscriptions rather than polling.
  • Full StoreKit 2 paywall with restoration and entitlement verification.
Architecture

Architecture

EpiphanyWidgetKit captureShakeRageSceneKit · CoreMotionSwiftUI app layershared componentsSupabaseauth + syncPostgreSQLstorage
Two very different clients over one shared identity and storage layer.
In hindsight

In hindsight

Sharing an auth layer across two apps with genuinely different session needs was the right instinct and the wrong abstraction — a game session and a capture session want different lifetimes. I'd split the token handling and keep only the identity layer common.

Stack

Stack

SwiftUISwiftDataWidgetKitSceneKitCoreMotionStoreKit 2SupabasePostgreSQLKeychain