自己动手
$109 每个平台,一次性付费
WebViewGold 许可证:一个现成的 Xcode 或 Android Studio 项目。粘贴您的 URL,设置名称和图标,开启所需功能,构建并提交。永久免费更新。
在主页查看方案Claude Code + 推送
推送是审核人员最先注意到的原生功能,也是用户真正会一直开着的功能。借助 WebViewGold,OneSignal 运行在原生外壳中;您的 Web 应用只需决定何时请求权限、设备属于哪个用户,以及点按通知后打开哪个页面。Claude Code 可以在一次会话中写完这一切,本指南为您提供提示词、服务器代码和测试计划。
每个平台一次性 $109 起,或由我们的团队全程代劳,$698 起。
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 | 您 |
|---|---|---|
| OneSignal 控制台 | 什么也不做;它无法访问您的控制台 | 创建应用,添加 APNs 凭据(iOS)和 Firebase 凭据(Android),复制 App ID 并创建一个 REST API 密钥 |
| WebViewGold 项目 | 如果项目就在仓库中,可以设置 kPushEnabled、oneSignalID、PUSH_ENABLED 以及 build.gradle 中的 ID;由您审阅 diff | Apple Developer 门户中的 Capabilities、OneSignal 扩展的 Bundle ID、签名和构建 |
| Web 应用 | 推送辅助模块、授权引导页面、设置项、注册路由、发送辅助函数、数据库迁移、CLAUDE.md 规则 | 批准计划、设置环境变量、部署 |
| 测试 | 一份书面测试计划;如有需要,还可以做一个隐藏的调试页面,显示推送状态和订阅 ID | 一台真实的 iPhone 和一台真实的 Android 手机、TestFlight 和内部测试 |
大约需要一个小时,大部分时间花在 Apple 和 Firebase 控制台上。
添加 iOS 平台并填入 APNs 凭据(OneSignal 推荐使用 .p8 认证密钥,推送证书也可以),再添加 Android 平台并上传 OneSignal 要求的 Firebase 服务账号 JSON。复制 OneSignal App ID,并为您的服务器创建一个 REST API 密钥。
把 kPushEnabled 设为 true,把 App ID 粘贴到 oneSignalID,并把 askforpushpermissionatfirstrun 设为 false,这样弹窗时机就由您掌握。在 Apple Developer 门户中,为该 App ID 启用 Push Notifications 和 App Groups。OneSignal 扩展 target 的 Bundle ID 是您的 Bundle ID 加上 .OneSignalNotificationServiceExtension。
把 PUSH_ENABLED 设为 true,把 App ID 粘贴到 build.gradle(Module: app)中的 ONE_SIGNAL_APP_ID,并把 requirePush 设为 false。用您自己的单色图标替换 ic_stat_onesignal_default.png,否则用户看到的是一个通用的铃铛图标。
把 useragent_iphone 和 useragent_ipad(iOS)以及 USER_AGENT(Android)设为一个普通的移动端 User-Agent,后面加上 WebViewGoldApp,这样 isNativeApp() 就能把应用与 Safari 或 Chrome 区分开。CLAUDE.md 模板解释了这一约定。
按 Shift+Tab,直到 Claude Code 显示计划模式(plan mode),或者用 claude --permission-mode plan 启动它。粘贴下面的提示词,只有当计划列出了您预期的文件和数据表时才批准。然后让它实施,阅读 diff,再部署。
为 TestFlight 归档 iOS 应用,并把 Android App Bundle 上传到内部测试。如果您不想为此安装 Xcode 和 Android Studio,Cloud Builder 会在浏览器中构建两个应用,并替您上传。

把 Web 应用转换成原生应用的绝佳方式!周转速度快,无需开发完整的原生应用就能把应用上架到应用商店!
根据您的应用调整数据表名称、路由和示例通知。提示词会告诉代理权威的 scheme 名称在哪里查,避免它凭空编造。
我们的 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 字段。
// 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 下。url 设为一个深层页面,例如 /orders/42。分别在三种状态下点按:应用已被彻底关闭、在后台、在前台。每次点按都会在应用内打开该页面。settingsapp:// 跳转到系统页面。在那里重新开启,返回应用,状态随之更新。window.location.href 先后设为多个 scheme,在 iOS 上不会生效(Android 没问题)。请用延迟依次调用,或者对受支持的数据调用使用文档中的 multiapicall://getonesignalplayerid,getappversion(MultiAPICall 文档)。url 字段。如果链接应在应用内打开,请让 kPushOpenDeeplinkInBrowser(iOS)和 OPEN_NOTIFICATION_URLS_IN_SYSTEM_BROWSER(Android)保持为 false。onesignalplayerid 是异步注入的,在 SDK 完成注册之前可能并不存在。请带超时轮询,并在下次启动应用时重试,而不是悄无声息地失败。kPushEnhanceUrl 或 PUSH_ENHANCE_WEBVIEW_URL 时,只有第一个请求会带上 ?onesignal_push_id=…;客户端路由会把它丢掉。请立即把它存进 Cookie 或会话,或者改用 autoInjectVariable / AUTO_INJECT_VARIABLES。registerpush:// 什么也不会做。调试 JavaScript 之前,先检查配置。可以。在 Config.swift 中设置 kFirebasePushEnabled 并加入您的 GoogleService-Info.plist,在 Config.java 中设置 FIREBASE_PUSH_ENABLED 并加入您的 google-services.json。Web 端的做法相同,只是 ID 来自 getfirebaseplayerid://,变量名为 firebaseplayerid。
不必,而且应用也不能依赖推送(准则 4.5.4)。不过对于准则 4.2 来说,推送仍是证明您的应用不只是一个网站的最有力信号之一。
可以。从 OneSignal 控制台群发不需要任何代码,而通过 sendlocalpushmsg:// 安排的本地提醒无需任何推送服务就能运行。只有发给单个用户的消息才需要服务器。
iOS 只会显示一次系统对话框。如果用户因为还不了解您的应用而拒绝,之后就只能在系统设置中更改。先让用户看到价值,再请求权限,同意率会高得多。
不要依赖它。在 WebViewGold 应用内,推送是通过 OneSignal、Firebase 或 Pushwoosh 实现的原生推送。如果您使用 Web 推送,请只面向浏览器访客。
结果相同,只是您需要投入的工作量不同。三种方式都附带审核通过保证和 7×24 小时人工支持。
$109 每个平台,一次性付费
WebViewGold 许可证:一个现成的 Xcode 或 Android Studio 项目。粘贴您的 URL,设置名称和图标,开启所需功能,构建并提交。永久免费更新。
在主页查看方案$349 每个许可证,一次性附加购买
无需 Mac、Xcode 或 Android Studio:在浏览器中完成配置,我们在云端构建两个应用,并将其交付到 App Store Connect 和 Play Console。
Cloud Builder 如何运作 试用免费演示$698 每个平台 $798
把您的 URL 和图标发给我们。我们的团队在 72 小时内完成配置、构建并交付您的应用,白标,附源码项目。两个平台共 $1,158。
Full Service 详情 服务提供方:冬季特惠价格有效期至 2026年12月31日。
App Store & Google Play 审核通过保证 – 否则 14 天内退款
想直接交给我们?Full Service:我们在 72 小时内完成配置、构建并交付,每个平台 $698 起。有疑问?通过 7×24 小时在线聊天联系我们。
退款依据我们 FAQ 中列出的条件:应用因非内容原因被 Apple 和/或 Google 拒审,并在购买后 14 天内提出申请。