Expo : la plateforme
Halterofit est une app React Native, mais tu ne verras presque jamais de code Xcode ni
d'Android Studio en la lisant. La raison tient en un mot : Expo. C'est la
couche qui prend en charge tout le « pénible natif » à ta place. Comprendre Expo, c'est
comprendre pourquoi ce projet a la forme qu'il a — un seul dossier
apps/mobile, un fichier app.json qui décrit l'app, et des builds
qui se font « dans le nuage » sans que tu installes quoi que ce soit de lourd.
React Native te laisse écrire ton interface en JavaScript/TypeScript, mais une app mobile, c'est aussi du code natif : compilation iOS/Android, accès à la caméra, aux notifications, aux capteurs, à l'écran… Tout ça, normalement, demande des outils lourds et des connaissances spécifiques à chaque plateforme. Expo est la plateforme qui gère cette partie native à ta place, pour que tu restes le plus possible dans le confort du JavaScript. C'est le grand thème de ce module : Expo = React Native sans (presque) la douleur native.
1. Qu'est-ce qu'Expo, au juste ?
Première confusion à dissiper : Expo, ce n'est pas une alternative à React
Native. C'est une couche par-dessus React Native. React Native te donne les briques
de base — des composants (View, Text, Pressable…) qui
se transforment en vrais composants natifs iOS et Android. Mais React Native « nu » te laisse
seul face à toute la machinerie autour : configurer le projet Xcode, le projet Android,
installer et lier des modules natifs à la main, gérer les permissions, compiler, signer,
distribuer… C'est précisément là qu'Expo intervient.
Expo est à la fois un framework et une plateforme. Comme framework, il fournit un grand catalogue de modules prêts à l'emploi (le « SDK Expo », qu'on détaille plus bas) pour accéder au natif depuis du JavaScript : notifications, image, audio, son, écran qui reste allumé, cryptographie, liens profonds… Comme plateforme, il fournit des services autour de ton app : la compilation dans le cloud (EAS Build), les mises à jour à distance (OTA), la soumission aux stores. Tu écris du TypeScript, et Expo orchestre le reste.
Expo, c'est React Native sans (presque) la douleur de Xcode et Android Studio. Imagine que React Native te donne un moteur et quatre roues, mais que tu doives assembler toi-même la voiture, fabriquer le tableau de bord, et apprendre la mécanique des deux constructeurs. Expo te livre la voiture montée, avec un tableau de bord standard et un garagiste dans le cloud. Tu gardes le volant (ton code), tu perds la corvée du montage.
Pourquoi ce point compte tant pour lire Halterofit ? Parce que la quasi-totalité de
ce que tu liras dans le projet est du TypeScript et du JSX. Tu ne croiseras presque jamais de
fichier .swift, .kotlin, .gradle ou
.xcodeproj — non parce qu'ils n'existent pas, mais parce qu'Expo les
génère à partir d'une configuration déclarative. Le projet décrit ce qu'il
veut (« je veux des notifications », « je veux du son »), et Expo fabrique le natif
correspondant. Tu lis des intentions, pas de la plomberie.
2. Le spectre « managed → bare », et où se situe Halterofit
Historiquement, on présentait Expo comme un choix binaire entre deux mondes. À une
extrémité, le mode managed (« géré ») : tu ne touches jamais au code natif,
Expo gère tout, et ton app tourne dans une application générique appelée Expo Go
(on y revient). C'est ultra-rapide à démarrer, mais limité : tu ne peux utiliser que les
modules qu'Expo a déjà inclus. À l'autre extrémité, le mode bare (« nu ») :
tu as un vrai projet React Native avec les dossiers ios/ et android/
ouverts, tu peux tout faire, mais tu retrouves toute la complexité native.
La réalité moderne — et celle de Halterofit — est plus subtile et bien meilleure que ce
binaire. L'approche actuelle s'appelle le development build (ou « dev
client »), avec le système des config plugins. L'idée est élégante : tu
restes dans le confort déclaratif du mode managed (tu n'édites pas les dossiers natifs à la
main), mais tu peux ajouter n'importe quel module natif custom. Les dossiers
ios/ et android/ sont considérés comme « jetables » : ils sont
régénérés à partir de ta config par une commande appelée prebuild. On parle de
Continuous Native Generation (génération native continue).
Comment sait-on que Halterofit suit exactement cette voie ? Trois indices concrets, lus dans le vrai projet :
-
Le
package.jsonlisteexpo-dev-clientdans les dépendances. C'est la brique du development build. -
Le
app.jsoncontient un blocdevelopmentClient(avecsilentLaunch), preuve qu'on configure ce dev client. -
Le
app.jsondéclare une liste deplugins(config plugins) — par exempleexpo-splash-screen,expo-notifications,expo-audio, le plugin Sentry, etc. Chacun injecte de la configuration native.
Halterofit utilise des modules qui n'existent pas dans Expo Go : par
exemple react-native-mmkv (stockage rapide), @nozbe/watermelondb
(base de données locale), @shopify/react-native-skia (graphismes),
react-native-nitro-modules… Ce sont des modules natifs « custom ». Dès qu'une
app en contient, Expo Go ne peut plus la faire tourner. Halterofit doit
donc utiliser un dev client. Ce n'est pas un choix de style : c'est une conséquence directe
de ses dépendances.
3. Expo Go vs dev client : la distinction qui piège tout le monde
C'est la confusion n°1 chez les gens qui découvrent Expo, alors prenons le temps. Les deux servent à tester ton app sur un téléphone pendant le développement, mais ils ne sont pas du tout interchangeables.
Expo Go est une application déjà compilée, que tu télécharges depuis l'App Store ou le Play Store. Elle contient un ensemble figé de modules natifs — ceux qu'Expo a décidé d'embarquer. Quand tu « lances » ton app dans Expo Go, tu ne compiles rien : Expo Go télécharge ton bundle JavaScript et l'exécute par-dessus son propre code natif. C'est magique de simplicité… tant que ton app n'a besoin que de modules natifs déjà présents dans Expo Go. Le jour où tu ajoutes un module natif qui n'y est pas, le code natif correspondant n'existe pas dans Expo Go, et ça plante.
Le development build (dev client) renverse la logique. Au lieu d'utiliser une
app générique, tu compiles ta propre version d'Expo Go, taillée pour ton
projet. Cette app custom contient exactement tes modules natifs à toi —
react-native-mmkv, WatermelonDB, Skia, etc. — parce qu'ils ont été compilés
dedans. Ensuite, elle se comporte comme Expo Go : tu modifies ton TypeScript, ça se
recharge à chaud. Tu gardes la boucle de développement rapide, mais sans la limite des
modules.
Le JavaScript se télécharge et s'exécute à chaud, oui. Mais le code natif (Swift/Kotlin/C++ d'un module) doit être compilé dans le binaire de l'app avant même de la lancer. Un module natif custom, c'est du code machine qu'il faut intégrer à la compilation. Expo Go étant déjà compilé et figé, il ne peut pas accueillir un nouveau module natif « à la volée ». D'où la règle : app avec modules natifs custom ⇒ dev client obligatoire. C'est exactement la situation de Halterofit.
| Expo Go | Development build (dev client) | |
|---|---|---|
| Tu l'installes… | depuis le store, tel quel | en le compilant pour ton app |
| Modules natifs | seulement ceux préinclus par Expo | tous les tiens, y compris custom |
| Démarrage initial | instantané | une compilation au départ |
| Recharge du JS à chaud | oui | oui |
| Convient à Halterofit ? | non (modules custom) | oui |
4. Le SDK Expo : la trousse à outils de Halterofit
Le « SDK Expo » est l'ensemble des modules expo-* que tu peux importer comme
n'importe quel paquet JavaScript, et qui te donnent accès au natif sans écrire une ligne de
Swift ou de Kotlin. En lisant le package.json réel de Halterofit, on voit
précisément lesquels sont en jeu. Voici le tour du propriétaire, avec le rôle de
chacun — c'est plus utile à retenir que des numéros de version.
-
expo-router— la navigation. C'est le routeur « basé sur les fichiers » : l'arborescence de ton dossierapp/définit les écrans et les URL, un peu comme les pages d'un site web. Il a son propre module dans le guide (le module sur la navigation), on ne fait ici que situer sa place dans l'écosystème Expo. -
expo-notifications— les notifications locales et push. Halterofit s'en sert, par exemple, pour rappeler les temps de repos ou des rappels d'entraînement. Le module gère les permissions, l'affichage, et la réception. -
expo-linking— les deep links (liens profonds). Il permet d'ouvrir l'app sur un écran précis depuis une URL externe (un lienhalterofit://…), ce qui se marie avec expo-router et avec l'authentification. -
expo-image— un composant image performant, avec cache, transitions et formats modernes. Plus rapide et plus malin que le<Image>de base de React Native. -
expo-crypto— des primitives de cryptographie (hachage, valeurs aléatoires sûres). Pratique pour générer des identifiants ou sécuriser certaines valeurs sans dépendre d'une grosse librairie. -
expo-constants— l'accès aux constantes de l'app : version, identifiants, et surtout les valeurs injectées via le blocextrade la config (par exemple l'eas.projectId). C'est le pont entreapp.jsonet ton code à l'exécution. -
expo-keep-awake— empêche l'écran de s'éteindre. Détail crucial pour une app d'entraînement : pendant une séance, l'écran doit rester allumé entre deux séries, sinon l'utilisateur doit rallumer son téléphone toutes les 30 secondes, les mains pleines de magnésie. Un petit module, un énorme confort d'usage. -
expo-audio— la lecture de sons. Utile pour les bips de fin de repos, les signaux sonores du chrono, etc. On voit d'ailleurs dansapp.jsonqu'il est configuré avecmicrophonePermission: false— l'app joue du son mais ne demande pas le micro, ce qui évite une permission inutile et rassurante pour l'utilisateur. -
expo-splash-screen— l'écran de démarrage (le logo affiché le temps que l'app charge). Configuré dansapp.jsonavec la couleur de fond et l'image. On le contrôle aussi en code pour le masquer pile quand l'app est prête. -
expo-auth-session— l'authentification via des fournisseurs externes (OAuth : Google, Apple, etc.). Il gère le va-et-vient navigateur ⇄ app, en s'appuyant justement sur les deep links d'expo-linkinget leschemedéclaré dans la config.
Le package.json de Halterofit contient d'autres modules expo-* au
service du build et de l'outillage plutôt que de fonctionnalités visibles :
expo-dev-client (le development build vu plus haut),
expo-build-properties (régler des détails de compilation iOS/Android, comme la
version du SDK Android ou la cible iOS), expo-asset (gestion des ressources),
expo-status-bar, expo-linear-gradient, expo-haptics
(les petites vibrations), expo-network (état du réseau). Tu n'as pas à tous les
connaître par cœur : retiens qu'ils suivent tous le même principe — un import
JS qui ouvre une porte vers le natif.
5. Le fichier app.json : la carte d'identité de l'app
Si tu ne devais lire qu'un fichier pour comprendre comment l'app est configurée au
niveau plateforme, ce serait app.json. C'est un fichier déclaratif :
il ne fait rien, il décrit. Expo le lit et en déduit toute la configuration
native. Voici un extrait réel, allégé, du app.json de Halterofit, commenté ligne
à ligne.
{
"expo": {
"name": "Halterofit", // nom affiché sous l'icône
"slug": "halterofit", // identifiant court côté Expo/EAS
"version": "0.1.0", // version visible de l'app
"orientation": "portrait", // verrouillé en mode portrait
"userInterfaceStyle": "dark",// l'app force le thème SOMBRE
"scheme": "halterofit", // schéme d'URL : halterofit://...
"backgroundColor": "#0A0A0A",
"icon": "./assets/icon.png", // l'icône de l'app
"plugins": [ /* ... config plugins, voir plus bas ... */ ],
"experiments": {
"typedRoutes": true, // routes typées (TS) pour expo-router
"reactCompiler": true // active le React Compiler
},
"developmentClient": { "silentLaunch": true } // on utilise un dev client
}
}
Rien que dans cet extrait, on apprend énormément : l'app s'appelle Halterofit, elle est
verrouillée en portrait, forcée en mode sombre, répond aux
liens halterofit://, utilise des routes typées et le
React Compiler (croisé dans le module sur les hooks d'optimisation), et tourne
via un dev client. Une vraie carte d'identité.
Le bloc le plus important pour comprendre le natif est plugins : la liste des
config plugins. Chaque entrée injecte de la configuration native au moment du
prebuild. Un plugin peut être un simple nom ("expo-router") ou un couple
[nom, options] quand on veut le paramétrer. Voici l'extrait réel correspondant.
"plugins": [
"expo-router", // navigation (forme simple : juste le nom)
["@sentry/react-native/expo", { // suivi des erreurs (forme [nom, options])
"organization": "halterofit",
"project": "halterofit"
}],
["expo-splash-screen", { // écran de démarrage paramétré
"backgroundColor": "#0A0A0A",
"image": "./assets/splash.png",
"imageWidth": 150
}],
["expo-notifications", { // notifications : icône + couleur
"icon": "./assets/adaptive-icon.png",
"color": "#0A0A0A"
}],
["expo-audio", { "microphonePermission": false }] // du son, mais PAS de micro
]
Lis cette liste comme une liste de courses pour le natif : « ajoute le routeur, branche Sentry sur tel projet, fabrique un splash screen avec cette image, prépare les notifications avec cette icône, active l'audio mais n'exige pas le micro ». Au prebuild, Expo traduit chaque ligne en vraie configuration iOS/Android.
Le "scheme": "halterofit" n'est pas décoratif : c'est lui qui rend les liens
halterofit://… capables d'ouvrir l'app. C'est la pièce qui fait fonctionner
expo-linking (deep links) et, par ricochet, le retour d'authentification
d'expo-auth-session (le navigateur termine sur halterofit://… pour
rendre la main à l'app). Trois morceaux qui ne sont compréhensibles qu'ensemble.
6. EAS Build : compiler dans le cloud
Reste une question : si Halterofit a besoin d'un dev client (donc d'une vraie compilation native), et si tu veux éviter Xcode et Android Studio… qui compile ? Réponse : EAS Build (Expo Application Services). C'est le service de compilation dans le cloud d'Expo. Tu lances une commande, Expo prend ton projet, le compile sur ses serveurs (machines macOS pour iOS, Linux pour Android), et te renvoie un binaire installable. Tu n'as aucun outil natif à installer localement. Pour un développeur seul, sur un PC Windows par-dessus le marché, c'est ce qui rend une app iOS possible tout court.
La compilation se pilote par un fichier eas.json, organisé en
profils de build. Chaque profil décrit une manière de compiler.
Voici le eas.json réel de Halterofit.
{
"cli": { "version": ">= 16.26.0", "appVersionSource": "remote" },
"build": {
"development": { // profil de dev : produit un DEV CLIENT
"developmentClient": true,
"distribution": "internal" // installation interne (pas le store)
},
"preview": { // version de test, proche de la prod
"distribution": "internal"
},
"production": { // la vraie version pour les stores
"autoIncrement": true // incrémente le numéro de build tout seul
},
"production-apk": { // variante : un .apk Android direct
"extends": "production", // hérite de "production"...
"android": { "buildType": "apk" } // ...mais sort un APK
}
},
"submit": { "production": {} } // config d'envoi aux stores
}
On lit trois étages clairs. development fabrique le dev client (le seul qui a
developmentClient: true). preview produit une app « comme en
prod » mais distribuée en interne, pour faire tester. production est la
version destinée aux stores. Bonus : production-apk étend la prod pour
sortir un APK Android installable directement — un bel exemple d'héritage de config.
Le "appVersionSource": "remote" dit « c'est EAS qui fait autorité sur les
numéros de build », plutôt que de les coder en dur dans les fichiers. Combiné à
autoIncrement sur le profil production, ça évite la corvée — et les erreurs — de
gérer à la main le numéro de build à chaque envoi sur les stores.
7. Pourquoi Expo, pour un développeur solo ?
Halterofit est, pour l'essentiel, un projet d'une seule personne. Ce contexte change tout dans le choix des outils. Le temps et l'énergie sont la ressource la plus rare, et chaque heure passée à se battre avec la configuration native est une heure de moins sur la vraie app. Expo attaque ce problème de trois côtés.
- La rapidité. Tu démarres une fonctionnalité en restant dans le JavaScript, avec recharge à chaud. Pas d'aller-retour incessant avec Xcode. La boucle « j'écris, je vois » reste courte.
-
Moins de configuration native. Les config plugins remplacent des heures de
réglages manuels dans des fichiers iOS/Android obscurs. Tu déclares une intention dans
app.json, Expo fabrique le natif. Et comme les dossiers natifs sont régénérables, tu n'as pas à les maintenir ni à les versionner. - Les mises à jour OTA (Over-The-Air). Comme une grande partie de l'app est du JavaScript, Expo permet d'envoyer une mise à jour du bundle JS directement aux téléphones, sans repasser par la validation des stores. Tu corriges un bug, tes utilisateurs l'ont en quelques minutes au lieu de plusieurs jours. (Attention : seul le JavaScript se met à jour ainsi ; changer du code natif — ajouter un module — exige toujours un nouveau build et un passage par les stores.)
Pour un solo, Expo déplace le centre de gravité du travail : de la plomberie vers le produit. Tu passes moins de temps sur « comment compiler » et plus sur « quelle fonctionnalité aide vraiment l'utilisateur ». EAS Build compile à ta place, les OTA distribuent à ta place, les config plugins configurent à ta place. Il te reste l'essentiel : écrire l'app.
8. Le lien avec l'outillage : Metro et Babel
Expo ne travaille pas seul. Sous le capot, deux outils transforment ton code avant qu'il ne tourne sur le téléphone, et Expo les pilote pour toi.
Metro est le bundler de l'écosystème React Native : il prend ton
arbre de fichiers TypeScript/JSX, suit tous les import, et assemble le tout en un
unique paquet JavaScript (le « bundle ») que le téléphone exécute. C'est aussi lui qui sert le
rechargement à chaud pendant le développement. Babel, lui, est le
transpileur : il convertit le TypeScript et le JSX modernes en JavaScript que le
moteur peut comprendre. Détail parlant : c'est par un plugin Babel
(babel-plugin-react-compiler, visible dans le package.json) que le
React Compiler activé dans app.json entre réellement en action.
Metro et Babel ont leur propre traitement détaillé dans le module sur l'outillage — comment le bundle se construit, ce que fait le transpileur, comment le React Compiler s'insère dans Babel. Ici, retiens seulement la place de ces outils : Expo est le chef d'orchestre, Metro assemble, Babel traduit. Le React Compiler, lui, est aussi évoqué dans le module sur useContext, useMemo, useCallback et memo, car c'est lui qui automatise une partie de ces optimisations.
9. Lire la config comme un tout cohérent
Le vrai déclic, en lisant Halterofit, c'est de voir que app.json,
eas.json et package.json se répondent. Une dépendance
expo-* dans le package.json correspond souvent à un plugin dans
app.json et à un comportement dans le code. Le scheme de
app.json active les deep links qu'utilise expo-auth-session. Le profil
development de eas.json produit le dev client qu'imposent les modules
natifs custom du package.json. Tout se tient.
Quand tu ouvriras un fichier de config du projet, ne le lis pas isolément. Demande-toi toujours : « quel besoin de l'app cette ligne sert-elle ? ». La config n'est pas de la paperasse : c'est le mode d'emploi que tu donnes à Expo pour qu'il fabrique exactement l'app que tu veux.
Voici un extrait réel du app.json de Halterofit. Lis-le, puis réponds.
"scheme": "halterofit",
"userInterfaceStyle": "dark",
"plugins": [
"expo-router",
["expo-audio", { "microphonePermission": false }]
],
"experiments": {
"typedRoutes": true,
"reactCompiler": true
}
Questions : (1) Que permet concrètement la ligne scheme, et
quel module Expo en dépend ? (2) Pourquoi a-t-on mis microphonePermission: false
sur expo-audio ? (3) Que t'apprend userInterfaceStyle: "dark" sur
le thème de l'app ?
Voir le corrigé
(1) Le scheme déclare un schéme d'URL :
halterofit://…. C'est ce qui permet à un lien externe d'ouvrir l'app
(deep link). Le module qui repose là-dessus est expo-linking, et par
extension expo-auth-session, qui ramène l'utilisateur dans l'app après une
connexion OAuth en terminant sur une URL halterofit://….
(2) L'app joue des sons (bips de repos, signaux du chrono) mais n'a
jamais besoin d'enregistrer. Mettre microphonePermission: false
évite de demander une permission micro inutile — moins d'écrans de permission, plus de
confiance de l'utilisateur, et un store moins regardant.
(3) L'app force le thème sombre, quel que soit le réglage
du téléphone. C'est cohérent avec la couleur de fond #0A0A0A qu'on retrouve un
peu partout (splash, fond, icône de notification). Halterofit assume une identité visuelle
sombre.
1. En une phrase : qu'est-ce qu'Expo par rapport à React Native ?
Une couche par-dessus React Native (framework + plateforme) qui prend en charge la partie native pénible — compilation, modules natifs, accès aux capteurs/notifications, mises à jour — pour qu'on reste le plus possible dans le JavaScript. « React Native sans (presque) la douleur de Xcode et Android Studio. »
2. Pourquoi Halterofit ne peut-il pas tourner dans Expo Go ?
Parce qu'il utilise des modules natifs custom (MMKV, WatermelonDB, Skia, Nitro…) qui ne sont pas inclus dans Expo Go. Or le code natif doit être compilé dans le binaire : Expo Go étant figé, il ne peut pas les accueillir. Il faut donc un dev client.
3. À quoi sert expo-keep-awake, et pourquoi est-ce pertinent ici ?
À empêcher l'écran de s'éteindre. Crucial pour une app d'entraînement : l'écran doit rester allumé entre les séries, sinon l'utilisateur doit rallumer son téléphone sans arrêt en pleine séance.
4. Que décrit le fichier eas.json ?
Les profils de build pour la compilation dans le cloud (EAS Build) : development (le dev client), preview (test proche de la prod), production (les stores), plus une variante production-apk. Chacun décrit une manière de compiler.
5. Une mise à jour OTA peut-elle livrer un nouveau module natif ?
Non. L'OTA ne met à jour que le bundle JavaScript. Ajouter ou changer du code natif (un module) exige un nouveau build via EAS et un passage par les stores.
Expo est une couche par-dessus React Native qui gère le natif à ta place
(framework + plateforme). Halterofit utilise un development build (dev client)
avec des config plugins, pas Expo Go, parce qu'il embarque des modules
natifs custom. Le SDK Expo fournit les modules expo-*
(router, notifications, linking, image, crypto, constants, keep-awake, audio, splash-screen,
auth-session). app.json décrit l'app (nom, icône, splash, scheme, plugins,
routes typées, mode sombre), eas.json décrit comment la compiler dans le cloud.
Et tout ça repose sur Metro (bundler) et Babel
(transpileur).
Deux confusions vont de pair. La première : croire qu'Expo Go et le dev client sont la même chose. Ils ne le sont pas — Expo Go est une app figée du store, le dev client est ta version compilée avec tes modules. La seconde : croire qu'« utiliser Expo » limite ce qu'on peut faire au natif. C'était à moitié vrai à l'époque du tout-managed + Expo Go ; ce n'est plus le cas. Avec les development builds et les config plugins, tu as accès à tout le natif, exactement comme en React Native « nu » — Halterofit en est la preuve vivante, avec ses modules natifs custom.
L'idée maîtresse dépasse Expo : une configuration déclarative (« je décris ce que je veux ») plutôt qu'impérative (« j'écris pas à pas comment le faire »), qu'un outil traduit ensuite en réalité. C'est le même principe que les fichiers d'infrastructure dans le cloud, les manifestes de conteneurs, ou les fichiers de CI. Lis d'abord la config d'un projet pour comprendre ses intentions : tu apprendras plus vite ce qu'il est qu'en plongeant tête baissée dans le code.