• 30 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 09/09/2020

Préparez votre application pour les stores Apple et Google

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Bienvenue dans le dernier chapitre de ce cours. Eh oui, déjà ! :( Ici, je vais vous apprendre comment finaliser votre application et la préparer pour un départ imminent vers les stores Apple et Google.

Actuellement, notre application fonctionne avec un serveur Node.JS, en localhost, sur lequel notre application pointe pour récupérer le fichier bundle Javascript. Vous vous doutez bien que votre application ne peut pas fonctionner comme cela chez vos utilisateurs. Il y a donc quelques étapes à effectuer afin de préparer votre application pour les stores.

Avant cela, il faut que l'on traite un point pour le moins... impactant pour l'utilisateur. Ce dernier peut ajouter un avatar, ajouter des films dans ses favoris, mais, dès qu'il relance l'application, son avatar et ses films favoris sont perdus. Il faut que l'on trouve le moyen de sauvegarder, en langage technique persister, les données de l'application. Et plus précisément ici, ce sont les données de notre store Redux.

Persistez les données

Il y a plusieurs solutions pour persister les données d'une application, on peut utiliser une base de données SQLite, un fichier texte, une API, etc. Toutes ces solutions fonctionnent, mais il y a mieux dans notre cas. Lorsque vous utilisez Redux pour gérer le state de votre application, comme ici, vous pouvez utiliser la librairie redux-persist qui permet de persister le store Redux de votre application mais aussi et surtout, de le réhydrater.

Réhydrater le store Redux de notre application ? Faut-il lui donner de l'eau ? T'as pas un peu perdu les pédales, là, toi ?

Peut-être, :) mais c'est comme cela que l'on appelle le fait de réinjecter des données persistées dans un store

Prenons l'exemple de notre application. L'utilisateur recherche un film, celui-ci lui plaît et il l'ajoute à ses films favoris. À cet instant, le film est ajouté au state de l'application et, en parallèle, il est sauvegardé quelque part dans l'application. Ça, c'est la persistance.

Ensuite, l'utilisateur vaque à ses occupations, l'application est killée.

Notre utilisateur vaque donc à ses occupations et notre application est killée. Lorsqu'il va relancer l'application, tout le store Redux de votre application est remis à zéro. Il faut donc réinjecter le store sauvegardé dans le store Redux de l'application. Et ça, c'est la réhydratation. C'est plus clair ? :)

Installez la librairie redux-persist

Il s'agit d'une librairie purement écrite en Javascript, on n'a pas besoin de la lier à nos projets mobiles natifs. Vous pouvez donc également utiliser cette librairie dans une CRNA. Comme à notre habitude, nous allons utiliser la commande  npm install  pour installer la librairie dans notre application. Dans un terminal, à la racine de notre projet, saisissez :

$ npm install --save redux-persist

Configurez la librairie redux-persist

Avant de pouvoir utiliser la librairie, il faut la configurer, c'est-à-dire lui donner le store Redux à persister, puis réhydrater l'application avec.

Pour persister le store Redux, il faut d'abord persister ses reducers.

Dans le fichier Javascript  configureStore.js  , faites ceci : Je vous explique tout après. ;)

// Store/configureStore.js
import { createStore } from 'redux'
import toggleFavorite from './Reducers/favoriteReducer'
import setAvatar from './Reducers/avatarReducer'
import { persistCombineReducers } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
const rootPersistConfig = {
key: 'root',
storage: storage
}
export default createStore(persistCombineReducers(rootPersistConfig, {toggleFavorite, setAvatar}))

 Première chose, j'ai remplacé la fonction  combineReducers  de Redux par la fonctionpersistCombineReducers  de redux-persist. Cette dernière permet de définir plusieurs reducers dans notre application et de les persister.

Ensuite, dans  rootPersistConfig  , j'ai défini des paramètres obligatoires pour la configuration de la persistance du state global : 

  • key  : permet à la librairie d'identifier de manière unique votre store persisté.

  • storage  : correspond au type de stockage.  Ici, on a utilisé redux-persist/lib/storage  qui correspond à un stockage local. C'est ce que l'on utilise 90 % du temps. Il existe d'autres types de stockage à utiliser selon le type de données à stocker. Vous pouvez retrouver la liste complète sur la documentation de redux-persist.

C'est presque bon pour la partie persistance. À présent, il faut persister le store lui-même. La librairie redux-persist dispose de la fonction  persistStore  pour... à votre avis ?

Oui, bravo, persister le store : :D

// App.js
import React from 'react'
import Navigation from './Navigation/Navigation'
import { Provider } from 'react-redux'
import Store from './Store/configureStore'
import { persistStore } from 'redux-persist'
export default class App extends React.Component {
render() {
let persistor = persistStore(Store)
return (
<Provider store={Store}>
<Navigation />
</Provider>
)
}
}

En plus de persister le store Redux, la fonction  persistStore  renvoie le store persisté. C'est indispensable pour pouvoir ensuite réhydrater notre application : Comme tout à l'heure, je vous explique tout après.

// App.js
import React from 'react'
import Navigation from './Navigation/Navigation'
import { Provider } from 'react-redux';
import Store from './Store/configureStore'
import { persistStore } from 'redux-persist'
import { PersistGate } from 'redux-persist/es/integration/react'
export default class App extends React.Component {
render() {
let persistor = persistStore(Store)
return (
<Provider store={Store}>
<PersistGate persistor={persistor}>
<Navigation />
</PersistGate>
</Provider>
)
}
}

On a donc utilisé la fonction  persistStore  pour persister et récupérer le store persisté. Ensuite, la librairie redux-persist nous simplifie la tâche en mettant à disposition le component PersistGate. Ce component se charge de réhydrater ses components enfants, c'est-à-dire toute notre application ici.

La partie réhydratation de notre application est faite, tout comme la partie persistance. Il ne nous reste plus qu'à tester. :pirate:

Lancez l'application et ajoutez des films en favoris. Vous pouvez également ajouter un avatar pour vérifier que le store Redux et le state global contiennent bien toutes les données gérées par nos deux reducers. Ensuite, relancez l'application et constatez que nos films favoris et notre avatar sont toujours là :

Persistance et ré-hydratation du store Redux avec redux-persist
Persistance et réhydratation du store Redux avec redux-persist

En voilà une bonne nouvelle ! On peut gérer une base de données, très rapidement, avec des informations que l'on gère déjà avec nos reducers. Ça, c'est de l'optimisation.

Onglet News

Si vous avez regardé la vidéo d'introduction de l'application que l'on a réalisée, vous avez dû voir qu'il y a un 3e onglet, appelé News. Cet onglet et son contenu sont le dernier développement que j'ai prévu de vous faire faire alors profitez-en. En plus, c'est loin d'être le plus dur, croyez-moi : ;)

Onglet News et son fonctionnement
Onglet News et son fonctionnement

Voici le cahier des charges :

C'est dans vos cordes ? Quand on y pense, c'est exactement la même fonctionnalité que pour la recherche, à la différence que, dans un cas, on recherche des films en fonction d'un texte et, dans l'autre cas, on recherche des films en fonction d'une note.

Vous aurez besoin de cette image pour l'onglet News. Renommez-la en ic_fiber_new.png

Image onglet News
Image onglet News

La solution :

// Navigation/Navigation.js
...
import News from '../Components/News'
...
const NewsStackNavigator = createStackNavigator({
News: {
screen: News,
navigationOptions: {
title: 'Les Derniers Films',
},
},
FilmDetail: {
screen: FilmDetail,
}
})
const MoviesTabNavigator = createBottomTabNavigator(
{
...,
News: {
screen: NewsStackNavigator,
navigationOptions: {
tabBarIcon: () => {
return <Image
source={require('../Images/ic_fiber_new.png')}
style={styles.icon}/>
}
}
}
...
)
// API/TMDBApi.js
...
// Récupération des meilleurs films
export function getBestFilmsFromApi (page) {
return fetch('https://api.themoviedb.org/3/discover/movie?api_key=' + API_TOKEN + '&vote_count.gte=1000&sort_by=release_date.desc&language=fr&page=' + page)
.then((response) => response.json())
.catch((error) => console.error(error));
}
// Components/News.js
import React from 'react'
import { } from 'react-native'
import FilmList from './FilmList'
import { getBestFilmsFromApi } from '../API/TMDBApi'
class News extends React.Component {
constructor(props) {
super(props)
this.page = 0
this.totalPages = 0
this.state = {
films: [],
isLoading: false
}
this._loadFilms = this._loadFilms.bind(this)
}
componentDidMount() {
this._loadFilms()
}
_loadFilms() {
this.setState({ isLoading: true })
getBestFilmsFromApi(this.page+1).then(data => {
this.page = data.page
this.totalPages = data.total_pages
this.setState({
films: [ ...this.state.films, ...data.results ],
isLoading: false
})
})
}
render() {
return (
<FilmList
films={this.state.films}
navigation={this.props.navigation}
loadFilms={this._loadFilms}
page={this.page}
totalPages={this.totalPages}
favoriteList={false}
/>
)
}
}
export default News

C'est tout ! On a tellement bien découpé nos components et leurs fonctionnalités que, en seulement 3 fichiers créés / modifiés, on est capable de mettre en place une fonctionnalité vraiment sympa dans notre application.

C'était le dernier développement que je vous demandais sur ce cours. J'espère, encore une fois, qu'il vous aura convaincu de la puissance de React et de ses components. Je vous ai mis les sources du projet fini sur GitHub. N'hésitez pas à mettre une ★ si le projet vous a plu. ;)

Cela ressemble à des adieux, mais ce n'en sont pas. On n'en a pas fini avec notre application. Il me reste à vous montrer comment la livrer sur les stores. Une fois n'est pas coutume, on va commencer par iOS et son App Store.

Publiez l'application sur l'App Store

Avant de pouvoir fournir notre application à Apple, il faut faire quelques préparatifs.

Tout d'abord, il faut s'assurer que toutes nos URL appelées dans l'application sont sécurisées, c'est-à-dire qu'elles passent bien par HTTPS. Si ce n'est pas le cas, il faut autoriser les appels vers les URL HTTP en suivant la documentation Enable App Transport Security. Dans notre cas, vous pouvez vérifier, mais tous les appels sont déjà sécurisés (les appels sont dans API/TMDBApi.js). On peut donc passer directement à l'étape suivante.

Passez en mode release

Pour cette étape, vous allez devoir être sur un Mac et ouvrir le projet mobile natif avec XCode. Pour ce faire, ouvrez l'IDE Xcode, puis, dans l'onglet "File" / "Open", sélectionnez et ouvrez le fichier MoviesAndMe/ios/MoviesAndMe.xcodeproj.

Un projet va s'ouvrir. Allez dans l'onglet menu "Product" / puis "Scheme" / "Edit Scheme..." (tout en bas). Une fenêtre s'affiche, assurez-vous d'être sur le sous-menu "Run". Dans le champ "Build Configuration", changez "Debug" pour "Release", comme ceci :

Passer en mode release
Passer en mode release

C'est bon, on est passé en mode release. On est sur une bonne lancée, on ne s'arrête pas en si bon chemin.

Générez le bundle Javascript

Depuis le début de ce cours, lorsque l'on développe et teste notre application, on lance un serveur Node.JS à côté, sur notre localhost. Ce serveur héberge notre code Javascript (sous la forme d'un bundle) et permet à notre application de le récupérer en temps réel et de se mettre à jour automatiquement.

Lorsque vos utilisateurs vont télécharger l'application sur les stores, leurs devices ne vont pas se connecter à votre serveur Node.JS. Déjà, parce que c'est impossible et ensuite, parce que vous n'allez pas faire tourner un serveur Node.JS éternellement sur votre machine. :D La solution est de générer nous-mêmes le fichier bundle Javascript et de l'intégrer dans le projet mobile natif. Ainsi, votre application, une fois sur le device de l'utilisateur, n'aura qu'à exécuter le fichier bundle Javascript qu'elle contient et à afficher le rendu à l'écran.

Pour générer un bundle, il faut exécuter la commande suivante, à la racine du projet :

$ react-native bundle --entry-file ./index.js --platform ios --dev false --bundle-output ./ios/MoviesAndMe/main.jsbundle --assets-dest ./ios/

 Ici, on génère le fichier bundle Javascript dans notre projet mobile natif iOS et on ajoute les assets (images) dans le dossier /ios.

Utilisez le bundle Javascript

Maintenant que notre bundle Javascript est créé, il faut dire à notre application d'aller le chercher. Retournez sur votre projet sur XCode et ouvrez le point d'entrée de l'application, c'est-à-dire le fichier AppDelegate.m : 

// AppDelegate.m
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"MoviesAndMe"
initialProperties:nil
launchOptions:launchOptions];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
@end

 Ahhh... mes yeux ! C'est quoi, tout ça ? o_O

C'est de l'Objective-C, la technologie, avec Swift, qui permet de développer des applications natives iOS. En gros, ici, le code définit l'emplacement où récupérer le fichier bundle Javascript (  jsCodeLocation  ). Puis, le code initialise une RCTRootView, autrement dit une vue React Native, avec le contenu de votre bundle et l'affiche à l'écran. Voilà comment une application React Native fonctionne. ;) Et attendez, ce n'est pas fini !

Actuellement, l'application va chercher un bundle nommé "index" sur notre localhost. C'est ce qui est défini ici :

jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];

 Si vous avez votre application qui tourne, avec le serveur Node.JS, accédez à l'URL : http://localhost:8081/index.bundle?platform=ios

Revenons à nos moutons. Notre application va donc chercher le bundle Javascript déposé sur notre serveur Node.JS en localhost. Il faut qu'on lui dise, à présent, d'aller chercher le bundle Javascript déposé dans notre projet. Pour cela, rien de plus simple, si tant est que l'on connaisse l'Objective-C, il faut remplacer :

jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];

par :

jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];

À partir de maintenant, dès que votre application va être compilée, elle va utiliser le fichier bundle Javascript contenu dans votre application

C'est terminé, notre application est prête pour le store Apple. À présent, si vous testez l'application sur device, vous la testez en mode release.

Plus besoin de serveur Node.JS, votre application est autonome maintenant, grâce au bundle que l'on a généré. Vous pouvez aller la montrer à vos amis dans toute la France en attendant sa publication sur l'App Store. ^^

OK, maintenant, comment je l'envoie sur l'App Store ?

Ça, c'est une autre histoire et cela n'a pas vraiment de lien avec React Native. Il faut suivre le processus basique de la publication d'une application sur l'App Store. Vous pouvez suivre ce tutoriel très bien réalisé qui vous explique pas à pas les étapes à suivre. Il vous faudra un peu de patience, de motivation et... d'argent, une licence Apple coûte 99 euros par an. La publication sur l'App Store est assez difficile la première fois, il faut générer des "provisionnings" et des "certificats", mais ensuite cela devient automatique. Si vous poussez votre application jusque là, je serai ravi de découvrir votre travail une fois l'application sur l'App Store, envoyez-moi un lien par MP. :) 

Publiez sur le Google Play

Sur Android, c'est plus facile, vous allez voir. Première chose à faire, il faut générer une clé de signature Android (signing key), indispensable pour signer et générer un fichier APK.

Pour générer une clé de signature, il faut d'abord localiser l'outil keytool de Java. Sur Mac / Linux, c'est facile, l'outil est accessible n'importe où depuis le terminal :

$ keytool

Sur Windows, il faut le récupérer dans le dossier /bin/ de Java, généralement ici :C:\Program Files\Java\jdkx.x.x_x\bin  . Placez-vous, dans un terminal, au niveau du dossier /bin/

À présent, que vous soyez sur Mac, Linux ou Windows, saisissez la commande :

$ keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

Plusieurs questions vont vous être posées, sur votre identité, votre adresse, etc. Des mots de passe vous seront demandés pour la clé de signature et l'alias.

Récupérez le fichier  my-release-key.keystore  créé à l'endroit même où vous avez tapé la commande et placez-le dans notre application, précisément dans le dossier android/app/.

À présent, dans le fichier android/gradle.properties, ajoutez les lignes suivantes, en remplaçant les informations par les vôtres : 

# android/gradle.properties
MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=*****
MYAPP_RELEASE_KEY_PASSWORD=*****

Allez, on a presque fini, dans android/app/build.gradle.

Ajoutez la configuration suivante :

# android/app/build.gradle
...
android {
...
defaultConfig { ... }
signingConfigs {
release {
if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
storeFile file(MYAPP_RELEASE_STORE_FILE)
storePassword MYAPP_RELEASE_STORE_PASSWORD
keyAlias MYAPP_RELEASE_KEY_ALIAS
keyPassword MYAPP_RELEASE_KEY_PASSWORD
}
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release
}
}
}
...

Et c'est fini.

 Mais attends, on a oublié plein de trucs, le bundle Javascript, ne plus passer par le localhost, passer en mode release, etc., non ?

Et bien, sur Android, non. Ce n'est pas qu'il ne faut pas passer par toutes ces étapes, c'est simplement qu'il existe une commande qui le fait pour nous. :soleil:

À la racine de votre projet React Native, saisissez la commande :

$ cd android && ./gradlew assembleRelease

Votre APK est généré dans le dossier /android/app/build/outputs/apk/. Enfin... vous devriez voir deux APK : un debug et un release. L'APK debug est généré à chaque fois que vous compilez (buildez) votre application pendant vos phases de développement. L'APK release est celui que vous enverrez au Google Play et qui est généré par la commande  ./gradlew assembleRelease  . Merci Android, car c'est quand même vachement plus simple que pour iOS et, en plus, pas besoin de modifier son code pour passer de debug à release.

On a notre APK, OK, c'est quoi la suite ?

Là encore, je m'arrête ici. Cela ne concerne plus React Native, mais le processus de livraison classique des applications Android. Vous devrez débourser 25 $ pour créer un compte développeur Android à vie et livrer votre application. Il n'y a pas de paiement annuel, comme sur iOS ; vous payez une fois et c'est pour toujours. :waw: Pour déposer votre application sur le Google Play, je vous invite à lire la documentation Android à ce sujet. Là aussi, c'est plus simple que pour iOS, il n'y a pas d'histoire de "provisionning" et de "certificats". Vous déposez votre APK sur la console Google Play et, quand vous le souhaitez, vous n'avez plus qu'à l'envoyer sur le store public. 

Même remarque que pour iOS, mais si vous poussez votre application jusque là, je serai ravi de voir le travail que vous avez effectué. Pensez à m'envoyer un MP. ;)

La suite ?

Voilà, cette fois mon cours est officiellement terminé. Avant de nous quitter, je vous ai préparé une liste de certains points que je n'ai pas abordés ici ou que je n'ai pas détaillés et qui peuvent être intéressants :

Proptypes

Les Proptypes sont une fonctionnalité de React Native permettant de vérifier les types des props passés entre les components. C'est utile pour s'assurer que l'on transmet le type de donnée attendu et éviter ainsi de nombreux bugs dans votre application. Les Proptypes s'inscrivent dans une démarche de fiabilité et de robustesse des applications.

Tests

Il existe plusieurs types de tests : unitaires, intégration et fonctionnels. Avec son découpage en components, React Native facilite énormément la mise en place de tests dans vos applications.

Module natif

J'en ai parlé rapidement dans un chapitre précédent, mais vous pouvez créer votre propre module natif iOS / Android et le faire communiquer avec React Native. Il y a une architecture spécifique à mettre en place, mais, une fois acquise, vous verrez que c'est loin d'être insurmontable. 

Mise à jour de React Native

React Native évolue et il évolue vite. De ce fait, il faut régulièrement mettre à jour sa version. C'est important pour disposer des nouveautés et peut-être d'ici peu de la version 1.0. :ange: 

Intégrer React Native dans une application native existante

Vous avez déjà une application mobile native qui tourne et qui vous plaît ? Vous souhaitez la faire évoluer ou même reprendre certaines parties de l'application ? Rien ne vous empêche d'y ajouter un peu de React Native.

Il est tout à fait possible d'ajouter du React Native dans une application native existante. C'est la démarche qu'a suivie Facebook, notamment pour migrer, petit à petit, certaines parties de son application en React Native.

CodePush

Vous le savez, le Javascript est un langage qui n'a pas besoin d'être compilé, il faut juste l'exécuter pour voir son rendu. De ce fait, imaginez que nous puissions envoyer un nouveau fichier bundle Javascript à nos utilisateurs et ainsi mettre à jour leur application automatiquement. Ce serait plutôt génial, non ? Et  bien, c'est possible, avec CodePush, une solution de Microsoft.

Contribution

React Native est Open Source, il y a une très grosse communauté derrière le framework. Rien ne vous empêche de faire partie de cette équipe et de contribuer à ce magnifique projet. :)

Conclusion

Ce cours touche à sa fin ! Vous avez à présent toutes les clés en main pour :

  • Décrire les avantages de React Native et le fonctionnement d’une Create React Native App

  • Expliquer les notions de base de React Native

  • Expliquer comment les vues sont gérées en React Native

  • Lister les possibilités d’optimisation d’une application React Native

  • Développer une application mobile React Native fonctionnelle

  • Gérer une donnée dans le state global avec Redux

  • Utiliser les props, le state et les styles avec React

N'oubliez pas de réaliser les exercices en fin de partie pour valider ces compétences. ;)

J'espère que ce cours vous a plu et que vous avez pris autant de plaisir à le lire que moi à l'écrire. Ce fut une super expérience pour moi, aussi bien sur le plan pédagogique que technique, car oui, moi aussi, j'ai appris des choses dans ce cours. :) Vous savez, ce sentiment de connaître une fonctionnalité et, au moment de l'expliquer à quelqu'un, d'en être incapable. Comme disait Einstein (moment poétique)

Si vous ne pouvez expliquer quelque chose simplement, c’est que vous ne l’avez pas bien compris.

Et je me suis rendu compte qu'il y avait beaucoup de fonctionnalités que je n'avais pas bien comprises. :lol:

N'hésitez pas à me faire vos retours, demander de l'aide dans vos projets, poser vos questions, etc., par MP, mail ou forum, je serai ravi d'y répondre et de vous aider. Merci aux personnes qui m'ont aidé dans la rédaction de ce cours, ce fut un réel plaisir d'échanger avec vous.

Je vous souhaite à tous beaucoup de réussite dans vos projets personnels et professionnels. Et un bon quiz et une activité pour finir en beauté. ;)

Exemple de certificat de réussite
Exemple de certificat de réussite