Do it yourself
$109 per platform, one-time
The WebViewGold license: a finished Xcode or Android Studio project. Paste your URL, set name and icon, switch on features, build and submit. Free updates forever.
See plans on the main pageClaude Code
An Agent Skill is a folder with a SKILL.md file that Claude Code loads only when a task needs it. That makes it the right home for the WebViewGold bridge: exact URL schemes, injected variables and platform quirks that matter whenever the agent touches a native feature and never otherwise. Here is a complete skill, checked against the WebViewGold documentation and ready for .claude/skills/.
From $109 one-time per platform, or let our team do it all from $698.
CLAUDE.md is read at the start of every session, so every line in it costs context on every task, including the many tasks that have nothing to do with native features. Skills work the other way around. Claude Code keeps only each skill's short description in view; the full SKILL.md loads when you type /webviewgold-bridge or when Claude sees that a request matches the description. Supporting files in the skill folder load only when the instructions point to them.
For a JavaScript bridge that is ideal. The scheme table, the callback signatures and the iOS workarounds are precise reference material the agent must copy exactly, and they are irrelevant while it fixes a CSS bug. Your CLAUDE.md keeps one line ("native features: see the webviewgold-bridge skill"), and the details live in one maintained place.
A skill is also not an MCP server. MCP connects Claude to a live system such as GitHub, a database or a browser. The bridge needs no live connection, it needs correct knowledge and a helper file, and that is exactly what a skill packages.
Three files. Only SKILL.md is required; the other two keep it short.
.claude/skills/webviewgold-bridge/
├── SKILL.md rules, scheme table, patterns (loads with the skill)
├── platform-notes.md iOS vs Android differences and native switches (loads on demand)
└── templates/
└── native.ts helper module Claude copies into src/lib/The description is the most important line: it decides when Claude loads the skill, so it names the features and the words people actually type. Every scheme in the table appears in the WebViewGold documentation, for example the push, biometrics and share sheet pages.
---
name: webviewgold-bridge
description: Adds or debugs native iOS and Android features in this web app through the WebViewGold JavaScript bridge (URL schemes such as registerpush://, bioauth://, qrcode://, shareapp://, getonesignalplayerid://, rateapp://). Use when a task involves push notifications, Face ID, Touch ID or fingerprint login, QR or barcode scanning, the share sheet, haptics or vibration, saving images, app rating, the app version, opening app settings, or any behavior that must differ inside the native app.
---
# WebViewGold JavaScript bridge
This web app also ships as native iOS and Android apps built with WebViewGold. The apps load
the production URL in a WKWebView (iOS) or an Android WebView. Native features are triggered
by navigating to a URL scheme. Results come back as injected global variables or as calls to
global functions. There is no SDK to install and nothing to bundle.
## Rules
1. Detect the app with isNativeApp() from src/lib/native.ts. If that file does not exist,
copy templates/native.ts from this skill. Never write a second user agent check.
2. Trigger schemes only through callNative(), callNativeQuietly() or callAndRead(). Every
call needs a browser fallback: the web equivalent, or hide the control. No dead buttons.
3. Use only the schemes in the table below. For anything else, read the feature page under
https://www.webviewgold.com/docs/iOS/ or https://www.webviewgold.com/docs/android/
(full text: https://www.webviewgold.com/llms-full.txt). If it is not documented, tell
the user it is not supported. Never invent a scheme, parameter or variable name.
4. iOS processes only one location change per script run. For several data calls at once
use multiapicall://getonesignalplayerid,getappversion (comma-separated, no spaces; it
works on Android too). Fire haptics on iOS through callNativeQuietly().
5. Injected variables arrive asynchronously. Read them with readInjected() or callAndRead(),
never synchronously right after the call.
6. A scheme for a feature that is switched off in the native project does nothing. When you
add a feature, list the native settings the user must enable (see platform-notes.md).
Never edit signing, certificates, provisioning profiles or keystore settings.
7. Biometric success is a UI gate only. Sensitive actions still need a server-side check.
## Scheme reference
| Feature | Call | Result |
|---|---|---|
| Push permission prompt | registerpush:// | System permission dialog |
| Push permission status | checkNativePushPermissions:// | window.nativePushEnabled (true or false) |
| App settings | settingsapp:// | Opens the system settings of the app |
| OneSignal subscription ID | getonesignalplayerid:// | window.onesignalplayerid |
| Firebase push ID | getfirebaseplayerid:// | window.firebaseplayerid |
| Local notification | sendlocalpushmsg://push.send?s=SECONDS=msg!MESSAGE&!#TITLE&!#URL | Shown after SECONDS |
| Cancel local notifications | sendlocalpushmsg://push.send.cancel | Removes all scheduled ones |
| Biometrics | bioauth:// | iOS calls onBioAuthSuccess(), onBioAuthFailure(errorCode, errorMessage) or onBioAuthUnavailable() |
| QR code | qrcode:// | The scanned URL opens in the app |
| Barcode | barcodescanner:// | window.barcodescanresult |
| Share sheet | shareapp://message?=TEXT&url=URL | Native share dialog |
| Haptics | lighthaptic:// heavyhaptic:// successhaptic:// warninghaptic:// errorhaptic:// | Vibration; mediumhaptic:// is iOS only |
| Save image | savethisimage://?url=IMAGE_URL | Saved to the photo library |
| Rating | rateapp:// | System review dialog; the system decides whether it appears |
| App version | getappversion:// | window.versionNumber and window.bundleNumber |
## Patterns
- Push: ask after onboarding or a first success, never on the first screen. Explain in one
sentence what we will send, then call registerpush://. In settings, use pushEnabled() and
offer settingsapp:// when push is off. Save onesignalplayerid on the user profile via our API.
- Share: always encodeURIComponent TEXT and URL; share() in native.ts does it.
- QR codes: encode URLs on our own domain, because the scanned URL loads inside the app.
- Rating: only after a positive moment (order completed, task finished). Never at launch and
never in exchange for a reward.
- Version: hide UI that older app builds cannot support by checking appVersion().
## Before you finish
- Every native call sits behind isNativeApp() and has a browser fallback.
- Your summary lists each scheme you used, the native switches to enable (platform-notes.md)
and any platform-specific limits.
- Remind the user to test inside the real app on a device: push and biometrics cannot be
tested in a desktop browser.
WebViewGold has allowed us the shortest and most efficient time-to-market ever possible from among all the other app platforms we have ever tried during the past 5 years. I don’t know why I hadn’t discovered you sooner, but I am really, really glad I did. Currently nearing 6000 app downloads on both iOS App Store & Google Play Store without any problem!
What has to be switched on in the native projects for each feature, and where iOS and Android differ. Claude reads this file when it tells you which settings to enable. The biometrics note reflects the docs as they stand: the iOS page documents three callbacks, the Android page documents the prompt only.
# Platform notes for the WebViewGold bridge
## User agent token
- iOS: Config.swift, useragent_iphone and useragent_ipad
- Android: Config.java, USER_AGENT
- Value: a normal mobile user agent string, a space, then WebViewGoldApp
## Push (OneSignal)
- iOS: kPushEnabled and oneSignalID in Config.swift. Set askforpushpermissionatfirstrun to
false when the web app asks later with registerpush://. The App ID needs the Push
Notifications and App Groups capabilities.
- Android: PUSH_ENABLED in Config.java, ONE_SIGNAL_APP_ID in app/build.gradle.
- Firebase instead of OneSignal: getfirebaseplayerid:// and window.firebaseplayerid.
## Variables without a call
- iOS autoInjectVariable, Android AUTO_INJECT_VARIABLES: values such as the push ID and the
app version are injected automatically. readInjected() works either way.
## Biometrics
- iOS: add "Privacy - Face ID Usage Description" to Info.plist. Callbacks:
onBioAuthSuccess(), onBioAuthFailure(errorCode, errorMessage), onBioAuthUnavailable().
- Android: the system BiometricPrompt, which may fall back to PIN or pattern. The Android
docs describe the prompt but not the three callbacks: test on a device before relying on
them, and keep a fallback.
- enableBioMetricAuth (both platforms) is a separate app lock on every launch and is
independent of bioauth://.
## Camera features (qrcode://, barcodescanner://)
- iOS: camera usage description in Info.plist; qrcodelinks in Config.swift decides where
scanned URLs open.
- Android: requireCamera and QR_CODE_URL_OPTIONS in Config.java.
## Haptics
- mediumhaptic:// exists on iOS only. Use lighthaptic:// or heavyhaptic:// on Android.
## Downloads
- savethisimage://?url=... saves an image. Other files download natively by extension:
extentionARY (Config.swift) and downloadableExtension (Config.java).
## Rating
- rateapp:// shows the system review dialog, which the system rate-limits.
iOS alternative: showcustomrateappdialog and ratemyappurl in Config.swift.
## Documentation
- iOS: https://www.webviewgold.com/docs/iOS/
- Android: https://www.webviewgold.com/docs/android/
- Full text for agents: https://www.webviewgold.com/llms-full.txtThe helper the skill tells Claude to copy. It is the only place that knows the user agent token, and it handles three details agents usually miss: the quiet iframe call for haptics from the iOS haptics docs, one location change per script run on iOS (see MultiAPICall), and clearing a stale variable before reading a fresh one.
// templates/native.ts
// Copied into src/lib/native.ts by the webviewgold-bridge skill.
// The only file that knows how the web app detects the WebViewGold apps.
// Set in Config.swift (useragent_iphone, useragent_ipad) and Config.java (USER_AGENT)
const TOKEN = "WebViewGoldApp";
export function isNativeApp(): boolean {
return typeof navigator !== "undefined" && navigator.userAgent.includes(TOKEN);
}
export function isAndroidApp(): boolean {
return isNativeApp() && /Android/i.test(navigator.userAgent);
}
export function isIOSApp(): boolean {
return isNativeApp() && !isAndroidApp();
}
// Navigate to a scheme. Returns false in normal browsers so callers can fall back.
export function callNative(scheme: string): boolean {
if (!isNativeApp()) return false;
window.location.href = scheme;
return true;
}
// Fire-and-forget call through a temporary iframe, so it does not cancel a
// navigation in progress (the technique from the WebViewGold iOS haptics docs).
export function callNativeQuietly(scheme: string): boolean {
if (!isNativeApp()) return false;
const frame = document.createElement("iframe");
frame.style.display = "none";
frame.src = scheme;
document.body.appendChild(frame);
frame.remove();
return true;
}
// Poll for a global variable the app injects (onesignalplayerid, nativePushEnabled ...)
export function readInjected<T = unknown>(name: string, timeoutMs = 3000): Promise<T> {
return new Promise((resolve, reject) => {
const started = Date.now();
const tick = () => {
const value = (window as any)[name];
if (value !== undefined) return resolve(value as T);
if (Date.now() - started > timeoutMs) return reject(new Error(name + " was not injected"));
setTimeout(tick, 100);
};
tick();
});
}
// Call a scheme and read a fresh result; clears a stale value first.
export async function callAndRead<T = unknown>(scheme: string, name: string, timeoutMs = 3000): Promise<T | null> {
if (!isNativeApp()) return null;
try { delete (window as any)[name]; } catch { /* variable not deletable, keep polling */ }
window.location.href = scheme;
return readInjected<T>(name, timeoutMs).catch(() => null);
}
export type Haptic = "light" | "heavy" | "success" | "warning" | "error";
export function haptic(kind: Haptic): void {
const scheme = kind + "haptic://";
if (isIOSApp()) callNativeQuietly(scheme);
else callNative(scheme);
}
export function share(text: string, url: string): boolean {
return callNative("shareapp://message?=" + encodeURIComponent(text) + "&url=" + encodeURIComponent(url));
}
export function pushEnabled(): Promise<boolean | null> {
return callAndRead<boolean>("checkNativePushPermissions://", "nativePushEnabled", 2000);
}
export function appVersion(): Promise<string | null> {
return callAndRead<string>("getappversion://", "versionNumber", 2000);
}Works the same in the terminal, the IDE extensions and cloud sessions.
Create .claude/skills/webviewgold-bridge/ in the repository root with the three files above and commit them. For a skill you want in all of your projects, use ~/.claude/skills/webviewgold-bridge/ instead.
Delete table rows for features you have not switched on in the native projects. A scheme for a disabled feature does nothing at runtime, and an agent that sees it in the table will happily wire it in.
Type / in Claude Code and look for webviewgold-bridge, or ask "which skills can you use in this project?". If it is missing, make sure the frontmatter starts on the very first line of SKILL.md.
Ask for a feature without naming the skill: "Add a share button to the recipe page." Claude should load the skill, use shareapp:// through share() and keep the copy-link button as the browser fallback.
/webviewgold-bridge add success haptics after checkout loads the skill and passes the rest of the line as the task.
Check every call for the isNativeApp() guard and read the list of native switches Claude reports. Then deploy and test inside the real app; push and Face ID do not work in a desktop browser.
Use this instead of copying the files by hand when you want the skill limited to the features you actually enabled.
Create an Agent Skill at .claude/skills/webviewgold-bridge/ for this repository. Our web app also runs inside native iOS and Android apps built with WebViewGold; native features are triggered by URL schemes and return values through injected JavaScript variables or global callback functions. 1. Read Config.swift and Config.java (in native/ios and native/android, adjust if different) and list which native features are actually switched on. 2. For each enabled feature, open the matching documentation page under https://www.webviewgold.com/docs/iOS/ and https://www.webviewgold.com/docs/android/ and copy the exact scheme, parameters and return variables. Leave out anything you cannot find in the documentation and list those items separately for me. 3. Write SKILL.md with frontmatter (name: webviewgold-bridge, and a description that names the features and the words a user would type), short rules (isNativeApp guard, browser fallback, multiapicall on iOS for several data calls, asynchronous injected variables, no invented schemes), a scheme table, and a checklist for finishing a task. 4. Put the iOS vs Android differences and the required Config.swift, Config.java, Info.plist and AndroidManifest.xml settings into platform-notes.md. 5. Put the helper module into templates/native.ts with isNativeApp, callNative, callNativeQuietly (iframe), readInjected and callAndRead. Keep SKILL.md under 150 lines and show me every file before saving.
| Mechanism | Loads when | Use it for in a WebViewGold project |
|---|---|---|
| CLAUDE.md | Every session | Short standing rules: the app runs in a WebView, the store rules, the protected routes |
| Skill (SKILL.md) | When a task matches, or via /name | Reference knowledge and templates, such as this bridge skill |
| Subagent | When Claude delegates | Work that needs its own context and a strict role, such as the store review checker |
| Command | When you type it | A procedure you start on purpose, such as /release-check |
| Hook | At a lifecycle event, every time | Rules that must hold even if the model disagrees, such as no localhost URL in Config files |
| MCP server | When its tools are called | Live systems: GitHub, your database, a browser for screenshots |
Yes. Project skills in .claude/skills/ are part of the repository, so cloud sessions load them, and the Claude Code GitHub Action can run a skill when the workflow checks out the repository first and passes /skill-name as the prompt.
When a request matches the description, yes. You can also run it yourself with /webviewgold-bridge. If you only want manual use, add disable-model-invocation: true to the frontmatter.
The guide explains the bridge to you and gives a one-time integration prompt. The skill is the permanent version for the agent: it loads by itself whenever a later task touches a native feature, months after you have forgotten the details.
SKILL.md follows the open Agent Skills format and several other agents can read it, but each tool looks in its own folder. Check that tool's documentation, or use the Cursor rules or AGENTS.md templates in this section.
Usually because several window.location.href assignments ran in the same script. iOS processes only one; combine data calls with multiapicall:// and fire haptics through a temporary iframe, as the helper does.
Same result, different amount of work on your side. All three come with the approval guarantee and 24/7 human support.
$109 per platform, one-time
The WebViewGold license: a finished Xcode or Android Studio project. Paste your URL, set name and icon, switch on features, build and submit. Free updates forever.
See plans on the main page$349 per license, one-time add-on
No Mac, no Xcode, no Android Studio: configure in the browser, we build both apps in the cloud and hand them to App Store Connect and the Play Console.
How the Cloud Builder works Try the free demo$698 per platform $798
Send us your URL and icon. Our team configures, builds and delivers your app within 72 hours, white-label, with source projects. Both platforms for $1,158.
Full Service details Powered byWinter Sale prices valid until December 31, 2026.
App Store & Google Play approval guarantee – or your money back within 14 days
Rather hand it over? Full Service: we configure, build and deliver within 72 hours, from $698 per platform. Questions? Chat with us 24/7.
Refunds are granted under the conditions listed in our FAQ: a non-content rejection by Apple and/or Google, claimed within 14 days after purchase.