Supabase 配置

搭建后端:数据库 schema、认证回调、Edge Functions 与密钥。

Supabase 是本模板的后端——Postgres、Auth、Storage 与 Edge Functions。本页带你从一个空 项目走到一个完全可用的后端,认证、待办、上传、订阅与推送都从它读取数据。

模板并未附带完整的 schema。 supabase/migrations/ 只包含 billing_entitlements.sqlsubscriptions + payments 两张表)。应用读取的 todosprofilesdevice_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.plistCFBundleURLTypes)中注册。没有这条白名单, 验证链接与 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_idtokenplatformupdated_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_idunique),即共享的权益只读模型。客户端仅有 SELECT RLS;所有写入都经由 service-role Edge Function。
  • payments —— 仅追加的订单流水,客户端同样只读。
  • 两者都从 auth.users 级联删除,因此注销账户时会自动清理。

该表与桌面端模板(soar-electron)共享——参见订阅

模板修复建议: 最干净的长期方案是把上面的 profiles / todos / device_tokens SQL 复制进 soar-swiftsupabase/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/ 下有三个函数。安装 CLIsupabase 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.tsrevenuecat.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_AUTHrevenuecat-webhook必须与 RevenueCat webhook 上设置的 Authorization 头一致
APNS_TEAM_IDsend-test-pushApple 开发者 Team ID
APNS_KEY_IDsend-test-pushAPNs Auth Key 的 Key ID
APNS_BUNDLE_IDsend-test-push例如 com.soarstarter.SoarStarterSwift
APNS_PRIVATE_KEYsend-test-push.p8 APNs Auth Key 的内容

不要SUPABASE_URLSUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEY 设为 密钥——Edge 运行时会自动注入它们。

验证后端

  • 注册可往返 —— 在应用中注册、验证 OTP,确认用户出现在 Authentication → Users 中并生成了 profiles 行。
  • 待办能持久化 —— 添加一个待办,强制退出后重启;它仍在。
  • 函数已部署 —— supabase functions list 显示三个函数。

相关页面

On this page