推送通知
配置通知权限、本地收件箱、Expo 推送 token 和可选服务端发送。
Expo 模板已经包含客户端通知体验:权限预热、Android 通知频道、Expo push token 获取、前台通知处理、本地收件箱,以及点击通知后打开深度链接。它还没有内置完整的推送发送后端。
真实远程推送测试需要 native development、preview 或 production build。Expo
文档说明,从 SDK 53 开始,Expo Go 的 Android 端不再支持
expo-notifications 远程推送;本模板使用 Expo SDK 54。
提供什么
| Surface | 状态 | 说明 |
|---|---|---|
NotificationsProvider | Integrated | 懒加载 expo-notifications,在 web 或缺少 native module 的 build 中也不会崩溃,并暴露 useNotifications() |
app/permissions.tsx | Integrated | 在一个页面中预热通知、照片、相机和 tracking 权限 |
app/notifications.tsx | Integrated | 显示权限状态、当前 Expo push token、缓存的通知收件箱和清空动作 |
| Android channels | Integrated | 注册 default 和 alerts 两个通知频道 |
| 通知点击路由 | Integrated | 从 notification response 的 data.url 读取 URL,并交给 openDeepLink() |
| 服务端发送 | 未接好 | 客户端不持久化 token;可选的 send-test-push 函数依赖当前不存在的 device_tokens 表 |
lib/notifications.tsx 用 lazy require 包住 native module。如果
expo-notifications 无法加载,isNativePushAvailable 会是 false,请求动作会返回友好错误,而不是让应用崩溃。
客户端流程
注册 handler
Provider 挂载后会安装前台通知 handler:显示 alert,不播放声音,不设置 badge。它也会订阅收到通知和点击通知的事件。
创建 Android 通知频道
Android 端会创建:
default -> General, default importance
alerts -> Alerts, high importanceAndroid 13+ 需要通知权限,系统弹窗也依赖在请求 push token 前先创建频道。
请求权限和 token
requestPermission() 调用 requestPermissionsAsync()。权限通过后,它调用
getExpoPushTokenAsync(),并把返回的 token 存在当前 React state 中。
保存本地收件箱
前台通知会被标准化为 { id, title, body, url, createdAt },插入收件箱顶部,最多保留 50 条,并通过 lib/cache.ts 持久化在 cache.notifications.inbox.v1。
点击通知打开路由
如果 notification response 带有 data.url,Provider 会调用:
openDeepLink(url, "/notifications");只有 lib/deep-links.ts 允许的路由会被打开,否则回退到通知页面。
尚未接好的部分
客户端会获取 Expo push token,但不会写入 Supabase。随附的 Supabase
send-test-push 函数只是一个可选 APNs 调试 artifact,来自 native stack。它查询:
public.device_tokens当前没有 migration 创建这张表,Expo 客户端也没有注册 native APNs token。请把它当作起点,而不是一个可直接使用的 Expo 推送后端。
如果继续使用 APNs 函数,也要更新它的 fallback deep link:
send-test-push 当前回退到 soar:///notifications,而本模板默认 scheme 是
soar-starter-expo-dev://、soar-starter-expo-preview:// 和
soar-starter-expo://。
真正发送推送
如果走 Expo push service:
添加 token 表
创建按用户隔离的 Expo push token 表:
create table public.device_push_tokens (
id uuid primary key default gen_random_uuid(),
user_id uuid not null references auth.users(id) on delete cascade,
platform text not null,
expo_push_token text not null,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (user_id, expo_push_token)
);
alter table public.device_push_tokens enable row level security;再添加基于 auth.uid() = user_id 的 insert/select/delete policies。
权限通过后持久化 token
getExpoPushTokenAsync() 成功后,为已登录用户 upsert token。如果你不希望共享设备继续收到账号相关消息,可以在退出登录时删除旧 token。
通过 Expo 发送
创建 Supabase Edge Function 或自己的 API route,读取 device_push_tokens,调用
Expo push API,并清理无效 receipts。服务端认证和 provider secret 都要放在 app bundle 外。
如果你更想直接使用 APNs/FCM,请扩展现有 send-test-push artifact:增加真正的 device_tokens migration,采集 native device token 而不是 Expo push token,并在生产前补上 Android FCM 路径。
验证
- 在真机上安装 native development build。
- 打开 Permissions,请求 Notifications 权限。
- 打开 Notifications,确认状态变成
granted。 - 确认 Expo push token 出现。如果一直为空,检查 native 凭据和运行时日志。
- 发送一条
data.url指向/notifications或/refer的测试推送,确认点击后能打开对应页面。
