Supabase 配置
搭建后端:数据库 schema、认证回调、Edge Functions 与密钥。
Supabase 是本模板的后端——Postgres、Auth、Storage 与 Edge Functions。本页带你从一个空 项目走到一个完全可用的后端,认证、待办、上传、订阅与推送都从它读取数据。
模板并未附带完整的 schema。 supabase/migrations/ 只包含
billing_entitlements.sql(subscriptions + payments 两张表)。应用读取的 todos、
profiles、device_tokens 表不在模板中——README 指向兄弟仓库 soar-expo,但
device_tokens 在那里也不存在。本页在下方直接内联给出全部五张表的完整 SQL,省去你四处
翻找。
1. 创建项目并获取密钥
在 app.supabase.com 创建项目。然后在
Project Settings → API 中复制以下值到你的 Secrets.xcconfig:
SUPABASE_URL = https:/$()/your-project-ref.supabase.co
SUPABASE_PUBLISHABLE_KEY = your-publishable-anon-key(https:/$()/ 写法见配置。)应用在
SupabaseClientProvider 中惰性构建客户端;这两个值设好后,所有依赖后端的功能即上线。
2. 注册认证回调
OTP 与 OAuth 回调通过自定义 URL scheme 返回应用。在测试注册、magic-link 或社交登录之前, 到 Authentication → URL Configuration → Redirect URLs 把下面这条加入白名单:
soar://auth-callback该 scheme 已在 SoarStarterSwift/Info.plist(CFBundleURLTypes)中注册。没有这条白名单,
验证链接与 OAuth 往返都会走不通。参见身份认证。
3. 应用数据库 schema
在控制台 SQL Editor 里运行下面的 SQL(或作为迁移添加)。每条语句都带
if not exists / create or replace 守卫,可重复应用而不产生副作用——在桌面端模板已建过表
的项目上运行也安全。
共享触发器 + profiles
profiles 会由注册时的触发器自动填充。
create or replace function public.set_updated_at()
returns trigger language plpgsql as $$
begin new.updated_at = now(); return new; end;
$$;
create table if not exists public.profiles (
id uuid primary key references auth.users(id) on delete cascade,
full_name text,
bio text,
phone text,
location text,
avatar_url text,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
alter table public.profiles enable row level security;
create policy "Users can view own profile"
on public.profiles for select using (auth.uid() = id);
create policy "Users can update own profile"
on public.profiles for update using (auth.uid() = id);
create policy "Users can insert own profile"
on public.profiles for insert with check (auth.uid() = id);
create or replace function public.handle_new_user()
returns trigger language plpgsql security definer as $$
begin
insert into public.profiles (id, full_name)
values (new.id, new.raw_user_meta_data ->> 'full_name');
return new;
end;
$$;
create or replace trigger on_auth_user_created
after insert on auth.users
for each row execute function public.handle_new_user();todos
Todos 示例功能(完整 CRUD、排序)读取此表。
create table if not exists public.todos (
id uuid primary key default gen_random_uuid(),
user_id uuid not null references auth.users(id) on delete cascade,
title text not null,
completed boolean not null default false,
sort_order integer not null default 0,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
create index if not exists todos_user_id_idx on public.todos (user_id);
create index if not exists todos_user_sort_idx
on public.todos (user_id, sort_order, created_at desc);
drop trigger if exists set_todos_updated_at on public.todos;
create trigger set_todos_updated_at
before update on public.todos
for each row execute function public.set_updated_at();
alter table public.todos enable row level security;
create policy "Users can view their todos" on public.todos for select using (auth.uid() = user_id);
create policy "Users can insert their todos" on public.todos for insert with check (auth.uid() = user_id);
create policy "Users can update their todos" on public.todos for update using (auth.uid() = user_id);
create policy "Users can delete their todos" on public.todos for delete using (auth.uid() = user_id);device_tokens
APNs 令牌写入此处。字段与 PushTokenRegistrar 的 upsert 一致(user_id、token、
platform、updated_at),采用复合主键以保证重复注册的幂等性。
create table if not exists public.device_tokens (
user_id uuid not null references auth.users(id) on delete cascade,
token text not null,
platform text not null default 'ios',
updated_at timestamptz not null default now(),
primary key (user_id, token)
);
create index if not exists device_tokens_user_idx on public.device_tokens (user_id);
alter table public.device_tokens enable row level security;
create policy "Users can view own device tokens" on public.device_tokens for select using (auth.uid() = user_id);
create policy "Users can insert own device tokens" on public.device_tokens for insert with check (auth.uid() = user_id);
create policy "Users can update own device tokens" on public.device_tokens for update using (auth.uid() = user_id);
create policy "Users can delete own device tokens" on public.device_tokens for delete using (auth.uid() = user_id);subscriptions + payments(已附带)
这两张表确实随模板附带,位于
supabase/migrations/20260630120000_billing_entitlements.sql。应用该文件(或
supabase db push)即可。要点:
subscriptions—— 每个用户一行(user_id为unique),即共享的权益只读模型。客户端仅有 SELECT RLS;所有写入都经由 service-role Edge Function。payments—— 仅追加的订单流水,客户端同样只读。- 两者都从
auth.users级联删除,因此注销账户时会自动清理。
该表与桌面端模板(soar-electron)共享——参见订阅。
模板修复建议: 最干净的长期方案是把上面的 profiles / todos / device_tokens
SQL 复制进 soar-swift 的 supabase/migrations/,这样单独一条 supabase db push 就能
建起整套 schema。在此之前,以本页 SQL 为准。
4. 创建上传存储桶
上传示例会写入一个名为 sample-uploads 的私有 Storage 桶,按用户分区。执行一次即可(策略
细节见存储):
insert into storage.buckets (id, name, public, file_size_limit)
values ('sample-uploads', 'sample-uploads', false, 52428800)
on conflict (id) do nothing;5. 部署 Edge Functions
supabase/functions/ 下有三个函数。安装
CLI、supabase login、在
supabase/config.toml 里设置 project_id,然后部署:
# RevenueCat 以未认证方式调用它——它自行校验 Authorization 头
supabase functions deploy revenuecat-webhook --no-verify-jwt --project-ref <ref>
supabase functions deploy send-test-push --project-ref <ref>
supabase functions deploy delete-account --project-ref <ref>
supabase functions list --project-ref <ref> # 确认三个都在functions/_shared/(apns.ts、revenuecat.ts)会自动打包进每个函数——绝不要单独
部署它。
revenuecat-webhook—— 根据 App Store 事件写入subscriptions行。verify_jwt = false(在config.toml中固定);改为通过Authorization头认证。参见 RevenueCat 配置。send-test-push—— 向调用者本人的 iOS 设备令牌发送测试推送(校验 JWT)。参见 推送通知。delete-account—— 清除调用者的行与上传,再删除 auth 用户。参见 身份认证。
6. 设置 Edge 密钥
这些是仅服务器密钥——绝不随应用包分发。用 supabase secrets set ... 或从
supabase/functions/.env 设置:
| 密钥 | 使用方 | 说明 |
|---|---|---|
REVENUECAT_WEBHOOK_AUTH | revenuecat-webhook | 必须与 RevenueCat webhook 上设置的 Authorization 头一致 |
APNS_TEAM_ID | send-test-push | Apple 开发者 Team ID |
APNS_KEY_ID | send-test-push | APNs Auth Key 的 Key ID |
APNS_BUNDLE_ID | send-test-push | 例如 com.soarstarter.SoarStarterSwift |
APNS_PRIVATE_KEY | send-test-push | .p8 APNs Auth Key 的内容 |
不要把 SUPABASE_URL、SUPABASE_ANON_KEY 或 SUPABASE_SERVICE_ROLE_KEY 设为
密钥——Edge 运行时会自动注入它们。
验证后端
- 注册可往返 —— 在应用中注册、验证 OTP,确认用户出现在
Authentication → Users 中并生成了
profiles行。 - 待办能持久化 —— 添加一个待办,强制退出后重启;它仍在。
- 函数已部署 ——
supabase functions list显示三个函数。
