• 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

Utilisez les composants du device

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

Si vous êtes encore là, c'est que vous avez envie d'en découdre avec notre application React Native avec du code natif et les composants des devices. J'ai peut-être semblé un peu nostalgique dans le précédent chapitre, lorsque l'on a abandonné notre CRNA, mais, sachez-le, le passage sur une application React Native avec du code natif nous a ouvert une multitude de possibilités : composants du device, librairies React Native, développement natif, etc.

Dans ce chapitre, je vous propose d'ajouter un peu d'humanité dans notre application.

Ah ouais, carrément ? :waw:

Bon, non, OK, j'exagère un peu, là. On va ajouter la possibilité à l'utilisateur de définir son propre avatar. L'avatar peut venir de sa galerie photo ou de l'appareil photo. On va donc avoir besoin d'accéder à deux composants du device : la galerie photo et la caméra. Vous voyez pourquoi je vous disais que la librairie Expo aurait pu nous servir ? Mais le but d'un cours est aussi de voir toutes les possibilités, et notamment celles qui sont les moins restrictives. C'est ce que l'on va voir ensemble dans ce chapitre.

Utilisez un composant natif

Comme vous le savez, l'accès aux composants du device passe par du langage natif. On va donc commencer par créer une classe Swift pour iOS et une classe Java pour Android et... je rigole. :lol: Je ne vais certainement pas vous obliger à développer en natif. Déjà, parce que c'est long, difficile et périlleux, mais surtout parce que ce n'est pas le sujet de ce cours.

Au lieu de développer notre fonctionnalité en natif, on va utiliser une librairie qui l'a déjà fait pour nous. Il s'agit de la librairie react-native-image-picker, une librairie React Native développée, une nouvelle fois, par la communauté React Native. Elle permet deux choses : 

  • récupérer une photo depuis la galerie photo ;

  • prendre et récupérer une photo prise depuis la caméra.

C'est exactement ce dont on a besoin.

Installez une librairie React Native

Pour installer une librairie React Native, on va, comme à notre habitude, utiliser le gestionnaire de paquet de Node, à savoir  npm . À la racine de notre projet, tapez la commande :

$ npm install --save react-native-image-picker

Le mieux ensuite est de suivre la documentation d'installation de la librairie. On peut y lire qu'il faut saisir une commande  link  , alors allons-y : 

$ react-native link

Vous devriez voir apparaître, dans notre terminal :

rnpm-install info Linking react-native-image-picker android dependency
rnpm-install info Android module react-native-image-picker has been successfully linked
rnpm-install info Linking react-native-image-picker ios dependency
rnpm-install info iOS module react-native-image-picker has been successfully linked

 Que s'est-il passé ici ?

La commande  link  de React Native permet de lier des librairies à nos projets mobiles natifs. Grâce à cette association, nos projets mobiles natifs vont être capables d'accéder aux librairies React Native et à leurs fonctionnalités. Dès la prochaine compilation,notre application pourra utiliser la librairie que l'on a ajoutée.

On en a presque fini avec l'installation de la librairie, cela va vite ! En suivant les étapes de la documentation, on peut y lire qu'il faut ajouter des permissions aux projets mobiles natifs. Car oui, l'accès à certains composants du device requiert l'autorisation de l'utilisateur. Vous savez, ce sont ces pop-up qui s'affichent et qui vous disent : "L'application X a besoin d'accéder à la fonctionnalité Y." Si vous accédez à un composant du device sans demander la permission de l'utilisateur, l'application va crasher, automatiquement. On ne rigole pas avec ça ! :waw:

On commence par iOS. Dans le fichier /ios/MoviesAndMe/Info.plist, ajoutez toutes ces permissions :

// /ios/MoviesAndMe/Info.plist
<plist version="1.0">
<dict>
...
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) voudrait accéder à votre galerie photo</string>
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) voudrait utiliser votre caméra</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>$(PRODUCT_NAME) voudrait enregistrer des photos dans votre galerie photo</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) voudrait accéder à votre micro (pour les vidéos)</string>
</dict>
</plist>

Sur iOS, c'est tout bon. Maintenant, passons à Android.

Dans le fichier android/app/src/main/AndroidManifest.xml, ajoutez : 

// /android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name...

OK, on a tout ce qu'il faut, notre application ne va pas "exploser" lorsque l'utilisateur va lancer l'appareil photo. :lol: La librairie react-native-image-picker est installée et configurée. Vous pouvez tester que votre projet fonctionne en le recompilant avec la commande  react-native run-ios / android  .

Ajoutez le component Avatar

Ici, on va créer le component qui va afficher l'avatar de l'utilisateur. Rien de très compliqué ici, notre component Avatar doit ressembler à cela :

Component Avatar
Component Avatar

L'image que vous voyez ici n'est autre que l'avatar par défaut. Commencez donc par récupérer l'image suivante et renommez-la en ic_tag_faces.png. Placez-la dans le dossier /images :

Avatar par défaut
Avatar par défaut

Au clic, sur notre Avatar, nous allons appeler une fonction  _avatarClicked()  dans laquelle nous utiliserons la librairie react-native-image-picker pour récupérer un avatar. 

Créez donc un component Avatar, comme ceci :

// Components/Avatar.js
import React from 'react'
import { StyleSheet, Image, TouchableOpacity } from 'react-native'
class Avatar extends React.Component {
constructor(props) {
super(props)
this.state = {
avatar: require('../Images/ic_tag_faces.png')
}
}
_avatarClicked() {
// Ici nous appellerons la librairie react-native-image-picker pour récupérer un avatar
}
render() {
return(
<TouchableOpacity
style={styles.touchableOpacity}
onPress={this._avatarClicked}>
<Image style={styles.avatar} source={this.state.avatar} />
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
touchableOpacity: {
margin: 5,
width: 100, // Pensez bien à définir une largeur ici, sinon toute la largeur de l'écran sera cliquable
height: 100,
justifyContent: 'center',
alignItems: 'center'
},
avatar: {
width: 100,
height: 100,
borderRadius: 50,
borderColor: '#9B9B9B',
borderWidth: 2
}
})
export default Avatar

Pas de surprise ici. ;)

Nous allons afficher notre avatar dans la vue Favoris, tout en haut de la liste des films favoris :

// Components/Favorites.js
import {, View } from 'react-native'
import Avatar from './Avatar'
// ...
class Favorites extends React.Component {
render() {
return (
<View style={styles.main_container}>
<View style={styles.avatar_container}>
<Avatar/>
</View>
<FilmList
films={this.props.favoritesFilm}
navigation={this.props.navigation}
favoriteList={true}
/>
</View>
)
}
}
const styles = StyleSheet.create({
main_container: {
flex: 1
},
avatar_container: {
alignItems: 'center'
}
})
// ...

Dans notre application, on a tous :

Component Avatar affiché dans la vue Favoris
Component Avatar affiché dans la vue Favoris

OK, super ! :) Maintenant, on peut s'intéresser à ce pour quoi on est ici. On va utiliser la librairie react-native-image-picker pour accéder aux composants du device et récupérer un avatar.

Utilisez la librairie react-native-image-picker

Commençons par ajouter la librairie à notre component Avatar :

// Components/Avatar.js
import ImagePicker from 'react-native-image-picker'

Ensuite, intéressons-nous au fonctionnement de la librairie. Au vu de la documentation, il faut utiliser la méthode  showImagePicker  qui renvoie plusieurs types de réponses, selon si cela s'est bien passé ou non : 

  • Dans le cas où cela ne se passe pas bien, on va afficher des logs. Si vous le voulez, vous pouvez pousser un peu plus loin et afficher des alertes. Vous savez faire, maintenant.

  • Dans le cas où cela se passe bien, on récupère un  uri  , c'est-à-dire un lien vers notre avatar enregistré par la librairie. Un  uri  n'est pas un lien statique vers une image, on n'utilisera donc pas  require  .

Cela nous donne, côté code :

// Components/Avatar.js
constructor(props) {
super(props)
this.state = {
avatar: require('../Images/ic_tag_faces.png')
}
// this.setState est appelé dans un callback dans showImagePicker, pensez donc bien à binder la fonction _avatarClicked
this._avatarClicked = this._avatarClicked.bind(this)
}
_avatarClicked() {
ImagePicker.showImagePicker({}, (response) => {
if (response.didCancel) {
console.log('L\'utilisateur a annulé')
}
else if (response.error) {
console.log('Erreur : ', response.error)
}
else {
console.log('Photo : ', response.uri )
let requireSource = { uri: response.uri }
this.setState({
avatar: requireSource
})
}
})
}

Normalement, c'est tout bon, vous pouvez tester sur votre application que ça fonctionne : (Comme à mon habitude, je vous ai mis des cercles rouges au niveau des clics effectués sur l'application. J'ai testé la prise de photo et la récupération d'une photo de la galerie.)

Fonctionnalités de la librairie react-native-image-picker (en rouge mes actions utilisateurs)
Fonctionnalités de la librairie react-native-image-picker (en rouge mes actions utilisateur)

Cela vous plaît ? Vous avez vu avec quelle rapidité on a pu installer et utiliser la librairie ? Finalement, une application React Native avec du code natif n'est pas si lente. Enfin, cela dépend pour quoi. ^^

Gérez notre avatar dans le store Redux

On arrive à gérer notre avatar dans notre vue Favoris, mais je me dis que je risque, par la suite, d'avoir besoin d'afficher mon avatar à d'autres endroits de l'application. Bien sûr, lorsque mon avatar est mis à jour, je veux que tous les components qui l'affichent se re-rendent pour afficher le nouvel avatar. 

Vous connaissez déjà la solution, il faut gérer notre avatar dans notre state global, dans le store Redux. Je vous laisse vous en occuper, c'est bon ? ;) Ce sera l'occasion de faire une piqûre de rappel sur pas mal d'éléments que l'on a vus depuis le début de ce cours. 

Avant de vous lancer, je vous donne quelques recommandations qui vous seront utiles :

  1. Vous allez devoir créer un nouveau reducer pour gérer l'avatar. Rappelez-vous, un reducer par fonctionnalité. Au moment d'ajouter ce nouveau reducer au store Redux, vous allez être bloqué. Actuellement, on ne peut ajouter qu'un reducer maximum au store. Il faut utiliser la fonction  combineReducers  de Redux pour ajouter plusieurs reducers au store :

    // Store/configureStore.js
    import { createStore, combineReducers } from 'redux';
    import toggleFavorite from './Reducers/favoriteReducer'
    import setAvatar from './Reducers/avatarReducer'
    export default createStore(combineReducers({toggleFavorite, setAvatar}))
  2. Ce changement implique qu'à présent, au moment de connecter des données de votre store Redux à vos components, vous devez spécifier dans quel reducer aller chercher ces données. Autrement dit, ne plus faire :

    const mapStateToProps = state => {
    return {
    favoritesFilm: state.favoritesFilm
    }
    }

    Mais bien :

    const mapStateToProps = state => {
    return {
    favoritesFilm: state.toggleFavorite.favoritesFilm
    }
    }

Cette fois, vous avez toutes les infos. ;) Je vous laisse chercher par vous-même et, de mon côté, je vous mets la solution juste en dessous.

Solution :

// Store/configureStore.js
import { createStore, combineReducers } from 'redux';
import toggleFavorite from './Reducers/favoriteReducer'
import setAvatar from './Reducers/avatarReducer'
export default createStore(combineReducers({toggleFavorite, setAvatar}))
// Store/Reducers/avatarReducer.js
const initialState = { avatar: require('../../Images/ic_tag_faces.png') }
function setAvatar(state = initialState, action) {
let nextState
switch (action.type) {
case 'SET_AVATAR':
nextState = {
...state,
avatar: action.value
}
return nextState || state
default:
return state
}
}
export default setAvatar
// Components/FilmList.js
...
const mapStateToProps = state => {
return {
favoritesFilm: state.toggleFavorite.favoritesFilm
}
}
// Components/FilmDetail.js
...
const mapStateToProps = (state) => {
return {
favoritesFilm: state.toggleFavorite.favoritesFilm
}
}
// Components/Favorites.js
...
const mapStateToProps = state => {
return {
favoritesFilm: state.toggleFavorite.favoritesFilm
}
}
// Components/Avatar.js
import React from 'react'
import { StyleSheet, Image, TouchableOpacity } from 'react-native'
import { connect } from 'react-redux'
import ImagePicker from 'react-native-image-picker'
class Avatar extends React.Component {
constructor(props) {
super(props)
this._avatarClicked = this._avatarClicked.bind(this)
}
_avatarClicked() {
ImagePicker.showImagePicker({}, (response) => {
if (response.didCancel) {
console.log('L\'utilisateur a annulé')
}
else if (response.error) {
console.log('Erreur : ', response.error)
}
else {
console.log('Photo : ', response.uri )
let requireSource = { uri: response.uri }
// On crée une action avec l'image prise et on l'envoie au store Redux
const action = { type: "SET_AVATAR", value: requireSource }
this.props.dispatch(action)
}
})
}
render() {
return(
<TouchableOpacity
style={styles.touchableOpacity}
onPress={this._avatarClicked}>
{/* A présent on peut récupérer notre avatar dans les props. Souvenez-vous Redux mappe notre state global et ses données dans les props de notre component. */}
<Image style={styles.avatar} source={this.props.avatar} />
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
touchableOpacity: {
margin: 5,
width: 100,
height: 100,
justifyContent: 'center',
alignItems: 'center'
},
avatar: {
width: 100,
height: 100,
borderRadius: 50,
borderColor: '#9B9B9B',
borderWidth: 2
}
})
// On mappe l'avatar aux props de notre component
const mapStateToProps = state => {
return {
avatar: state.setAvatar.avatar
}
}
export default connect(mapStateToProps)(Avatar)

Cela a été ? Vous pouvez vérifier que notre application et la fonctionnalité Avatar fonctionnent toujours aussi bien. Finalement, ajouter un reducer n'est pas si compliqué que cela. Une fois que l'architecture Redux est en place, cela va extrêmement vite. Maintenant, si on souhaite afficher notre avatar ailleurs dans l'application, on peut, et très facilement. :-°
 

C'est sur ce petit rappel de Redux que je clos ce chapitre.

Maintenant que vous savez comment installer une librairie React Native qui utilise les composants du device, plus rien ne vous empêche d'en installer d'autres. La communauté React Native est très active, de nombreuses librairies sortent chaque jour.

Il existe un site qui recense toutes les librairies React Native avec un indicateur de qualité, leur compatibilité (iOS, Android, Expo pour les CRNA, Web), etc. Il s'agit du site Native Directory.

Pour revenir au site Native Directory, il est très pratique quand on est à la recherche d'une fonctionnalité ou que l'on souhaite rapidement voir et comparer des librairies entre elles. On retrouve, par exemple, notre librairie react-native-image-picker :

Librairie react-native-image-picker sur le site Native Directory
Librairie react-native-image-picker sur le site Native Directory

On peut voir qu'elle n'est pas compatible avec Expo. Elle n'aurait pas fonctionné sur notre CRNA. Je n'avais pas le choix de vous faire passer sur une application React Native avec du code natif.

Dans le prochain chapitre, je vais vous parler des outils pour débuguer son application. Pour l'instant, notre débugage s'est limité au  console.log  de Javascript, mais vous allez voir que l'on peut aller beaucoup plus loin. ;) 

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