Vous vous lancez dans le développement mobile ? Vous vous demandez sûrement comment créer des applications qui fonctionnent simultanément sur différents appareils.
En réalité, parfois, nous choisissons de ne pas le faire. La question se pose : devons-nous opter pour le natif, avec des bases de code distinctes pour iOS et Android, ou pour le cross-platform, en poursuivant le rêve du "Coder une fois, déployer partout" ? React Native s'est imposé comme une solution cross-platform privilégiée par de nombreux développeurs.
"Coder une fois, déployer partout", disaient-ils. "Ca sera amusant". Et ça l'est... jusqu'à ce que vous vous retrouviez à débuguer simultanément sur Xcode et Android Studio, vous demandant si "cross-platform" n'était pas juste une plaisanterie. Car "cross-platform" ne signifie pas "zéro code natif".
Le code natif reste incontournable. Chaque plateforme nécessite ses propres configurations - gestion des dépendances, paramètres de build, permissions, gestion des ressources. Bien que ces tâches puissent sembler gérables individuellement, elles se complexifient avec chaque intégration tierce, créant des configurations natives complexes que les développeurs doivent maintenir et synchroniser minutieusement entre les plateformes.
C'est là qu'Expo entre en jeu, réinventant complètement cette expérience en transformant les tâches natives manuelles en configurations déclaratives. Pour y parvenir, ils ont introduit la Génération Native Continue (CNG), et aujourd'hui, nous allons démystifier son implémentation technique !
Mais commençons par un rappel sur le fonctionnement d'une application React Native.
Si vous maîtrisez déjà React Native, vous pouvez passer cette section. Pour les autres, voici un aperçu rapide 😃
Une application cross-platform construite avec React Native combine des couches natives et JavaScript pour fonctionner sur un appareil natif.
La couche JavaScript, basée sur React, définit l'interface utilisateur et la logique applicative, s'exécutant dans un moteur JavaScript (comme Hermes, un moteur JS optimisé pour RN) intégré à l'application native.
Les couches natives, écrites dans des langages spécifiques aux plateformes (Swift/Objective-C pour iOS, Java/Kotlin pour Android), offrent une interopérabilité C++ via des mécanismes intégrés : Objective-C++ (fichiers .mm) pour iOS et JNI (Java Native Interface) pour Android.
À partir de ces éléments, le cœur C++ de React Native (l'Interface JavaScript ou JSI) sert d'interface bas niveau entre JavaScript et le code natif, créant et maintenant des liaisons - des objets hôtes C++ qui peuvent être détenus et appelés directement depuis JavaScript.
Dans React Native, trois éléments principaux sont construits sur cette stack :
Lorsque vous examinez une application React Native, son point d'entrée est une enveloppe native (située dans les couches natives) qui initialise l'environnement JS et charge le code JS bundlé. Les dépendances natives sont gérées via des systèmes de build spécifiques aux plateformes (CocoaPods pour iOS, Gradle pour Android).
Le processus de build compile le code natif et C++ (via les compilateurs de plateforme), bundle le code JS, et les combine en une application prête pour la production avec un binaire final (.ipa pour iOS, .apk/.aab pour Android). À l'exécution, ces composants compilés collaborent pour créer les liaisons JSI qui font le pont entre JS et le code natif.
Une fois votre application compilée pour la production, vous pouvez mettre à jour le bundle JS sans mettre à jour l'application entière grâce aux mises à jour OTA, car le bundle JS est traité comme un asset pour l'application native. Mais pour les parties natives, une nouvelle compilation est nécessaire.
Si vous venez du développement web et React, considérez React Native comme React pour mobile, avec quelques différences clés :
Bien que l'architecture principale diffère significativement du fonctionnement d'une application web, React Native et Expo supportent en fait l'intégration web grâce à différentes solutions et bibliothèques permettant un code source unique entre toutes les plateformes principales (web, iOS et Android).
L'écosystème Expo ayant considérablement évolué depuis sa création, comprendre ses composants et les éléments exploitables dans une "application Expo" peut être complexe. Il y a beaucoup de nouveau vocabulaire, même pour les développeurs RN. Dans cette section, nous allons explorer et résumer les concepts clés qui alimentent l'architecture et le workflow d'Expo.
Expo introduit plusieurs nouveaux concepts. Pour apprécier son fonctionnement — particulièrement si vous comptez créer des modules ou plugins personnalisés qui s'intègrent à Expo — il est crucial de comprendre les mécanismes sous-jacents. Analysons les composants essentiels et examinons leurs rôles dans l'écosystème. Nous couvrirons :
Dans une application React Native traditionnelle, votre couche native est stockée dans des dossiers natifs (ios/, android/) au sein de votre projet. Historiquement, ces dossiers étaient traités comme du code source - des fichiers projet permanents que les développeurs devaient maintenir, modifier et gérer manuellement. Toute modification des dépendances ou configurations natives nécessitait une manipulation minutieuse de ces dossiers pour éviter les changements cassants ou les conflits.
Expo réinvente cette expérience avec la Génération Native Continue (CNG). Au lieu de générer le code natif une fois puis le gérer manuellement, CNG traite le code natif comme un artéfact généré à partir de vos fichiers JavaScript et de configuration. Concrètement :
expo prebuild
app.json
, plugins)Cette approche transforme le workflow de développement, passant de "maintenir le code natif" à "déclarer ce dont vous avez besoin" - rendant tout le cycle de vie du développement mobile plus maintenable, de la configuration initiale aux mises à jour des dépendances et au déploiement.
L'idée centrale de CNG : le code natif devient un output de build plutôt qu'un code source à maintenir.
Le code natif devenant un output prévisible, réversible et personnalisable compris par le framework, CNG permet de continuer à utiliser les outils et services Expo tout en vous permettant d'ajouter des modules natifs personnalisés et d'effectuer des modifications au niveau natif.
Par exemple, la commande prebuild
peut s'exécuter localement sur votre machine, et c'est la même commande qui est utilisée lors de la compilation de votre application avec les services Expo.
app.json
ou app.config.json
valide à la racine de votre projet : c'est un fichier de configuration d'application pour Expo💡 Bien que cela semble assez simple, pour les applications plus anciennes qui migrent vers Expo, certaines solutions alternatives sont nécessaires. L'équipe Expo a fourni un exemple de commit de migration que vous pouvez consulter si vous essayez de l'appliquer à votre projet. Revenez-y peut-être une fois que vous serez plus à l'aise avec les concepts.
Le SDK Expo comprend :
expo
centralexpo-*
qui sont inclus dans le SDK ou peuvent être installés une fois que vous avez le package central@expo est un package scopé qui contient des outils comme "@expo/cli" (qui contient et définit la commande prebuild) et @expo/prebuild-config (principalement utilisé par @expo/cli pour la configuration interne de la commande prebuild).
Le package principal expo inclut en fait beaucoup de ces packages @expo scopés comme dépendances dans sa configuration package.json, et c'est ce que nous appelons généralement le SDK expo.
Quand nous parlons de packages/modules Expo, cela peut faire référence à plusieurs choses :
Le package expo
central,
Les modules SDK Expo et bibliothèques (qui fournissent des fonctionnalités spécifiques),
Les outils de développement et de build (utilisés pour le développement, la compilation ou la gestion des projets Expo),
ou les packages de configuration (pour configurer le processus de build et de bundling des projets Expo).
Lorsque vous voyez une dépendance expo-*[quelquechose]
, cela signifie généralement qu'il s'agit d'un package ou utilitaire destiné à fonctionner dans l'écosystème Expo. Vous avez généralement besoin du package central expo
, entre autres, pour les intégrer dans un projet.
L'écosystème Expo est la force du framework mais il peut être déroutant quand on débute !
💡 Par exemple, un package utile fourni par Expo est l'utilitaire install-expo-modules qui facilite l'intégration des modules SDK dans une application RN existante. Lorsque vous exécutez :
.
npx install-expo-modules@latest
Cette commande effectue (ou tente d'effectuer) plusieurs choses automatiquement :
expo
et expo-modules-core
)Imaginons que vous souhaitiez interagir avec la caméra native d'un appareil. Vous essayez d'utiliser le module expo-camera. L'API de la caméra provient du SDK de votre appareil et se trouve en dehors de votre application. Comment tout cela fonctionne-t-il ensemble ? Voici une illustration de ce qui se passe en coulisses :
Lors de l'installation initiale du module expo-camera, le code natif est téléchargé dans le répertoire node_modules de votre projet. Cependant, à ce stade :
Pendant le processus de prebuild :
expo-camera
Les modules Expo sont assez similaires aux packages React Native standard, mais leur intégration avec Expo leur permet d'utiliser et de fournir des plugins de configuration.
La plupart des modules Expo sont livrés avec des configurations natives intégrées grâce à des plugins de configuration prédéfinis, ce qui permet au développeur d'éviter la configuration native tant qu'il fournit le plugin correspondant à la configuration de son propre projet.
À leur cœur, les plugins sont des fonctions qui modifient un objet ExpoConfig. Les mods (abréviation de modificateurs) sont des actions spécifiques que les plugins utilisent pour modifier des fichiers natifs individuels. On pourrait dire que les plugins décident quoi changer, et les mods sont les outils qu'ils utilisent pour effectuer ces changements spécifiques. Expo utilise ces mods pour effectuer des modifications plus complexes du code natif.
Les plugins de configuration sont principalement appliqués sur l'objet ExpoConfig selon le flux suivant :
Les mods sont exécutés séquentiellement pendant la phase de configuration du prebuild. Chaque mod peut lire l'état actuel de la config et des fichiers projet, faire des modifications, et écrire ces modifications dans le système de fichiers. Ils ne mettent pas seulement à jour l’objet ExpoConfig mais modifient directement les fichiers natifs (Info.plist, build.gradle) pendant la génération (evalModsAsync).
Voici quelques exemples de plugins de configuration fournis par Expo :
*Le processus de prebuild d'Expo inclut une liste de plugins intégrés par défaut qu'Expo applique automatiquement. Lorsque vous installez expo-splash-screen dans votre projet, vous n'avez pas besoin d'ajouter manuellement un plugin de configuration à votre app.json ou app.config.js.
Chaque fois que vous souhaitez automatiser le processus de génération de configuration native au moment du prebuild, vous pouvez également écrire votre propre plugin de configuration personnalisé.
Un cas d'usage courant serait si vous devez intégrer un NativeModule personnalisé non disponible dans l'écosystème Expo à votre processus de prebuild, et que ce module nécessite une configuration native. Dans ce cas, vous devriez exploiter les plugins de configuration :
Stripe est une solution de traitement des paiements qui prend en charge diverses méthodes de paiement, notamment Apple Pay et Google Pay. Des fonctionnalités comme Apple Pay et Google Pay sont natives à leurs plateformes respectives : elles font partie du système d'exploitation central et disposent d'APIs ou de services directement gérés par iOS et Android. Pour que la bibliothèque stripe-react-native fonctionne avec la CNG, elle doit définir manuellement certaines configurations natives dans un plugin de configuration :
AndroidManifest.xml
pour confirmer l'éligibilité de l'application à utiliser les APIs de Google Pay.En examinant l'implémentation, le plugin prend l'objet de configuration Expo, applique des plugins intermédiaires pour chaque plateforme et le retourne.
Dans le plugin de configuration iOS, il :
merchantIdentifier
des props du pluginwithEntitlementsPlist
(un plugin mod fourni par Expo pour accéder et modifier le plist d'autorisations).withEntitlementsPlist
s'attend à ce que vous modifiiez le config.modResults
, qui représente le contenu du fichier Entitlements.plist
sous forme d'objet JavaScript. Cet objet est en fait typé.
setApplePayEntitlement
(une méthode personnalisée dans le fichier plugin) pour mettre à jour le plist.withEntitlementsPlist
modifie le mod entitlements (qui est typé comme un dictionnaire) de votre configuration.
Ensuite, setApplePayEntitlement retourne un Record javascript qui correspond au format du fichier xml .entitlement final.
Maintenant, toute application utilisant la CNG peut utiliser stripe-react-native :
1. La configuration du plugin vous permet de référencer le plugin dans votre propre fichier de configuration app.json
expo :
2. Vous pouvez maintenant exécuter la commande prebuild pour appliquer les configurations personnalisées :
npx expo prebuild
Les templates de dossiers natifs sont initialisés. L’objet ExpoConfig est mis à jour tout au long du processus de prebuild et les fichiers natifs sont écrasés par les mods pendant l'étape de synchronisation. À la fin, les dépendances sont réinstallées (pod install
pour iOS) et vous obtenez des dossiers natifs configurés 🎉
Vos dossiers natifs sont maintenant des artéfacts de build générés de manière prévisible qui peuvent toujours être régénérés et principalement configurés et versionnés depuis votre couche JS.
Cela signifie que chaque défi de configuration native a désormais une solution déclarative.
Un grand merci à Kadi Kraman de l'équipe Expo pour m’avoir contacté et proposé d’enrichir l’article avec quelques conseils ! 🙌
.gitignore
, car ils sont maintenant considérés comme des artéfacts générés plutôt que du code source.npx expo run:ios
ou run:android
pour la première fois, Expo exécute automatiquement prebuild pour vous.eas build
intègre prebuild dans son processus, mais avec une distinction importante : si vos dossiers natifs sont correctement ignorés dans git, ils sont traités comme des artéfacts générés et eas build
utilise la CNG. Cependant, s'ils ne sont pas ignorés, eas build
utilisera directement le contenu de vos dossiers natifs existants. Ce comportement est particulièrement important à garder en tête lors du déploiement de vos applications, et c'est en fait ce qui définit la différence entre les workflows "managed" et "bare" d’Expo.--clean
. Comme les applications mod ne garantissent pas d'être idempotentes (ce qui peut les rendre dangereuses), ce flag assure un contenu correct en réinitialisant le template existant et en ré-appliquant les configurations de zéro.
En automatisant la génération et la gestion du code natif, prebuild et CNG sont des ajouts majeurs apportés à l’écosystème React Native. Bien que ce soit déjà beaucoup à assimiler, nous n'avons fait qu'effleurer la surface, car la suite d'outils disponible aujourd'hui continue de s'étendre et évolue vers des versions stables.
La plupart des développeurs n'auront pas besoin de s'inquiéter d'implémenter des plugins de configuration à partir de zéro—la communauté fait de son mieux pour fournir les plugins manquants. Vous pouvez simplement profiter des solutions fournies par le framework sans plonger dans tous les détails techniques. Mais si vous prenez le temps de les maîtriser, vous pourrez libérer tout le potentiel du framework pour vos applications.
La meilleure façon de donner du sens à tout cela est de le mettre en pratique. 💪 Cela vous sera utile lorsque vous voudrez aller plus loin et implémenter des configurations natives personnalisées ! :) 📱💻