TLDR:
SupabaseClientgot the URL and anon key swapped. Swift compiled it without complaint. Everything failed silently. The fix was one line.
What We Were Building
We'd just spun up a native iOS companion for our internal CRM (Key Opinion Leader relationship tracker — think webinar pipeline management, not a spreadsheet).
The web app was humming on Next.js. Now we wanted a real iOS client backed by the same Supabase project.
First session. Fresh Xcode project. Wire up Supabase, hit the ground running.
Except nothing ran.
The Wall
Contacts didn't load. Saves didn't save. No crash. No red error banner.
Just… silence.
That's the worst kind of bug — the kind that doesn't scream at you. It just sits there politely returning empty data while you doubt everything you've built.
I checked the Supabase dashboard. Tables were there. RLS policies looked fine. The project URL was right.
What I Tried First (Wrong)
My first instinct was the key format.
Supabase has two key formats you'll encounter — the legacy anon key and the newer publishable sbp_... format. I figured maybe I'd grabbed the wrong one.
Swapped the key. Same silence.
Then I went deeper. Ran a full audit on the Swift models — all the DB-nullable fields needed to be Optional, the JSONDecoder needed keyDecodingStrategy = .convertFromSnakeCase, and I had a decoder/encoder config that was quietly conflicting with CodingKeys. Fixed all of it.
Still nothing.
The Fix That Actually Worked
Stared at the SupabaseClient initializer.
SupabaseClient(
supabaseURL: URL(string: supabaseKey)!,
supabaseKey: supabaseURL
)
There it is.
The constants were swapped. The URL was going in as the key, and the key was going in as the URL. Swift didn't blink. It compiled, ran, and handed Supabase a JWT where it expected an HTTPS URL — and a project URL where it expected an auth token.
The fix:
SupabaseClient(
supabaseURL: URL(string: supabaseURL)!,
supabaseKey: supabaseKey
)
Connection came up immediately. Contacts loaded. Saves worked.
Why This Matters
Swift's labeled parameters give you a false sense of safety.
You see supabaseURL: and supabaseKey: and you think — great, the compiler will catch it if I mix these up. It won't. Both boil down to String in your constants file. The labels only enforce what you pass TO them, not whether the values you defined are logically correct.
The real lesson: don't define your Supabase credentials as generic strings and then infer which is which by variable name. Name them unmistakably in your constants file. Better yet — type-alias or wrap them so a SupabaseURL literally cannot be assigned to a SupabaseKey.
We lost a solid hour to this. One swapped pair of strings, zero compiler warnings, and a whole screen of empty state.
Never again.
P.S. Once the connection was live, the next thing to check was RLS. If your helpers create a fresh
SupabaseClientinternally instead of receiving the session-bound client from the caller — that's a whole other silent-failure trap. Different bug, same infuriating empty data symptom.