TLDR: Supabase rolled out new-format API keys. The anon key is now sb_publishable_..., not the old eyJ... JWT. If you're bootstrapping a Swift project from an old example and it fails silently — check your key first.

the setup

I'm building a native iOS KOL CRM — to manage Key Opinion Leaders (KOLs, like doctors and media partners) for a health business's webinar operation.

Supabase (my go-to hosted Postgres + auth backend) is the data layer.

I wired up the Swift client, hit build… and it compiled. ✓

Forms appeared. Taps registered. Everything looked fine.

what broke

Nothing crashed.

That's the worst kind of bug.

Forms submitted and dismissed as if everything saved — but the database was empty. Relationship owners weren't populating. Just silence, everywhere.

what I tried first

I started with the obvious stuff.

Checked my SupabaseClient initializer — and sure enough, I'd cross-wired the URL and the key. Two string constants, swapped. Builds perfectly clean, fails at runtime without a peep.

Fixed that. Still broken in spots.

Then I ran a full nullable audit — any DB column that can be NULL has to be Optional in Swift, or the decoder silently drops entire rows. Fixed that too.

Still not right.

the fix that actually worked

I stared at my supabaseKey constant for a long moment.

I'd copied the anon key from an older project. It started with eyJ... — the old JWT format Supabase used to issue for every project.

Supabase changed the format.

New projects get keys that look like sb_publishable_... for the anon/public key, and sb_secret_... for the service-role key. Totally different shape. The client initializes without throwing — it just fails at the network layer, SILENTLY, on every single request.

I swapped in the correct key from the dashboard. Everything came alive instantly.

why this one matters

Three separate bugs in one session… and not one of them threw an error.

So what do three silent failures tell you? That "it compiles" means absolutely nothing until you've verified a real write landed in the database.

The lesson I keep re-learning: when a form "works" but saves nothing, the bug is almost never in the UI. It's in how you authenticated before the very first call was ever made.

Check your key. Check its format. And if you're copying from an old project — go get a fresh key from the Supabase dashboard instead. Don't trust memory.

P.S. The new format applies to JS too — @supabase/supabase-js and @supabase/ssr both use sb_publishable_... as the anon key now, not eyJ.... Same trap, same fix.