Ajouter les notifications push OneSignal à une app web créée avec Codex grâce à WebViewGold
Le push est la fonctionnalité native que l'App Review remarque en premier, et l'essentiel du travail se trouve dans votre app web et votre backend : c'est donc une bonne tâche pour Codex. WebViewGold embarque déjà le SDK OneSignal dans les deux projets natifs ; Codex construit le moment de l'opt-in, synchronise l'ID d'abonnement de chaque appareil avec votre serveur et écrit l'endpoint qui envoie des notifications ciblées. Voici la répartition des rôles, les règles AGENTS.md et un prompt qui évite les erreurs classiques.
Dès $109 en paiement unique par plateforme, ou notre équipe s'occupe de tout dès $698.
Garantie d'approbation – ou remboursement sous 14 jours
Aucun codage requis
Paiement unique, mises à jour gratuites
Support humain 24/7
Conçu en Allemagne depuis 2014 · ISO 27001
9:41
Votre app
Votre appDésormais dans votre poche
App Store · Google Play Approuvée
L'objectif, et ce que voit l'examinateur
L'objectif est un push propre à chaque utilisateur : « Nouvelle réponse à votre commentaire » sur le téléphone d'une personne, et non un envoi groupé à tous ceux qui ont installé l'app. Il faut pour cela trois éléments : l'app native enregistrée auprès de OneSignal, un serveur qui sait quel abonnement OneSignal appartient à quel utilisateur, et un appel d'envoi qui ouvre le bon écran dans l'app quand on touche la notification.
L'examinateur en voit moins que vous ne le pensez, mais il le voit tôt. Une boîte de dialogue d'autorisation qui surgit avant même que le premier écran ne s'affiche donne l'impression d'un site web qui réclame des autorisations sans raison. La guideline 4.5.4 d'Apple précise aussi que le push ne doit pas être indispensable au fonctionnement de l'app, et que les push promotionnels exigent un opt-in explicite dans votre propre interface ainsi qu'un moyen de se désinscrire. Donc : demandez après une action significative, expliquez ce que les gens vont recevoir et gardez un interrupteur dans les réglages. Fait ainsi, le push est l'un des signes les plus clairs de fonctionnalité native au titre de la guideline 4.2 (ce que la 4.2 attend des apps web).
Ce guide suppose un front-end React, une API Express et Firestore, avec le helper de user agent isNativeApp() issu du modèle AGENTS.md pour WebViewGold. Le principe s'applique à n'importe quelle stack.
Qui fait quoi
Élément
Où il se trouve
Qui
App OneSignal avec les identifiants APNs (iOS) et FCM (Android)
Tableau de bord OneSignal
Vous, une seule fois
OneSignal activé dans les apps natives
iOS : kPushEnabled à true et oneSignalID dans Config.swift. Android : PUSH_ENABLED à true dans Config.java, App ID dans ONE_SIGNAL_APP_ID dans build.gradle
Vous, ou Codex dans le projet natif
Demande au premier lancement désactivée
askforpushpermissionatfirstrun à false (Config.swift), requirePush à false (Config.java)
Vous
Feuille d'opt-in qui déclenche la boîte de dialogue native
App web, navigation vers registerpush://
Codex
ID d'abonnement transmis à votre serveur
getonesignalplayerid:// remplit onesignalplayerid ; ou activez autoInjectVariable / AUTO_INJECT_VARIABLES ; ou recevez ?onesignal_push_id= sur la première URL via kPushEnhanceUrl / PUSH_ENHANCE_WEBVIEW_URL
Codex (web et Express)
État dans les réglages
checkNativePushPermissions:// remplit nativePushEnabled ; settingsapp:// ouvre les réglages système de l'app
Codex
Envoi
Express appelle l'API REST de OneSignal
Codex, relu par vous
La section AGENTS.md pour le push
Ajoutez ceci à la fin de votre AGENTS.md. Codex le lit au début de chaque session, et c'est la rubrique Review guidelines que suit la revue de code de Codex quand vous commentez @codex review sur une pull request. Chaque schéma d'URL et chaque clé provient de la documentation push iOS et de la documentation push Android.
markdown
## Push notifications (OneSignal via WebViewGold)
- Native side: OneSignal is enabled in the WebViewGold projects
(iOS: kPushEnabled + oneSignalID; Android: PUSH_ENABLED + ONE_SIGNAL_APP_ID).
The first-run prompt is OFF (askforpushpermissionatfirstrun / requirePush = false).
- Never add the OneSignal Web SDK, a web push service worker or
Notification.requestPermission(). In the app, permission is requested only by
navigating to registerpush://, always behind isNativeApp().
- Permission status: checkNativePushPermissions:// sets window.nativePushEnabled
(asynchronously). If false, offer a button that navigates to settingsapp://.
- Device identity: getonesignalplayerid:// sets window.onesignalplayerid
(asynchronously; may be empty before the user allows push). Poll with a timeout.
On iOS, combine data calls: multiapicall://getonesignalplayerid,getappversion
- Storage: pushDevices/{subscriptionId} = { uid, platform, updatedAt }.
One user can have many devices. Delete the row on logout.
- Sending happens only on the server (server/push/send.js), with ONESIGNAL_API_KEY
from the environment. Deep links go in data.url (opens inside the app).
Never set OneSignal's top-level url field: that is the Launch URL and opens the browser.
- Promotional pushes need explicit opt-in in our UI and an opt-out in settings.
## Review guidelines
- P1: ONESIGNAL_API_KEY, or any call to api.onesignal.com, in client code.
- P1: a notification payload with a top-level url instead of data.url.
- P1: registerpush:// or getonesignalplayerid:// without an isNativeApp() check.
WebViewGold nous a permis d'avoir le temps de mise sur le marché le plus court et le plus efficace jamais possible parmi toutes les autres plates-formes d'application que nous avons essayées au cours des 5 dernières années. Je ne sais pas pourquoi je ne vous ai pas découvert plus tôt, mais je suis vraiment, vraiment content de l'avoir fait. Nous approchons actuellement les 6000 téléchargements d'application sur l'App Store iOS et Google Play Store sans aucun problème!
Lancez Codex dans le dépôt avec un accès en écriture à l'espace de travail et des approbations à la demande : codex --sandbox workspace-write --ask-for-approval on-request. Dans ce mode, le réseau est coupé par défaut, ce qui convient à cette tâche : elle doit produire du code et des tests avec mocks, pas de vraies notifications. Remplacez les parties entre crochets.
À coller dans la CLI Codex ou l'extension IDE
Lisez d'abord AGENTS.md, en particulier le bridge WebViewGold et la section sur le push. Implémentez ensuite les notifications push de bout en bout dans ce dépôt. Ne touchez pas aux projets natifs.
Contexte : les apps natives iOS et Android utilisent OneSignal via WebViewGold. La demande d'autorisation au premier lancement est désactivée (askforpushpermissionatfirstrun = false dans Config.swift, requirePush = false dans Config.java) : c'est donc notre app web qui décide quand demander.
1. Opt-in : après que l'utilisateur [a créé son premier projet], affichez une courte feuille qui explique quelles notifications il recevra ([réponses, rappels]), avec les boutons Activer et Pas maintenant. Activer navigue vers registerpush:// quand isNativeApp() est true. Mémorisez Pas maintenant pendant 14 jours. N'affichez jamais la feuille dans les navigateurs.
2. Synchronisation de l'abonnement : après la connexion et après l'opt-in, naviguez vers getonesignalplayerid:// et attendez window.onesignalplayerid (vérifiez toutes les 200 ms, abandonnez après 5 s, ignorez les valeurs vides). Envoyez-le en POST avec la plateforme à /api/push/devices, authentifié avec le Firebase ID token. Le serveur stocke pushDevices/{subscriptionId} = { uid, platform, updatedAt }, de sorte qu'un téléphone qui change de mains passe au nouvel utilisateur. À la déconnexion, appelez DELETE /api/push/devices/:id avant de déconnecter l'utilisateur.
3. Réglages : une ligne Notifications. Naviguez vers checkNativePushPermissions:// et lisez window.nativePushEnabled après un court délai. Si la valeur est false, affichez « Les notifications sont désactivées » et un bouton qui navigue vers settingsapp://.
4. Envoi : server/push/send.js, qui exporte sendPushToUser(uid, { title, body, path }). Il charge les pushDevices de l'utilisateur, appelle l'API REST de OneSignal avec include_subscription_ids et place le deep link (notre origine de production + path) dans data.url. N'utilisez jamais le champ url de premier niveau. Lisez ONESIGNAL_APP_ID et ONESIGNAL_API_KEY dans l'environnement et ajoutez les deux à .env.example sans valeur. Appelez la fonction depuis [le handler des nouvelles réponses].
5. Tests : des tests unitaires pour les routes des appareils (authentification requise, upsert, suppression limitée à ses propres lignes) et pour sendPushToUser avec fetch mocké. N'appelez pas la vraie API OneSignal.
6. Terminez par : les fichiers modifiés, les variables d'environnement que je dois définir et une liste numérotée de tests sur appareil.
À quoi doit ressembler le côté serveur
Servez-vous-en comme référence quand vous lisez le diff. Deux lignes comptent plus que tout : la clé API ne quitte jamais le serveur, et le deep link se trouve dans data.url. Le champ url de premier niveau de OneSignal est la Launch URL, qui ouvre un navigateur ; la documentation WebViewGold demande explicitement d'utiliser à la place le champ de données supplémentaires url.
javascript
// server/push/index.js (Express + firebase-admin, Node 18+ for global fetch)
import express from "express";
import { getFirestore, FieldValue } from "firebase-admin/firestore";
import { requireUser } from "../auth/requireUser.js"; // verifies the Firebase ID token, sets req.uid
const db = getFirestore();
export const pushRouter = express.Router();
// One row per device. The document ID is the OneSignal subscription ID, so a phone
// that changes hands (logout, login as someone else) simply moves to the new user.
pushRouter.post("/api/push/devices", requireUser, express.json(), async (req, res) => {
const { subscriptionId, platform } = req.body || {};
if (!subscriptionId) return res.status(400).json({ error: "subscriptionId required" });
await db.collection("pushDevices").doc(subscriptionId).set({
uid: req.uid, platform: platform || "unknown", updatedAt: FieldValue.serverTimestamp()
});
res.status(204).end();
});
pushRouter.delete("/api/push/devices/:id", requireUser, async (req, res) => {
const ref = db.collection("pushDevices").doc(req.params.id);
const snap = await ref.get();
if (snap.exists && snap.get("uid") === req.uid) await ref.delete();
res.status(204).end();
});
export async function sendPushToUser(uid, { title, body, path }) {
const devices = await db.collection("pushDevices").where("uid", "==", uid).get();
const ids = devices.docs.map((d) => d.id);
if (ids.length === 0) return 0;
const res = await fetch("https://api.onesignal.com/notifications?c=push", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Key " + process.env.ONESIGNAL_API_KEY // server only
},
body: JSON.stringify({
app_id: process.env.ONESIGNAL_APP_ID,
target_channel: "push",
include_subscription_ids: ids,
headings: { en: title },
contents: { en: body },
// WebViewGold opens this inside the app. Not the top-level "url" (Launch URL).
data: { url: "https://app.example.com" + path }
})
});
if (!res.ok) throw new Error("OneSignal " + res.status + ": " + (await res.text()));
return ids.length;
}
De la configuration à la première notification sur un appareil
Prévoyez un après-midi ; l'essentiel consiste à attendre les builds.
Créez l'app OneSignal
Ajoutez la plateforme Apple iOS avec vos identifiants APNs et la plateforme Google Android avec le JSON du compte de service Firebase que demande OneSignal. Copiez l'App ID et la clé API de l'app. La clé va dans l'environnement de votre serveur, jamais dans AGENTS.md, dans un environnement de tâche cloud que vous partagez ou dans le bundle client.
Activez OneSignal dans WebViewGold
iOS : kPushEnabled à true, votre App ID dans oneSignalID, askforpushpermissionatfirstrun à false, et la capability Push Notifications activée pour votre bundle ID. Android : PUSH_ENABLED à true, l'App ID dans ONE_SIGNAL_APP_ID dans build.gradle, requirePush à false. Config.swift et Config.java sont du texte brut : une seconde session Codex dans le projet WebViewGold peut donc définir exactement ces valeurs ; la signature reste entre vos mains.
Lancez la tâche web et relisez-la
Collez le prompt ci-dessus. Avant d'accepter le résultat, tapez /review dans la CLI pour une seconde passe sur les modifications non commitées, puis lisez vous-même le git diff avec les règles AGENTS.md à côté. Cherchez d'abord les deux règles P1.
Déployez et configurez l'environnement
Définissez ONESIGNAL_APP_ID et ONESIGNAL_API_KEY sur le serveur et déployez l'app web et l'API. Les projets natifs ne changent pas à cette étape : c'est tout l'intérêt de garder la logique côté web.
Compilez pour de vrais appareils
TestFlight pour iOS, une piste de test interne pour Android. Le push exige un build signé sur du matériel réel. Pas de Mac sous la main ? Le Cloud Builder définit les mêmes options WebViewGold dans un formulaire en ligne et compile les deux apps.
Envoyez deux fois
D'abord depuis le tableau de bord OneSignal avec un champ de données supplémentaires url, ce qui valide le côté natif. Ensuite en déclenchant votre propre événement (une réponse, un rappel), ce qui valide le serveur, l'association des appareils et le deep link.
Checklist de test sur appareil
Installation neuve : aucune boîte de dialogue d'autorisation au lancement.
La feuille d'opt-in apparaît après l'action déclencheuse ; Activer affiche la boîte de dialogue native ; Pas maintenant est mémorisé.
Après l'autorisation, l'ID d'abonnement de cet appareil est enregistré pour l'utilisateur connecté ; un second appareil ajoute une seconde ligne.
Déconnexion puis connexion avec un autre utilisateur sur le même téléphone : la ligne passe au nouvel utilisateur, et l'ancien ne reçoit plus rien sur ce téléphone.
Refusez l'autorisation, puis ouvrez les réglages dans l'app : l'état indique désactivé, et le bouton ouvre les réglages système via settingsapp://.
Touchez une notification avec l'app fermée puis avec l'app en arrière-plan : l'écran cible s'ouvre dans l'app, pas dans Safari ou Chrome.
Android 13 ou plus récent : la boîte de dialogue système apparaît ; nativePushEnabled n'est true que si le réglage des notifications et l'autorisation d'exécution sont tous deux accordés.
Les erreurs que Codex commet sans les règles
Ajouter le SDK Web de OneSignal, un service worker ou Notification.requestPermission(). Le web push n'est pas le bon canal dans l'app ; WebViewGold embarque déjà le SDK natif.
Lire window.onesignalplayerid juste après la navigation. Les variables injectées arrivent de manière asynchrone : interrogez-les à intervalles réguliers avec un timeout.
Enchaîner plusieurs appels au bridge sur iOS, où ils ne s'exécutent pas tous. Combinez les appels de données avec multiapicall://getonesignalplayerid,getappversion (voir la documentation MultiAPICall).
Stocker un seul ID push par utilisateur. Les gens ont un téléphone et une tablette ; stockez une ligne par abonnement.
Placer le deep link dans le champ url de premier niveau, ce qui envoie les utilisateurs vers le navigateur.
Brancher registerpush:// alors qu'aucun service push n'est activé dans la configuration native. Il ne se passe rien, et le bouton semble cassé.
Questions fréquentes
Puis-je utiliser Firebase Cloud Messaging ou Pushwoosh au lieu de OneSignal ?
Oui. WebViewGold prend en charge OneSignal, Firebase et Pushwoosh. Pour Firebase, activez kFirebasePushEnabled (iOS) ou FIREBASE_PUSH_ENABLED (Android), ajoutez les fichiers de service Google et lisez le token avec getfirebaseplayerid://, qui remplit firebaseplayerid. Les règles AGENTS.md restent les mêmes ; seuls les noms de variables et l'API d'envoi changent.
Codex peut-il tester les notifications push dans son sandbox ?
Pas de bout en bout. Un push exige un build signé sur un vrai appareil et des identifiants réels. Codex peut tester unitairement les routes des appareils et la fonction d'envoi avec fetch mocké, ce qui attrape la plupart des bugs ; la dernière étape, c'est votre test sur appareil.
Pourquoi stocker les ID d'abonnement plutôt que d'utiliser les external IDs de OneSignal ?
Parce que le bridge WebViewGold transmet à votre app web l'ID d'abonnement (getonesignalplayerid:// ou la variable injectée) : c'est donc l'identifiant sur lequel vous pouvez compter. Associer les ID d'abonnement à votre propre ID utilisateur permet aussi de gérer correctement la déconnexion, le changement de compte et la suppression de compte.
Faut-il laisser activée la demande d'autorisation au premier lancement ?
Seulement si le push est au cœur de l'app dès la première seconde, par exemple pour une app d'alertes. Pour la plupart des apps, demander après une action significative obtient plus de oui et ressemble moins à un site web qui réclame des autorisations.
Le push suffit-il pour passer la guideline 4.2 ?
Il aide, mais aucune fonctionnalité ne garantit à elle seule l'approbation. Les examinateurs jugent l'expérience dans son ensemble : une mise en page mobile soignée, une navigation digne d'une app, la gestion du hors-ligne et des touches natives comme le push et le partage.
Trois façons de mettre votre app dans les stores
Même résultat, charge de travail différente de votre côté. Les trois formules incluent la garantie d'approbation et le support humain 24/7.
Faites-le vous-même
$109 par plateforme, paiement unique
La licence WebViewGold : un projet Xcode ou Android Studio prêt à l'emploi. Collez votre URL, définissez le nom et l'icône, activez les fonctionnalités, compilez et soumettez. Mises à jour gratuites à vie.
Ni Mac, ni Xcode, ni Android Studio : configurez dans le navigateur, nous compilons les deux apps dans le cloud et les transmettons à App Store Connect et à la Play Console.
Envoyez-nous votre URL et votre icône. Notre équipe configure, compile et livre votre app sous 72 heures, en marque blanche, projets sources inclus. Les deux plateformes pour $1,158.
Les remboursements sont accordés aux conditions indiquées dans notre FAQ : un refus non lié au contenu par Apple et/ou Google, demandé dans les 14 jours suivant l'achat.