Intégrer crashlytics de firebase à votre application React Native.
Le “crash reporting” vous connaissez ? C’est une des notions les plus importantes afin d’assurer la bonne qualité de votre application.
Les bugs et crashes qui appairaissent dans votre application une fois déployée, peuvent être très difficile à taquer. Vous ne serez sûrement pas en contact avec les utilisateurs afins qu’ils puissent vous détailler ce qu’il s’est passé. Et encore pire, l’utilisateur vous laissera sûrement une mauvaise note.
Dans ce tutoriel, nous allons voir comment intégrer le module crashlytics à votre applicatin react native.
Ce tutoriel nécessite que vous ayez complété notre première partie sur l’initialisation de firebase avec React Native.
Installation
Le module @react-native-firebase/crashlytics propose des fonctionnalités utiles pour les développeurs.
Créer le projet dans la console Firebase
Allez sur la console Firebase, assurez-vous que votre projet est créé, sinon suivez l’article précédent.
Intégrer @react-native-firebase/crashlytics
Exécutez dans un terminal dans votre projet :
yarn add @react-native-firebase/crashlytics cd ios/ && pod install
Pour la partie android, dans `android/build.gradle` :
// ..
buildscript {
// ..
dependencies {
// ..
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
}
// ..
}
Puis build à nouveau le projet avec :
npx react-native run-android
Tester votre configuration
Afin d’être sûr que le paramétrage fonctionne, créez d’abord un fichier `firebase.json` à la racine de votre projet, en y ajoutant :
{
"react-native": {
"crashlytics_auto_collection_enabled": true,
"crashlytics_debug_enabled": true, // c'est temporaire, on va remettre à false plus tard
"crashlytics_javascript_exception_handler_chaining_enabled": false
}
}
`crashlytics_debug_enabled` permet de dire à Crashlytics d’également tracker les bugs en mode debug. C’est important pour notre premier test.
Ensuite, soit lors d’un clic de bouton, soit au chargement d’un screen, insérez ce bout de code :
import crashlytics from '@react-native-firebase/crashlytics'; // .. crashlytics().crash();
Allez ensuite sur la console Firebase, puis attendez 1 ou 2 minutes pour voir apparaître votre erreur :
Testez la manip’ sur les 2 simulateurs iOS et android pour être sûr que tout fonctionne.
Maintenant, on peut passer aux cas pratiques !
Intégration d’un ErrorBoundary
Explication
Crashlytics va maintenant attraper les erreurs qui proviennent dans votre application, et vous les lister dans la console. Mais comme vous le savez, le code `react native` est compilé en natif, donc vous perdez votre code javascript lors de la release.
Donc vos stack d’erreurs ne seront pas très lisible.
Exemple de stack d’erreur reporté par `crashlytics`.
Fatal Exception: java.lang.ClassCastException: com.facebook.react.bridge.ReadableNativeMap cannot be cast to java.lang.String
at com.facebook.react.bridge.ReadableNativeArray.getString(ReadableNativeArray.java:102)
at com.facebook.react.bridge.JavaMethodWrapper$5.extractArgument(JavaMethodWrapper.java:73)
at com.facebook.react.bridge.JavaMethodWrapper$5.extractArgument(JavaMethodWrapper.java:69)
at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:356)
at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:151)
at com.facebook.react.bridge.queue.NativeRunnable.run(NativeRunnable.java)
La méthode pour récupérer l’erreur javascript est la suivante :
crashlytics().recordError(error);
ErrorBoundary
L’explication étant maintenant terminée, passons à la bonne pratique. Le composant ErrorBoundary permet de rattraper les erreurs qui se produisent dans le code javascript, et de montrer une vue plus propre à l’utilisateur, au lieu de faire cracher l’application.
Comme par exemple :
Nous allons mettre en place ce composant, et envoyer les erreurs à Crashlytics à ce moment.
Dans `component/ErrorBoundary.js` :
import crashlytics from '@react-native-firebase/crashlytics';
import { node } from 'prop-types';
import React, { Component } from 'react';
import { SafeAreaView, Text, View } from 'react-native';
import RNRestart from 'react-native-restart'
import Button from '~/components/common/Button';
import t from '~/configs/i18n';
import styles from './styles';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {};
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
if(errorInfo && errorInfo.componentStack) {
crashlytics().log(errorInfo.componentStack);
}
crashlytics().recordError(error, error.message ? error.message : undefined);
}
onRestart() {
RNRestart.Restart();
};
render() {
const { hasError } = this.state;
const { children } = this.props;
if (hasError) {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
<View style={styles.content}>
<Text style={{ width: '100%', }}>
500
</Text>
<Text style={{ fontSize: 32 }}>{t('label.error')}</Text>
<Text style={{ marginVertical: 10, lineHeight: 23, fontWeight: '500' }}>
{t('error.description')}
</Text>
<Button
onPress={() => this.onRestart()}
>
{t('action.backToLogin')}
</Button>
</View>
</View>
</SafeAreaView>
);
}
return children;
}
}
ErrorBoundary.propTypes = {
children: node,
};
ErrorBoundary.defaultProps = {
children: null,
};
export default ErrorBoundary;
Puis dans `App.js`, entourez votre application avec ce composant :
return (
<ErrorBoundary>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
{storybookActive ? <StorybookUIRoot /> : <Router />}
</PersistGate>
</Provider>
</ErrorBoundary>
);
Voila ! Maintenant à chaque crash, nous allons envoyer les informations js à la console `Crashlytics` de `Firebase`. Ce que j’aime bien mettre en place, c’est le rechargement de l’application lors du clic sur le bouton, grâce à la dépendance react-native-restart. C’est optionnel bien sûr.
Loggez encore plus d’information
Grâce à l’étape précédente, vous aurez la stacktrace javascript. Mais c’est toujours mieux de rajouter plus d’info (bien sûr, avec parcimonie).
Pour cela 2 méthodes :
I am text block. Click edit button to change this text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
crashlytics().log(errorInfo.componentStack); //celle-là permet de logger une activité sur crashlytics crashlytics().setAttribute(attribute, value); //celle-là permet de mettre un attribut
J’utilise la 2ème méthode pour savoir sur quelle page l’utilisateur se trouvait avant le grand crash.
Si vous utilisez @react-navigation/native :
<NavigationContainer
theme={AppTheme}
ref={navigationRef}
onReady={() => {
routeNameRef.current = navigationRef.current.getCurrentRoute().name;
crashlytics().setAttribute("screen", routeNameRef.current);
}}
onStateChange={() => {
const previousRouteName = routeNameRef.current;
const currentRouteName = navigationRef.current.getCurrentRoute().name;
if (previousRouteName !== currentRouteName) {
crashlytics().setAttribute("screen", currentRouteName);
}
routeNameRef.current = currentRouteName;
}}
>
Conclusion
Et voila ! Ça fait pas mal d’information quand même. Maintenant j’espère que vous allez pouvoir débugger vos applications plus facilement. Ne tracker pas des informations susceptibles d’identifier vos utilisateurs (#rgpd).
