Esc
 Naviguer  Ouvrir Esc Fermer
Aller au contenu

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é 13 min prérequis : axes 1-14 maîtrisés

Quel que soit le choix, ces problèmes restent à traiter :

SujetDétails
DistributionApp Store, Play Store, sideloading (Android), TestFlight
SignatureCertificats Apple ($99/an), keystore Android
Notifications pushAPNs (iOS), FCM (Android), Web Push (PWA)
Hors-ligneCache, sync différée, gestion conflits
CapteursGPS, caméra, biométrie, Bluetooth, NFC
StockageFichiers, DB locale (SQLite, IndexedDB)
Mise à jourOTA (over-the-air) ou via store
PermissionsDemande contextualisée, refus géré
AccessibilitéVoiceOver, TalkBack — différents de a11y web

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éiOS SafariChrome AndroidNotes
Installation home-screenPas via App Store
Offline / cacheService Worker
Push notifications✅ (iOS 16.4+)iOS exige l’install préalable
GéolocalisationAvec consentement
Caméra / micgetUserMedia
Background SyncPermet de retry en arrière-plan
Periodic Background Sync✅ avec restrictionsUpdate de contenu en arrière-plan
Bluetooth✅ avec restrictionsWeb Bluetooth
NFC✅ avec restrictionsWeb NFC (lecture)
Biométrie✅ via WebAuthn✅ via WebAuthnPas l’empreinte natif
Paiements✅ Apple Pay via JS✅ Google Pay via JSPas IAP store

Apple a longtemps freiné les PWA. Depuis iOS 16.4 (2023) : push notifications. Depuis iOS 17 : installprompt plus prévisible. En 2026, la PWA est une option crédible sur iOS.

// 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égieQuand
Cache-firstAssets immutables (/static/abc123.js)
Network-firstHTML, données dynamiques
Stale-while-revalidateAPI peu critiques (catalogue)
Cache-onlyMode offline strict
Network-onlyEndpoints 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é app
const sw = await navigator.serviceWorker.ready;
await sw.sync.register('sync-orders');
// Côté SW
self.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.

// Demande de permission + souscription
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
});
// Envoyer subscription au serveur
await 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',
}));

✅ 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.

  • 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 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.
Fenêtre de terminal
npx create-expo-app@latest my-app
cd my-app
npx expo start
app/profile.tsx
import { 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>
);
}
  • 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.
CritèreReact NativeFlutter
LangageTS / JSDart
RenduComposants natifsCanvas (Skia / Impeller)
LookDifférent par OS (par défaut)Identique partout (par défaut)
PerfBonne (excellente avec New Arch)Excellente
CommunautéPlus largeSolide, en croissance
Bundle~3-5 MB~5-10 MB
OTA updatesOui (EAS Update)Non officiel
RecrutementVivier 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 (Ionic) wrap ton app web dans un container natif :

Fenêtre de terminal
npm i @capacitor/core @capacitor/cli
npx cap init my-app com.example.app
npx cap add ios && npx cap add android

Ton 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.


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é.


  • 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.
  • 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.
  • TestFlight (iOS) — bêta jusqu’à 10 000 testeurs.
  • F-Droid (Android) — store OSS.
  • Sideloading direct (Android, Mac).
Fenêtre de terminal
npx eas-cli build --platform ios
# Build cloud, IPA téléchargeable

Indispensable pour les équipes qui n’ont pas de Mac local.


PlateformeServiceCôté app
iOSAPNsexpo-notifications, @react-native-firebase/messaging
AndroidFCMidem
Web (PWA)Web Push (VAPID)natif navigator.serviceWorker
Cross-platformOneSignal, Pusher Beams, Notif.iowrapper 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 ? »).


SymptômeSolution
Push permission au bootContextualiser : après value moment
Splash screen 5 sPréfetch en parallèle, splash visuel sub-1s
Login obligatoire dès l’ouverturePermettre l’usage anonyme + login différé
Liste de 10 000 items sans virtualisationFlatList (RN), lazy_load_indexed_stack (Flutter), react-window (web)
Animations sur le main thread JSReanimated worklets / GPU compositor
Pas de gestion offlineAu minimum : cacher les data récentes
App sans dark modeSuivre prefers-color-scheme ou useColorScheme()
Build Android / iOS uniquement quand on push en prodCI build à chaque PR (TestFlight + APK)

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 / Avalonia

Tu lances une app B2B (gestion d'inventaire) pour des clients qui l'utiliseront sur leurs propres tablettes. Pas besoin du store. Time-to-market 3 mois, équipe 100 % React. Quelle stratégie ?
Tu démarres une app consumer photo & vidéo, design pixel-perfect, animations complexes, ambition long terme. Stack idéale ?
Tu demandes la permission push notifications dès l'ouverture de l'app. Quel risque ?
Vous avez une PWA mature. Le client vous demande de la publier sur l'App Store iOS. Solution la plus rapide ?


Suite : 16.2 — Temps réel & collaboratif — WebSockets, WebRTC, CRDT.