TL;DR: In Swift, named parameters to struct memberwise initializers must match declaration order — not the order that looks logical to you. Get it wrong inside SupabaseClientOptions and your client quietly misbehaves or won't compile at all.

The Build

I spun up a native iOS KOL CRM (key-opinion-leader contact manager, think "Salesforce lite for influencer relationships") for an ecommerce business — 23 Swift files, SwiftUI with @Observable, Google OAuth via supabase-swift, the works.

Wired up SupabaseService.swift the night it started. Day two: nothing works.

The First Wall (The Obvious One)

The supabaseKey was… a monster.

Somewhere in the copy-paste, I'd built a Frankenstein JWT — a real anon key header and payload, then the new sb_publishable_ key grafted on as the signature segment. It looked plausible. Supabase did not think so.

// what was in the file 😬
supabaseKey: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJz...sb_publishable_qndNQRphvquAw6iUSIdt1w_ysOkTxdl"

"Invalid API key" on every Google OAuth attempt. Fixed it by just using the publishable key alone — no JWT wrapper, no splicing. Classic.

The Second Wall (The One That Teaches You Something)

Fixed the key. Still broken.

The options block for SupabaseClient looked totally reasonable to me:

options: .init(
    auth: .init(
        redirectToURL: ...,
        emitLocalSessionAsInitialSession: true
    ),
    db: .init(
        decoder: { ... }(),
        encoder: { ... }()
    )
)

The problem? Swift struct memberwise initializers require parameters in declaration order. Not the order that reads nicely. Not the order you'd organize them if you wrote it yourself. The order the SDK declared them internally.

SupabaseClientOptions declares db before auth. DatabaseOptions declares encoder before decoder.

I had both backwards.

The fix was two swaps — db: before auth:, encoder: before decoder::

options: .init(
    db: .init(
        encoder: { ... }(),
        decoder: { ... }()
    ),
    auth: .init(
        redirectToURL: ...,
        emitLocalSessionAsInitialSession: true
    )
)

App connected. Auth worked. Two swaps.

Why This Catches You

If you come from Python — where keyword arguments are fully reorderable — or JavaScript — where object keys have no enforced order — Swift's behavior here feels backwards. You HAVE named labels. Why should order matter?

It matters because Swift's auto-generated memberwise initializers aren't keyword arguments in the Python sense. They're positional arguments with labels. Correct label, wrong position → compile error (or worse, silent type mismatch if the types happen to align).

The Supabase Swift SDK doesn't document parameter order in its getting-started examples. Neither does the error message explain it clearly. You just stare at options that look right until you diff them against the struct definition.

The Lesson

Whenever you're nesting config structs in a Swift SDK you don't own — check declaration order, not logical order.

My rule now: if something's broken in SupabaseClientOptions and the values look correct, I go read the struct definition in the supabase-swift package source before I change anything else. Five seconds in the SDK beats 90 minutes of wrong guesses.

P.S. The key format issue and the argument order issue hit the same afternoon, same file, two hours apart. Both completely silent failures until they weren't. Supabase is great — but it will not hold your hand on setup day.