直接定义:移动应用后端要解决的,除了和普通 Web 一样的认证与数据,还多了三件移动特有的事——弱网下的离线、系统推送通知、和商店的登录合规。Supabase 的 SDK 原生覆盖 iOS/Android,这一节我们讲它怎么当移动后端,以及离线、推送怎么接。
下面 SVG 先给出移动端的整体架构,看清"本地队列—同步—推送"三层怎么摆:

iOS 用 supabase-swift,Android 用 supabase-kotlin,调用形态和 Web 一致。登录同样拿到 JWT,查询走同一套 RLS。
// iOS (Swift) import Supabase let client = SupabaseClient(supabaseURL: URL, supabaseKey: anonKey) try await client.auth.signIn(email: "a@b.com", password: "secret") let todos = try await client.from("todos").select().execute().value as [[String: Any]]
// Android (Kotlin) val supabase = createSupabaseClient(supabaseUrl, anonKey) { install(GoTrue) ; install(Postgrest) } supabase.auth.signInWith(Email) { email = "a@b.com"; password = "secret" } val todos = supabase.from("todos").select().decodeList<Todo>()
注意:移动端持久化登录态由 SDK 处理,App 重启后自动恢复会话,不用自己存 token。
移动网络不稳,好的体验是"操作先落本地、联网再同步"。Supabase 的实时和幂等写入能支撑最终一致。做法是本地队列存待同步操作,恢复网络后重放:
// 伪代码:离线队列重放(概念,非特定 SDK) async function flushQueue() { for (const op of localQueue) { await supabase.from(op.table).insert(op.row) // 幂等靠唯一约束 localQueue.remove(op) } }
关键在于服务端表要有幂等保障(如唯一约束),重放不会产生重复行。
-- 用唯一约束让重放安全 create table public.sync_items ( id uuid primary key default gen_random_uuid(), client_uuid uuid unique, -- 客户端生成的去重键 payload jsonb );
Supabase 本身不发 APNs/FCM 推送,但可以用 Edge Function 在事件发生时调用推送服务(密钥存函数环境变量)。下面用触发器 + outbox + 函数做"新消息推通知":
-- outbox 记录待推送事件 create table public.push_outbox ( id bigint generated always as identity primary key, user_id uuid, title text, body text, sent boolean default false );
// Edge Function: 读 outbox 调 FCM(示意,密钥在服务端) Deno.serve(async () => { const admin = createClient(URL, SERVICE_ROLE) const { data: pending } = await admin .from('push_outbox').select('*').eq('sent', false).limit(100) for (const p of pending) { await fetch('https://fcm.googleapis.com/...', { /* 调推送 */ }) await admin.from('push_outbox').update({ sent: true }).eq('id', p.id) } return new Response('done') })
背景:一个健身 App 用户在地铁里打卡,网络断断续续,不能因离线丢记录。
操作过程:
synced=false。insert 到 Supabase 的 workouts 表,表带 client_uuid 唯一约束防重。workouts 的 RLS 保证用户只能写自己的行。create table public.workouts ( id bigint generated always as identity primary key, user_id uuid references auth.users(id), client_uuid uuid unique, kind text, done_at timestamptz ); alter table public.workouts enable row level security; create policy "自己写自己" on public.workouts for all using (auth.uid() = user_id) with check (auth.uid() = user_id);
结果:地铁里断网照常打卡,恢复后无缝同步,重复提交被唯一约束挡掉,统计实时更新。
解读:移动后端的核心不是"多写接口",而是"离线不丢 + 同步幂等 + 权限在库"。Supabase 用 RLS + 唯一约束 + 实时把这三点都覆盖了,移动端只管本地队列。
变式:若推送量巨大,别用函数轮询 outbox,改由消息队列触发函数,函数只处理单条事件,扩展性更好。小体量用定时函数扫 outbox 足够。
⚠️ 移动端千万别把 service_role 打进 App 包。App 包可被反编译提取密钥,等于把数据库钥匙发给每个用户。推送等需要管理员权的逻辑,一律走服务端函数。
💡 我们建议离线同步一律加客户端生成的去重键(唯一约束),否则弱网下重试会产生重复业务记录。这是移动后端最容易翻车的点,比选哪个 SDK 更重要。
下一节看游戏后端:玩家档案、排行榜、实时对战,Supabase 能覆盖多少、哪部分要自己造。