跳至内容
WebViewGold 从 Vibe Coding 到 App Store
中文
获取 WebViewGold

Claude Code + 推送

用 WebViewGold 为 Claude Code Web 应用添加 OneSignal 推送通知

推送是审核人员最先注意到的原生功能,也是用户真正会一直开着的功能。借助 WebViewGold,OneSignal 运行在原生外壳中;您的 Web 应用只需决定何时请求权限、设备属于哪个用户,以及点按通知后打开哪个页面。Claude Code 可以在一次会话中写完这一切,本指南为您提供提示词、服务器代码和测试计划。

每个平台一次性 $109 起,或由我们的团队全程代劳,$698 起。

  • 审核通过保证 – 否则 14 天内退款
  • 无需编程
  • 一次性付费,免费更新
  • 7×24 小时人工支持
  • 2014 年起德国出品 · ISO 27001

您要接通的各个部分,以及审核人员为何在意

WebViewGold 在两个原生项目中都内置了 OneSignal SDK。开启之后,即使应用处于关闭状态也能收到推送。您的 Web 应用仍负责三项决定:系统权限弹窗何时出现、设备属于哪个用户,以及通知打开哪个 URL。每一项都对应一个 URL scheme 或一个注入的 JavaScript 变量,详见iOS 推送文档和Android 推送文档。

审核人员在意它有两个原因。准则 4.2 要求基于 Web 的应用不能只是一个网站,而一个可用的推送授权流程通常是他们看到的第一项原生能力。准则 4.5.4 则划定了界限:应用在没有推送的情况下也必须能正常使用,通知中不得包含敏感的个人数据,推广类推送需要在您的界面中获得用户明确同意,并提供退订方式。Android 13 及更高版本新增了运行时权限(POST_NOTIFICATIONS),因此在两个平台上,请求权限的时机都很重要。

最终的流程是这样的:新手引导结束后,Web 应用导航到 registerpush://;用户允许后,读取 onesignalplayerid 并保存到该用户名下;您的服务器向这个订阅 ID 发送推送,并在附加数据(additional data)中带上 url,用户点按通知时,WebViewGold 就会打开这个 URL。原生功能的整体介绍请参阅推送通知专题页。

Claude Code 做什么,您做什么

领域Claude Code您
OneSignal 控制台什么也不做;它无法访问您的控制台创建应用,添加 APNs 凭据(iOS)和 Firebase 凭据(Android),复制 App ID 并创建一个 REST API 密钥
WebViewGold 项目如果项目就在仓库中,可以设置 kPushEnabled、oneSignalID、PUSH_ENABLED 以及 build.gradle 中的 ID;由您审阅 diffApple Developer 门户中的 Capabilities、OneSignal 扩展的 Bundle ID、签名和构建
Web 应用推送辅助模块、授权引导页面、设置项、注册路由、发送辅助函数、数据库迁移、CLAUDE.md 规则批准计划、设置环境变量、部署
测试一份书面测试计划;如有需要,还可以做一个隐藏的调试页面,显示推送状态和订阅 ID一台真实的 iPhone 和一台真实的 Android 手机、TestFlight 和内部测试

开启 OneSignal,把 Web 部分交给 Claude Code

大约需要一个小时,大部分时间花在 Apple 和 Firebase 控制台上。

  1. 为两个平台创建同一个 OneSignal 应用

    添加 iOS 平台并填入 APNs 凭据(OneSignal 推荐使用 .p8 认证密钥,推送证书也可以),再添加 Android 平台并上传 OneSignal 要求的 Firebase 服务账号 JSON。复制 OneSignal App ID,并为您的服务器创建一个 REST API 密钥。

  2. iOS:Config.swift 和 Capabilities

    把 kPushEnabled 设为 true,把 App ID 粘贴到 oneSignalID,并把 askforpushpermissionatfirstrun 设为 false,这样弹窗时机就由您掌握。在 Apple Developer 门户中,为该 App ID 启用 Push Notifications 和 App Groups。OneSignal 扩展 target 的 Bundle ID 是您的 Bundle ID 加上 .OneSignalNotificationServiceExtension。

  3. Android:Config.java 和 build.gradle

    把 PUSH_ENABLED 设为 true,把 App ID 粘贴到 build.gradle(Module: app)中的 ONE_SIGNAL_APP_ID,并把 requirePush 设为 false。用您自己的单色图标替换 ic_stat_onesignal_default.png,否则用户看到的是一个通用的铃铛图标。

  4. 在 User-Agent 中标记应用

    把 useragent_iphone 和 useragent_ipad(iOS)以及 USER_AGENT(Android)设为一个普通的移动端 User-Agent,后面加上 WebViewGoldApp,这样 isNativeApp() 就能把应用与 Safari 或 Chrome 区分开。CLAUDE.md 模板解释了这一约定。

  5. 在计划模式下运行提示词

    按 Shift+Tab,直到 Claude Code 显示计划模式(plan mode),或者用 claude --permission-mode plan 启动它。粘贴下面的提示词,只有当计划列出了您预期的文件和数据表时才批准。然后让它实施,阅读 diff,再部署。

  6. 构建并安装

    为 TestFlight 归档 iOS 应用,并把 Android App Bundle 上传到内部测试。如果您不想为此安装 Xcode 和 Android Studio,Cloud Builder 会在浏览器中构建两个应用,并替您上传。

把 Web 应用转换成原生应用的绝佳方式!周转速度快,无需开发完整的原生应用就能把应用上架到应用商店!

Kevin Joling · FlightConnections & TrainConnections · Trustpilot

Claude Code 提示词

根据您的应用调整数据表名称、路由和示例通知。提示词会告诉代理权威的 scheme 名称在哪里查,避免它凭空编造。

粘贴到 Claude Code(先用计划模式)
我们的 Web 应用同时运行在用 WebViewGold 构建的原生 iOS 和 Android 应用中。原生项目中已经开启了 OneSignal 推送。Web 应用需要在合适的时机请求权限,把设备的订阅 ID 报告给我们的服务器,再由服务器发送定向通知。编写代码之前,先阅读 https://www.webviewgold.com/llms-full.txt 中 iOS 和 Android 的 Push Notifications 章节,只使用其中记录的 URL scheme 和变量。先给出计划,等待我批准。

实现:
1. src/lib/native.ts(如果已存在就复用):isNativeApp() 检查 navigator.userAgent 中是否包含标记 WebViewGoldApp;callNative(url) 仅在应用内设置 window.location.href;readInjected(name, timeoutMs) 每 100 毫秒轮询一次 window[name],超时后 reject。
2. src/lib/push.ts:askForPush() 导航到 registerpush://。getPushStatus() 导航到 checkNativePushPermissions://,并以 window.nativePushEnabled(true 或 false)resolve。syncPushId() 导航到 getonesignalplayerid://,读取 window.onesignalplayerid,并连同平台(ios 或 android,根据 User-Agent 判断)一起 POST 到 /api/push/register。不要在同一次脚本执行中触发两个 scheme:在 iOS 上,一个脚本里多次设置 window.location.href 不会生效,所以要以大约一秒的间隔依次调用。
3. 一个授权引导页面,在新手引导之后显示一次,绝不出现在第一个页面上,并且只在 isNativeApp() 为 true 时显示:用一句话说明我们会发送什么(订单更新和回复),一个“开启通知”按钮(先调用 askForPush,3 秒后再调用 syncPushId),以及一个“以后再说”链接,通过 localStorage 让该页面在 14 天内不再出现。
4. 设置:一行“通知”,显示 getPushStatus() 返回的状态。如果通知已关闭,显示“打开设置”,导航到 settingsapp://。另外添加一个“产品动态”开关,默认关闭,保存在用户资料中;只有打开了这个开关的用户才能收到推广推送(Apple 准则 4.5.4)。
5. 已登录用户每次在应用内启动应用时,调用一次 syncPushId(),因为重新安装后订阅 ID 可能会变化。
6. 服务器:POST /api/push/register(需要身份验证;把 user_id、subscription_id、platform、last_seen_at upsert 到 push_devices 表中;添加迁移),以及一个仅限服务器端的辅助函数 sendPushToUser(userId, title, message, path),它使用 include_subscription_ids 调用 OneSignal REST API,并把目标页面的完整 https URL 放进 data.url。ONESIGNAL_APP_ID 和 ONESIGNAL_REST_API_KEY 只能从服务器环境变量中读取。
7. 在创建新回复或订单状态发生变化的地方调用 sendPushToUser。通知文本中不得包含个人或敏感信息。
8. 把上述推送规则添加到 CLAUDE.md。

最后报告修改过的文件、迁移、需要我设置的环境变量,以及所有无法验证的内容。

注册路由和发送辅助函数

Next.js App Router + TypeScript;如果您的技术栈是 Express、Supabase Edge Function 或 Cloud Function,请让代理改写。data.url 的值就是用户点按通知时 WebViewGold 打开的地址;不要使用 OneSignal 的 Launch URL 字段。

typescript
// app/api/push/register/route.ts  (由原生外壳中的 Web 应用调用)
import { NextResponse } from "next/server";
import { getUser, db } from "@/lib/server"; // 您的身份验证和数据库辅助函数

export async function POST(req: Request) {
  const user = await getUser(req);
  if (!user) return NextResponse.json({ error: "unauthorized" }, { status: 401 });

  const { subscriptionId, platform } = await req.json();
  if (typeof subscriptionId !== "string" || subscriptionId.length < 10) {
    return NextResponse.json({ error: "invalid subscription id" }, { status: 400 });
  }
  await db.pushDevices.upsert({ userId: user.id, subscriptionId, platform, lastSeenAt: new Date() });
  return NextResponse.json({ ok: true });
}

// lib/push-server.ts  (仅限服务器端,切勿从客户端组件中导入)
export async function sendPushToUser(userId: string, title: string, message: string, path: string) {
  const devices = await db.pushDevices.findMany({ userId });
  if (devices.length === 0) return { sent: 0 };

  const res = await fetch("https://api.onesignal.com/notifications?c=push", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Key " + process.env.ONESIGNAL_REST_API_KEY,
    },
    body: JSON.stringify({
      app_id: process.env.ONESIGNAL_APP_ID,
      include_subscription_ids: devices.map((d) => d.subscriptionId),
      headings: { en: title },
      contents: { en: message },
      data: { url: "https://app.example.com" + path }, // 用户点按时由 WebViewGold 打开
    }),
  });
  if (!res.ok) throw new Error("OneSignal " + res.status + ": " + (await res.text()));
  return { sent: devices.length };
}

在真实手机上的验证清单

模拟器和桌面浏览器都无法证明这些。请在 iPhone 上使用 TestFlight 版本,在 Android 13 及以上的手机上使用内部测试版本,两者都要全新安装。

  • 首次启动:不出现系统弹窗,因为 askforpushpermissionatfirstrun 和 requirePush 都是 false。
  • 新手引导结束后,出现您的授权引导页面;点“开启通知”会弹出系统对话框;点“以后再说”会隐藏该页面,下次启动时它仍保持隐藏。
  • 允许之后,push_devices 中新增一行,该设备出现在 OneSignal 的 Audience → Subscriptions 下。
  • 从 OneSignal 控制台发送一条测试推送,在 Additional data 中把 url 设为一个深层页面,例如 /orders/42。分别在三种状态下点按:应用已被彻底关闭、在后台、在前台。每次点按都会在应用内打开该页面。
  • 触发一个真实事件(给测试用户发一条回复),确认只有该用户的设备收到推送。
  • 在第二台设备上拒绝权限:设置中显示通知已关闭,“打开设置”通过 settingsapp:// 跳转到系统页面。在那里重新开启,返回应用,状态随之更新。
  • 删除并重新安装应用,然后登录:系统注册了一个新的订阅 ID。

常见陷阱

  • 在 iOS 上的同一次脚本执行中调用两个 scheme。在同一个脚本里把 window.location.href 先后设为多个 scheme,在 iOS 上不会生效(Android 没问题)。请用延迟依次调用,或者对受支持的数据调用使用文档中的 multiapicall://getonesignalplayerid,getappversion(MultiAPICall 文档)。
  • 用 Launch URL 代替附加数据。WebViewGold 读取的是附加数据中的 url 字段。如果链接应在应用内打开,请让 kPushOpenDeeplinkInBrowser(iOS)和 OPEN_NOTIFICATION_URLS_IN_SYSTEM_BROWSER(Android)保持为 false。
  • 过早读取 ID。onesignalplayerid 是异步注入的,在 SDK 完成注册之前可能并不存在。请带超时轮询,并在下次启动应用时重试,而不是悄无声息地失败。
  • 依赖 URL 参数。使用 kPushEnhanceUrl 或 PUSH_ENHANCE_WEBVIEW_URL 时,只有第一个请求会带上 ?onesignal_push_id=…;客户端路由会把它丢掉。请立即把它存进 Cookie 或会话,或者改用 autoInjectVariable / AUTO_INJECT_VARIABLES。
  • 推送服务没有开启。如果封装层中没有启用任何推送服务,registerpush:// 什么也不会做。调试 JavaScript 之前,先检查配置。
  • 把密钥放在客户端。REST API 密钥只能放在服务器环境变量中。Claude Code 永远不需要真实的值,告诉它变量名即可。

常见问题

可以用 Firebase Cloud Messaging 代替 OneSignal 吗?

可以。在 Config.swift 中设置 kFirebasePushEnabled 并加入您的 GoogleService-Info.plist,在 Config.java 中设置 FIREBASE_PUSH_ENABLED 并加入您的 google-services.json。Web 端的做法相同,只是 ID 来自 getfirebaseplayerid://,变量名为 firebaseplayerid。

要通过 App Store 审核,必须有推送吗?

不必,而且应用也不能依赖推送(准则 4.5.4)。不过对于准则 4.2 来说,推送仍是证明您的应用不只是一个网站的最有力信号之一。

没有服务器也能发送通知吗?

可以。从 OneSignal 控制台群发不需要任何代码,而通过 sendlocalpushmsg:// 安排的本地提醒无需任何推送服务就能运行。只有发给单个用户的消息才需要服务器。

为什么不在首次启动时请求权限?

iOS 只会显示一次系统对话框。如果用户因为还不了解您的应用而拒绝,之后就只能在系统设置中更改。先让用户看到价值,再请求权限,同意率会高得多。

Service Worker 实现的 Web 推送在应用内能用吗?

不要依赖它。在 WebViewGold 应用内,推送是通过 OneSignal、Firebase 或 Pushwoosh 实现的原生推送。如果您使用 Web 推送,请只面向浏览器访客。

让您的应用上架商店的三种方式

结果相同,只是您需要投入的工作量不同。三种方式都附带审核通过保证和 7×24 小时人工支持。

自己动手

$109 每个平台,一次性付费

WebViewGold 许可证:一个现成的 Xcode 或 Android Studio 项目。粘贴您的 URL,设置名称和图标,开启所需功能,构建并提交。永久免费更新。

在主页查看方案

Cloud Builder

$349 每个许可证,一次性附加购买

无需 Mac、Xcode 或 Android Studio:在浏览器中完成配置,我们在云端构建两个应用,并将其交付到 App Store Connect 和 Play Console。

Cloud Builder 如何运作 试用免费演示

Full Service

$698 每个平台 $798

把您的 URL 和图标发给我们。我们的团队在 72 小时内完成配置、构建并交付您的应用,白标,附源码项目。两个平台共 $1,158。

Full Service 详情 服务提供方: WeSetupYourWebViewApp

冬季特惠价格有效期至 2026年12月31日。

准备好发布您的应用了吗?

App Store & Google Play 审核通过保证 – 否则 14 天内退款

想直接交给我们?Full Service:我们在 72 小时内完成配置、构建并交付,每个平台 $698 起。有疑问?通过 7×24 小时在线聊天联系我们。

退款依据我们 FAQ 中列出的条件:应用因非内容原因被 Apple 和/或 Google 拒审,并在购买后 14 天内提出申请。