16.1 — Mobile & cross-platform
🎯 Objectif : choisir une stratégie mobile cohérente avec ta cible. Pas de réponse universelle — la PWA suffit pour 60 % des cas, le natif est obligatoire pour les 10 % les plus exigeants. Entre les deux, React Native, Flutter et Capacitor servent des contextes précis.
À l'issue de cet axe, tu sauras :
- Évaluer si une PWA suffit (et quand elle ne suffit pas)
- Construire une PWA installable avec Service Worker, Background Sync et Push
- Comparer React Native / Expo, Flutter et Capacitor pour le cross-platform
- Anticiper les contraintes des stores (Apple, Google) et de la signature d'app
- Choisir entre une équipe web qui s'étend au mobile et une équipe mobile dédiée
Avancé
Le tronc commun mobile en 2026
Section intitulée « Le tronc commun mobile en 2026 »Quel que soit le choix, ces problèmes restent à traiter :
| Sujet | Détails |
|---|---|
| Distribution | App Store, Play Store, sideloading (Android), TestFlight |
| Signature | Certificats Apple ($99/an), keystore Android |
| Notifications push | APNs (iOS), FCM (Android), Web Push (PWA) |
| Hors-ligne | Cache, sync différée, gestion conflits |
| Capteurs | GPS, caméra, biométrie, Bluetooth, NFC |
| Stockage | Fichiers, DB locale (SQLite, IndexedDB) |
| Mise à jour | OTA (over-the-air) ou via store |
| Permissions | Demande contextualisée, refus géré |
| Accessibilité | VoiceOver, TalkBack — différents de a11y web |
PWA — la voie sous-estimée
Section intitulée « PWA — la voie sous-estimée »Une Progressive Web App est un site web avec :
- Service Worker (cache offline + sync en arrière-plan).
- Web App Manifest (icône, nom, splashscreen).
- HTTPS obligatoire.
- Installable depuis le navigateur.
Capacités PWA en 2026
Section intitulée « Capacités PWA en 2026 »| Capacité | iOS Safari | Chrome Android | Notes |
|---|---|---|---|
| Installation home-screen | ✅ | ✅ | Pas via App Store |
| Offline / cache | ✅ | ✅ | Service Worker |
| Push notifications | ✅ (iOS 16.4+) | ✅ | iOS exige l’install préalable |
| Géolocalisation | ✅ | ✅ | Avec consentement |
| Caméra / mic | ✅ | ✅ | getUserMedia |
| Background Sync | ❌ | ✅ | Permet de retry en arrière-plan |
| Periodic Background Sync | ❌ | ✅ avec restrictions | Update de contenu en arrière-plan |
| Bluetooth | ❌ | ✅ avec restrictions | Web Bluetooth |
| NFC | ❌ | ✅ avec restrictions | Web NFC (lecture) |
| Biométrie | ✅ via WebAuthn | ✅ via WebAuthn | Pas l’empreinte natif |
| Paiements | ✅ Apple Pay via JS | ✅ Google Pay via JS | Pas IAP store |
Apple a longtemps freiné les PWA. Depuis iOS 16.4 (2023) : push notifications. Depuis iOS 17 :
installpromptplus prévisible. En 2026, la PWA est une option crédible sur iOS.
Service Worker — l’essentiel
Section intitulée « Service Worker — l’essentiel »// sw.ts (compilé en sw.js)const CACHE = 'app-v1';const ASSETS = ['/', '/styles.css', '/app.js', '/offline.html'];
self.addEventListener('install', (event: any) => { event.waitUntil(caches.open(CACHE).then((c) => c.addAll(ASSETS)));});
self.addEventListener('activate', (event: any) => { event.waitUntil( caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))) ) );});
self.addEventListener('fetch', (event: any) => { // Network-first pour HTML, cache-first pour les assets versionnés const url = new URL(event.request.url); if (event.request.mode === 'navigate') { event.respondWith( fetch(event.request).catch(() => caches.match('/offline.html')) ); } else { event.respondWith( caches.match(event.request).then((r) => r ?? fetch(event.request)) ); }});Stratégies de cache courantes :
| Stratégie | Quand |
|---|---|
| Cache-first | Assets immutables (/static/abc123.js) |
| Network-first | HTML, données dynamiques |
| Stale-while-revalidate | API peu critiques (catalogue) |
| Cache-only | Mode offline strict |
| Network-only | Endpoints sensibles (auth) |
Workbox (Google) rend tout ça déclaratif. À utiliser plutôt que de réécrire un SW à la main.
Background Sync — retry quand revient le réseau
Section intitulée « Background Sync — retry quand revient le réseau »// Côté appconst sw = await navigator.serviceWorker.ready;await sw.sync.register('sync-orders');
// Côté SWself.addEventListener('sync', (event: any) => { if (event.tag === 'sync-orders') { event.waitUntil(syncPendingOrders()); }});Tu enqueue des actions hors-ligne dans IndexedDB ; le SW les rejoue dès que le réseau revient.
Web Push
Section intitulée « Web Push »// Demande de permission + souscriptionconst registration = await navigator.serviceWorker.ready;const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),});// Envoyer subscription au serveurawait fetch('/api/push-subscribe', { method: 'POST', body: JSON.stringify(subscription),});Côté serveur (Node) :
import webpush from 'web-push';
webpush.setVapidDetails('mailto:you@example.com', VAPID_PUBLIC, VAPID_PRIVATE);await webpush.sendNotification(subscription, JSON.stringify({ title: 'Nouvelle commande', body: 'Commande #1234 prête', url: '/orders/1234',}));Quand la PWA suffit
Section intitulée « Quand la PWA suffit »✅ Cas pour la PWA :
- App de contenu ou transactionnel léger (e-commerce, blog, dashboard, gestion).
- Cible mobile + desktop unifiée.
- Time-to-market court, équipe web déjà en place.
- Pas besoin des stores (revenu B2B direct, SaaS abonnement).
❌ Cas où la PWA ne suffit pas :
- App qui doit être dans l’App Store (consumer mass-market).
- Besoin de capteurs avancés (HealthKit, AR, NFC complet sur iOS).
- Contraintes de performance natives (jeu, vidéo, AR).
- Partenariat / SDK propriétaire iOS-only.
React Native / Expo
Section intitulée « React Native / Expo »React Native en 5 lignes
Section intitulée « React Native en 5 lignes »- JavaScript / TypeScript + JSX → composants natifs (UIView, View Android).
- Bridge JS ↔ natif (anciennement) ou New Architecture (TurboModules, Fabric, JSI).
- Code partagé iOS + Android, UI proche du natif.
Expo — la rampe d’accès recommandée 2026
Section intitulée « Expo — la rampe d’accès recommandée 2026 »Expo est devenu le standard pour démarrer en React Native :
- Expo Router — file-based routing à la Next.js.
- EAS Build — build cloud iOS sans Mac obligatoire.
- EAS Update — OTA updates (corrige bugs sans store).
- Config Plugins — étendre sans éjecter.
- Expo Modules — accès natif typed-safe.
npx create-expo-app@latest my-appcd my-appnpx expo startimport { Text, View } from 'react-native';import { useUser } from '@/hooks/useUser';
export default function Profile() { const user = useUser(); return ( <View> <Text>Bonjour {user.name}</Text> </View> );}React Native — choses à savoir en 2026
Section intitulée « React Native — choses à savoir en 2026 »- New Architecture (Fabric / TurboModules) est la norme — bien plus performante.
- Hermes — moteur JS optimisé RN, défaut.
- Reanimated 3 + gesture-handler — animations à 60-120 FPS sur main thread natif.
- React Native Skia — UI ultra-custom GPU.
- Performance proche du natif sur 95 % des cas.
- Réutilisation équipe React existante (~80 % de connaissances).
- OTA updates (corrige bugs sans repasser par store).
- Accès aux APIs natives via les modules Expo / community.
- Communauté énorme.
- UX iOS / Android un peu différentes par défaut — il faut polir.
- Certains modules natifs pas maintenus (audit avant intégration).
- Build initial complexe sans Expo.
Google. Dart + UI déclarative. Rendu propre (Skia/Impeller), pas via composants natifs.
- UI identique sur toutes les plateformes (rendu maison).
- Performance excellente (compile en code natif AOT).
- Outillage solide : DevTools, hot reload performant.
- Multi-plateforme : iOS, Android, web, desktop, embedded.
- Excellente pour apps fortement designées où le pixel-perfect compte.
- Dart — langage à apprendre, écosystème plus petit que JS.
- Bundle plus gros (~5-10 MB de runtime).
- Recrutement plus difficile que React.
- Moins idéal si l’équipe est déjà 100 % React.
Comparaison rapide React Native vs Flutter
Section intitulée « Comparaison rapide React Native vs Flutter »| Critère | React Native | Flutter |
|---|---|---|
| Langage | TS / JS | Dart |
| Rendu | Composants natifs | Canvas (Skia / Impeller) |
| Look | Différent par OS (par défaut) | Identique partout (par défaut) |
| Perf | Bonne (excellente avec New Arch) | Excellente |
| Communauté | Plus large | Solide, en croissance |
| Bundle | ~3-5 MB | ~5-10 MB |
| OTA updates | Oui (EAS Update) | Non officiel |
| Recrutement | Vivier large (React-friendly) | Plus tendu |
En 2026 : si l’équipe vient du web, React Native + Expo. Si tu pars from scratch sans contrainte ou que la qualité visuelle est centrale, Flutter est défendable.
Capacitor — l’autre voie web → mobile
Section intitulée « Capacitor — l’autre voie web → mobile »Capacitor (Ionic) wrap ton app web dans un container natif :
npm i @capacitor/core @capacitor/clinpx cap init my-app com.example.appnpx cap add ios && npx cap add androidTon code reste pur web (Vue / React / Astro / vanilla). Les APIs natives passent par des plugins JS :
import { Camera } from '@capacitor/camera';const photo = await Camera.getPhoto({ quality: 90 });- Reuse d’une app web existante quasi tel quel.
- Plus léger que React Native (pas de bridge complexe, pas de re-rendu à la marge).
- Même framework UI partout (la web).
- Idéal pour transformer une app web en app store rapidement.
- UX moins native (animations, scroll-feel, transitions).
- WebView a des limites (perfs sur grosses listes, animations 3D).
- Pour une app vraiment native dans le ressenti → React Native ou Flutter.
Capacitor est l’option idéale si tu as déjà une PWA mature et que tu veux la mettre au store sans réécrire.
Et le natif (Swift, Kotlin) ?
Section intitulée « Et le natif (Swift, Kotlin) ? »Toujours pertinent pour :
- Apps performance critique (jeux, AR, audio temps réel).
- Apps qui exploitent des SDK Apple/Google récents (HealthKit, Vision Pro, Core ML).
- Très grandes orgs où mobile = équipe dédiée de longue date.
- Apps consumer mass-market où chaque détail UX compte (Instagram, TikTok…).
Le natif coûte plus cher à entretenir (deux codebases), mais offre le plafond de qualité.
Distribution & stores
Section intitulée « Distribution & stores »App Store (Apple)
Section intitulée « App Store (Apple) »- Compte développeur 99 €/an (entreprise 299 €/an).
- Review 24-72 h habituellement, parfois plus pour les apps sensibles.
- 30 % de commission sur les achats in-app (15 % pour < 1 M$/an).
- Refus possibles : guidelines mouvantes, contenu, fonctionnalités.
Google Play
Section intitulée « Google Play »- Compte développeur 25 € à vie.
- Review automatique + manuelle, plus rapide que l’App Store.
- 15-30 % de commission.
- Sideloading possible (APK ou Play Console) — utile pour bêta privée.
Alternatives
Section intitulée « Alternatives »- TestFlight (iOS) — bêta jusqu’à 10 000 testeurs.
- F-Droid (Android) — store OSS.
- Sideloading direct (Android, Mac).
EAS Build (Expo) — sans Mac
Section intitulée « EAS Build (Expo) — sans Mac »npx eas-cli build --platform ios# Build cloud, IPA téléchargeableIndispensable pour les équipes qui n’ont pas de Mac local.
Notifications push
Section intitulée « Notifications push »| Plateforme | Service | Côté app |
|---|---|---|
| iOS | APNs | expo-notifications, @react-native-firebase/messaging |
| Android | FCM | idem |
| Web (PWA) | Web Push (VAPID) | natif navigator.serviceWorker |
| Cross-platform | OneSignal, Pusher Beams, Notif.io | wrapper unifié |
Anti-pattern : demander la permission au boot. iOS pénalise — tu n’auras jamais de seconde chance. Au lieu de ça, demande après une action contextuelle (« voulez-vous être notifié quand votre commande est prête ? »).
Anti-patterns mobiles
Section intitulée « Anti-patterns mobiles »| Symptôme | Solution |
|---|---|
| Push permission au boot | Contextualiser : après value moment |
| Splash screen 5 s | Préfetch en parallèle, splash visuel sub-1s |
| Login obligatoire dès l’ouverture | Permettre l’usage anonyme + login différé |
| Liste de 10 000 items sans virtualisation | FlatList (RN), lazy_load_indexed_stack (Flutter), react-window (web) |
| Animations sur le main thread JS | Reanimated worklets / GPU compositor |
| Pas de gestion offline | Au minimum : cacher les data récentes |
| App sans dark mode | Suivre prefers-color-scheme ou useColorScheme() |
| Build Android / iOS uniquement quand on push en prod | CI build à chaque PR (TestFlight + APK) |
Choisir — l’arbre de décision
Section intitulée « Choisir — l’arbre de décision »App déjà web mature ?├── Oui → Capacitor (port rapide) ou PWA si store optionnel└── Non └── L'équipe est React ? ├── Oui → React Native + Expo └── Non └── Besoin perf / qualité visuelle élite ? ├── Oui → Flutter ou natif └── Non → React Native + Expo (vivier de recrutement large)
Cas particuliers :- Mobile seulement, jeu / AR / vidéo → natif (Swift / Kotlin)- App jamais déployée au store, intranet → PWA pure- Stack Microsoft → MAUI / AvaloniaAuto-évaluation
Section intitulée « Auto-évaluation »Pour aller plus loin
Section intitulée « Pour aller plus loin »- web.dev / progressive-web-apps — web.dev/progressive-web-apps
- Workbox — developer.chrome.com/docs/workbox
- Expo docs — docs.expo.dev
- React Native docs — reactnative.dev
- Flutter docs — flutter.dev/docs
- Capacitor — capacitorjs.com
- PWABuilder — pwabuilder.com — packager PWA pour les stores
Suite : 16.2 — Temps réel & collaboratif — WebSockets, WebRTC, CRDT.