Aller au contenu
WebViewGold Du vibe coding à l'App Store
Français
Obtenir WebViewGold

ChatGPT & Codex

Un mode hors ligne et des états d'erreur qui passent l'App Review : un workflow Codex pour les apps WebViewGold

Certains examinateurs activent le mode avion, et tous finissent par tomber sur une requête lente. Une app web encapsulée a deux couches hors ligne : WebViewGold intercepte le moment où la page ne peut pas se charger du tout, et votre app web doit gérer chaque requête qui échoue une fois la page chargée. Codex convient bien à la seconde couche, car il peut lister chaque appel de données du code et prouver chaque état d'erreur avec des tests qui coupent le réseau.

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

L'objectif et le point de vue de l'examinateur

C'est sous la guideline 2.1 (App Completeness) que finissent les problèmes de hors-ligne : un indicateur de chargement qui ne s'arrête jamais, un écran blanc après une requête échouée, un « TypeError: Failed to fetch » brut sur une carte, un formulaire qui perd tout ce que vous avez saisi (le refus 2.1 expliqué). Google Play classe la même chose dans les fonctionnalités défaillantes. Rien de tout cela n'a besoin du mode avion pour se produire : une offre d'hébergement gratuite qui se met en veille après une période d'inactivité, une fonction serverless qui démarre à froid ou un réseau d'hôtel faible suffisent.

L'objectif : l'app n'affiche jamais d'écran vide ou figé. Au lancement sans réseau, un écran hors ligne natif avec un bouton pour réessayer apparaît. En cours de session, chaque écran affiche un état de chargement qui se termine, un état d'erreur avec un bouton Réessayer, ou des données en cache avec une mention indiquant qu'elles ne sont peut-être pas à jour.

La page dédiée au mode hors ligne compare les options natives, et le mode hors ligne pour les apps ChatGPT et Codex couvre la configuration pour les projets Codex.

Deux couches indispensables, deux en option

Écran hors ligne natif (WebViewGold)

Affiché quand l'app ne peut pas charger votre URL. Sur iOS, les textes de la boîte de dialogue et de l'écran proviennent de offlinetitle, offlinemsg, screen1, screen2 et offlinebuttontext, et offlinescreenautoretry réessaie chaque seconde sans qu'il faille toucher l'écran. Sur Android, OFFLINE_SCREEN_BACKGROUND_COLOR l'accorde à votre marque. Aucun code web n'est nécessaire.

États d'erreur dans l'app web (Codex)

Chaque requête reçoit un timeout, un état d'erreur avec possibilité de réessayer et un message lisible. C'est la couche sur laquelle les examinateurs tombent le plus souvent, et celle que ce guide automatise.

Page de repli intégrée (facultative)

Avec offlinelocalhtmlswitch (iOS) ou FALLBACK_USE_LOCAL_HTML_FOLDER_IF_OFFLINE (Android), l'app affiche hors ligne une page du dossier local-html du projet, et un bouton qui navigue vers reconnect:// recharge la dernière URL en ligne (API Reconnect). Sur iOS, zipfiledownloadfromserver et zipfileremoteurl permettent de mettre à jour cette page de repli depuis votre serveur.

Données en cache (facultatif, Firebase)

Le cache local persistant de Firestore sert hors ligne les derniers documents connus : les listes s'affichent donc toujours, avec une mention « dernière mise à jour », et les écritures sont mises en file d'attente jusqu'au retour de la connexion.

Le contrat des états d'erreur pour AGENTS.md

Écrivez les règles une fois pour toutes, sous forme d'invariants, dans l'AGENTS.md de l'app web. Chaque tâche ultérieure (« ajoutez une page de rapports ») arrive alors avec ses états d'erreur sans effort supplémentaire, et la section Review guidelines permet à la revue de code de Codex d'attraper les régressions sur les pull requests.

markdown
## Network and error states (every screen)
- All HTTP calls go through src/lib/request.ts: request(path, { timeoutMs = 10000 }).
  It uses AbortSignal.timeout, retries idempotent GETs once with backoff, and throws
  typed errors (OfflineError, TimeoutError, HttpError with status) that carry a
  user-facing message. Components never call fetch directly.
- Every data view renders exactly one of: loading (skeleton, ends after 10 s at the
  latest), content, empty (helpful text and a next action), error (message and a
  "Try again" button).
- No raw error text in the UI: no "TypeError", no stack traces, no JSON.
- Offline banner: listen to the online and offline events and render
  <div role="status">You are offline. Changes will sync when you are back.</div>
  Do not rely on navigator.onLine alone; a failed request also counts as offline.
- Forms keep their input when a submit fails. Submit buttons show progress and
  re-enable on error. Never send the same mutation twice without an idempotency key.
- Firestore: initializeFirestore with persistentLocalCache. Show "Last updated"
  when snapshot.metadata.fromCache is true.
- Express: error middleware returns JSON { error, message } with the right status,
  never an HTML error page. GET /healthz answers 200 without touching the database.
- No service worker for offline inside the native app. WebViewGold's offline screen
  and the bundled fallback page handle "no network at launch".

## Review guidelines
- P1: fetch() or axios outside src/lib/request.ts, or any request without a timeout.
- P1: a catch block that swallows an error without showing an error state.
- P1: a loading state with no exit (no timeout, no error branch).

Avec une bonne version optimisée de votre application Web, vous pouvez avoir une superbe application mobile en quelques minutes. De plus, le service client est très agréable et très utile! Je ne peux que recommander WebViewGold :-)

Julian Koch · Co-fondateur de DuelDonkey · CodeCanyon

Inventaire et correction : la tâche Codex

Celle-ci fonctionne bien en tâche cloud, car le résultat est un diff volumineux mais mécanique que vous voudrez lire sous forme de pull request. Dans l'environnement cloud, ajoutez npx playwright install --with-deps chromium au script de configuration pour que le navigateur soit prêt avant le démarrage de l'agent ; dans la CLI, Codex demande une approbation quand il a besoin du réseau pour l'installer. Les textes d'interface du prompt (« Try again », « You are offline », « Last updated ») restent en anglais pour correspondre aux tests plus bas ; si votre app est en français, adaptez les textes et les tests ensemble.

À coller dans Codex (tâche cloud, CLI ou extension IDE)
Lisez AGENTS.md, en particulier « Network and error states ». Cette app web s'exécute dans des apps natives iOS et Android créées avec WebViewGold, et l'App Review la teste sur de mauvais réseaux.

1. Inventaire : trouvez chaque appel réseau (fetch, axios, getDoc, getDocs et onSnapshot de Firestore, server-sent events ou WebSockets, téléversements de fichiers). Rédigez docs/network-inventory.md avec une ligne par appel : fichier, écran, appel, gestion actuelle du chargement, de l'état vide et des erreurs.
2. Créez src/lib/request.ts comme spécifié dans AGENTS.md et faites passer chaque appel HTTP par ce module.
3. Pour chaque écran de l'inventaire, implémentez les quatre états : un chargement qui se termine au plus tard après 10 secondes, le contenu, l'état vide, et l'erreur avec Try again. Réutilisez un seul composant ErrorState et un seul composant EmptyState.
4. Ajoutez la bannière hors ligne avec role="status", pilotée par les événements online et offline et par l'OfflineError de request().
5. Formulaires : conservez la saisie en cas d'échec, affichez l'erreur à côté du bouton d'envoi, réactivez le bouton.
6. Firestore : passez à persistentLocalCache et affichez « Last updated » quand snapshot.metadata.fromCache est true.
7. Express : un middleware d'erreur JSON, un handler 404 en JSON pour /api, et GET /healthz.
8. Créez native-fallback/index.html : un seul fichier autonome (CSS inline, le logo en SVG inline ou en data URI, aucune requête externe) avec le nom de l'app, le texte « You are offline » et un bouton Try again qui navigue vers reconnect://. Je le copierai dans le dossier local-html des projets natifs.
9. Tests Playwright : requêtes API interrompues, requêtes API qui ne répondent jamais, et contexte du navigateur passé hors ligne puis de nouveau en ligne. Chaque cas doit aboutir à un état d'erreur ou hors ligne en 12 secondes au plus, sans afficher de texte d'erreur brut. Installez Playwright s'il manque (demandez d'abord).
Terminez par l'inventaire, la liste des écrans modifiés et tout ce que vous n'avez pas pu couvrir.

Les tests qui le prouvent

Trois pannes réseau couvrent l'essentiel de ce que peut rencontrer un examinateur. Ces tests s'exécutent dans le sandbox de Codex et dans la CI, car ils n'ont besoin que du serveur de développement local.

javascript
// tests/offline.spec.js (Playwright)
import { test, expect } from "@playwright/test";

test("failed API shows a retry, not a white screen", async ({ page }) => {
  await page.route("**/api/**", (route) => route.abort("internetdisconnected"));
  await page.goto("/dashboard");
  await expect(page.getByRole("button", { name: /try again/i })).toBeVisible({ timeout: 12000 });
  await expect(page.getByText(/TypeError|Failed to fetch/)).toHaveCount(0);
});

test("hanging API never leaves an endless spinner", async ({ page }) => {
  await page.route("**/api/**", () => { /* never answer: a server that hangs */ });
  await page.goto("/dashboard");
  await expect(page.getByRole("button", { name: /try again/i })).toBeVisible({ timeout: 12000 });
});

test("offline banner appears and clears", async ({ page, context }) => {
  await page.goto("/dashboard");
  await context.setOffline(true);
  await expect(page.getByRole("status")).toContainText(/offline/i);
  await context.setOffline(false);
  await expect(page.getByRole("status")).toHaveCount(0);
});

Les réglages hors ligne de WebViewGold en un coup d'œil

ObjectifiOS (Config.swift)Android (Config.java)
Textes hors ligne et libellé du boutonofflinetitle, offlinemsg, screen1, screen2, offlinebuttontext (traduisibles via alternatelanguage1 et alternatelanguage2)Aucune clé de texte documentée dans Config.java ; couleur de fond via OFFLINE_SCREEN_BACKGROUND_COLOR
Réessayer sans toucher l'écranofflinescreenautoretryPas d'option documentée ; utilisez une page de repli avec reconnect://
Page de repli intégréeofflinelocalhtmlswitch à true, page dans le dossier local-htmlFALLBACK_USE_LOCAL_HTML_FOLDER_IF_OFFLINE à true, page dans le dossier local-html
Réessayer depuis la page de replireconnect://reconnect://
Mettre à jour la page de repli à distancezipfiledownloadfromserver et zipfileremoteurlLivrez-la avec le build
Actualiser quand l'app revient de l'arrière-planautoRefreshEnabledAUTO_REFRESH_ENABLED

Mettez-le en production

  1. Ajoutez le contrat à AGENTS.md

    Collez la section ci-dessus et adaptez les chemins et les timeouts à votre projet. Commitez-la avant l'exécution de la tâche, pour qu'une tâche cloud la voie.

  2. Lancez la tâche d'inventaire et de correction

    En tâche cloud, vous obtenez un diff et une pull request ; dans la CLI, utilisez codex --sandbox workspace-write --ask-for-approval on-request. Dans les deux cas, les tests Playwright doivent passer.

  3. Relisez en vous appuyant sur l'inventaire

    Chaque ligne de docs/network-inventory.md doit correspondre à un écran avec quatre états. Commentez @codex review sur la pull request pour appliquer une seconde fois les review guidelines.

  4. Configurez la couche native

    Rédigez les textes hors ligne dans le ton et la langue de votre app, décidez pour offlinescreenautoretry, définissez la couleur de fond Android et, si vous voulez la page de repli, copiez native-fallback/index.html vers local-html/index.html et activez offlinelocalhtmlswitch ou FALLBACK_USE_LOCAL_HTML_FOLDER_IF_OFFLINE (documentation Reconnect Android).

  5. Coupez le réseau exprès

    Mode avion au lancement et en cours de session, sur de vrais appareils. Sur iPhone, le Network Link Conditioner dans les réglages Développeur (disponible une fois l'appareil connecté à Xcode) simule des réseaux très mauvais et une coupure totale ; sur Android, utilisez le mode avion ou les réglages réseau de l'émulateur.

  6. Prévenez l'examinateur

    Si l'app a besoin d'une connexion Internet, dites-le dans les notes pour l'examen, en précisant ce que l'app affiche hors ligne. Pour le reste de la soumission, consultez le workflow de publication avec Codex.

Le scénario mode avion (à dérouler avant l'examinateur)

  • Démarrage à froid en mode avion : l'écran hors ligne natif apparaît, aux couleurs de la marque et lisible, et le bouton pour réessayer fonctionne dès que le réseau revient.
  • Mode avion en cours de session : la bannière hors ligne apparaît, l'écran actuel garde son contenu, et les actions affichent un message clair.
  • Retour en ligne : la bannière disparaît, Réessayer recharge, et rien n'a été envoyé deux fois.
  • Un serveur qui ne répond plus (arrêtez votre API, ou utilisez un profil à 100 % de perte dans le Network Link Conditioner) : chaque indicateur de chargement s'arrête en une dizaine de secondes.
  • Page de repli, si elle est activée : elle se charge sans réseau, n'a ni image ni police cassée, et reconnect:// ramène à la dernière page.
  • Laissez l'app un moment en arrière-plan puis revenez-y : avec autoRefreshEnabled ou AUTO_REFRESH_ENABLED, la page s'actualise ; sans, les écrans périmés se comportent quand même correctement.
  • La session connectée survit à un lancement hors ligne ; personne n'est déconnecté par une erreur réseau.

Pièges

  • Se fier à navigator.onLine. Il reste à true derrière un portail Wi-Fi captif ou sur une connexion morte ; traitez aussi comme hors ligne les requêtes échouées ou expirées.
  • Une page de repli qui charge des polices web, un script de CDN ou un logo distant. Hors ligne, ces requêtes échouent et la page paraît cassée. Mettez tout en inline.
  • Les tempêtes de nouvelles tentatives : des boutons qui déclenchent plusieurs requêtes par toucher, ou des tentatives automatiques sans backoff qui martèlent une API déjà en difficulté.
  • Express ou un proxy qui répond par une page d'erreur HTML, que le client tente d'analyser comme du JSON (« Unexpected token < »). Le middleware d'erreur JSON règle le problème à la source.
  • Bâtir la stratégie hors ligne sur un service worker. Sur iOS, WKWebView n'exécute en général les service workers que pour les apps configurées avec des App-Bound Domains : considérez-le comme un bonus pour les utilisateurs du navigateur.
  • Une offre d'hébergement gratuite qui se met en veille : la première requête après une période calme prend assez de temps pour ressembler à un blocage. Gardez l'API éveillée ou passez à une offre supérieure avant de soumettre.

Questions fréquentes

Mon app doit-elle fonctionner entièrement hors ligne pour passer l'examen ?

Non. Les apps qui ont besoin d'un serveur peuvent exiger une connexion, à condition de bien gérer son absence : un écran hors ligne clair, des états d'erreur avec possibilité de réessayer, aucun écran figé ou vide. Mentionnez dans les notes pour l'examen que l'app a besoin d'une connexion Internet.

Quelle est la différence entre l'écran hors ligne et la page de repli ?

L'écran hors ligne est natif et se configure avec quelques textes et options. La page de repli est votre propre HTML dans le dossier local-html du projet, affichée à la place quand offlinelocalhtmlswitch (iOS) ou FALLBACK_USE_LOCAL_HTML_FOLDER_IF_OFFLINE (Android) est activé, et elle peut proposer un bouton reconnect://.

Codex peut-il tester le comportement hors ligne ?

Oui, pour la couche web. Playwright peut interrompre des requêtes, les laisser sans réponse et passer le contexte du navigateur hors ligne, le tout dans le sandbox de Codex. L'écran hors ligne natif et la page de repli exigent toujours un test sur appareil en mode avion.

Faut-il activer la persistance hors ligne de Firestore ?

Pour les apps surtout en lecture, oui : les listes s'affichent depuis le cache et les écritures se synchronisent plus tard. Indiquez quand les données proviennent du cache, et pensez aux appareils partagés avant de mettre en cache des données sensibles.

AbortSignal.timeout est-il pris en charge dans l'app ?

Oui, dans les WebViews système actuelles ; Safari le prend en charge depuis la version 16. Si vous prenez en charge des versions d'iOS plus anciennes, demandez à Codex un AbortController avec setTimeout comme solution de repli dans request.ts.

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.

Voir les offres sur la page principale

Full Service

$698 par plateforme $798

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.

Détails du Full Service Propulsé par WeSetupYourWebViewApp

Prix Soldes d'hiver valables jusqu'au 31 décembre 2026.

Prêt à publier votre app ?

Garantie d'approbation App Store & Google Play – ou remboursement sous 14 jours

Vous préférez déléguer ? Full Service : nous configurons, compilons et livrons sous 72 heures, dès $698 par plateforme. Des questions ? Chattez avec nous 24/7.

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.