Création et diffusion d’un package pour React Native

Création du repo sur GitHub

Création d’une bibliothèque React Native

npx create-react-native-module react-native-image-processing-tools --generate-example
  • Je me positionne dans le dossier parents, afin que mon projet se crée directement dans le dossier de mon projet, dont je pourrai alors commiter directement les modifications.
  • create-react-native-module est un outil créé par Chris Brody, qui vous permet de générer un template de module en React Native, et qui inclue déjà une application de démo (example), ainsi que le “pont” entre le code Natif et le code écrit en React Native.
  • L’option generate-example permet comme son nom l’indique de créer une application de démo dans mon projet.
cd example
yarn ios
yarn android

Structure du projet et explications

  • index.js : fichier racine de notre projet ;
  • ios : dossier contenant le code natif de notre projet en Objective C ;
  • android : dossier contenant le code natif de notre projet en Java ;
  • example : dossier contenant notre projet de test et de démo ;
import { NativeModules } from ‘react-native’;const { ImageProcessingTools } = NativeModules;export default ImageProcessingTools;
#import “ImageProcessingTools.h”
@implementation ImageProcessingTools
RCT_EXPORT_MODULE()RCT_EXPORT_METHOD(sampleMethod:(NSString *)stringArgument numberParameter:(nonnull NSNumber *)numberArgument callback:(RCTResponseSenderBlock)callback)
{
// TODO: Implement some actually useful functionality
callback(@[[NSString stringWithFormat: @”numberArgument: %@ stringArgument: %@”, numberArgument, stringArgument]]);
}
@end
package dev.onthebeach.rnimageprocessingtools;import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
public class ImageProcessingToolsModule extends ReactContextBaseJavaModule {
private final ReactApplicationContext reactContext;

public ImageProcessingToolsModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}
@Override
public String getName() {
return “ImageProcessingTools”;
}
@ReactMethod
public void sampleMethod(String stringArgument, int numberArgument, Callback callback) {
// TODO: Implement some actually useful functionality
callback.invoke(“Received numberArgument: “ + numberArgument + “ stringArgument: “ + stringArgument);
}
}
import ImageProcessingTools from ‘react-native-image-processing-tools’;
ImageProcessingTools.sampleMethod(‘Testing’, 123, (message) => {
this.setState({
status: ‘native callback received’,
message
});
});

Modification du code natif

//Récupération de l'entier définir dans numberArgument
int numberArgumentValue = [numberArgument intValue];
//Création d'un nouvel objet contenant la valeur de numberArgument incrémentée de 1
NSNumber *newNumberArgument = [NSNumber numberWithInt:numberArgumentValue + 1];
//Modification du callback pour renvoyer le nouvel objet
callback(@[[NSString stringWithFormat: @”numberArgument: %@ stringArgument: %@”, newNumberArgument, stringArgument]]);
//Création d'un nouvel entier dont la valeur est égale à celle de numberArgument incrémentée de 1
int newNumberArgument = numberArgument + 1;
//Modification du callback pour renvoyer le nouvel entier
callback.invoke(“Received numberArgument: “ + newNumberArgument + “ stringArgument: “ + stringArgument);
cd example
yarn ios
yarn android

Structure du code JS du module React Native

import { NativeModules } from ‘react-native’;const { ImageProcessingTools } = NativeModules;export default ImageProcessingTools;
import ImageProcessingTools from ‘./lib/imageProcessingTools’;
export default ImageProcessingTools;
import { NativeModules } from ‘react-native’;
const { ImageProcessingTools } = NativeModules;
//Déclaration de la classe RNImageProcessingTools
class RNImageProcessingTools {
//Création de la méthode sampleMethod de notre classe, qui pourra être appelée dans le code JS des apps utilisant le module
sampleMethod(stringArgument, numberArgument, callback) {
//Notre méthode JS appelle les méthodes natives, et en renvoie le résultat via la callback définie dans les arguments
ImageProcessingTools.sampleMethod(stringArgument, numberArgument, (message) => {
callback(message);
});
}
}
//Nous créons une instance de notre classe
const imageProcessingTools = new RNImageProcessingTools();
//Nous exportons l'insctance pour qu'elle soit utilisée dans notre code
export default imageProcessingTools;
La première ligne de l’aide me propose la méthode sampleMethod.
Une fois la méthode sélectionnée l’aide m’indique les arguments à y passer.
  • Celle d’origine simplement afficher via la bibliothèque Image de React Native depuis son URL
  • Celle renvoyée par notre module, qui sera représenté en Base64.
import React, { Component } from ‘react’;
import { Platform, StyleSheet, Text, View, Image } from ‘react-native’;
import ImageProcessingTools from ‘react-native-image-processing-tools’;//Déclaration de l'url de l'image à retravailler
const imageUrl = ‘https://images.unsplash.com/photo-1536602295863-59cf052c35f5?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=2636&q=80';
export default class App extends Component {
constructor() {
super();
this.state = {
status: ‘starting’,
processedImage: ‘’
};
this.getImageProcessed = this.getImageProcessed.bind(this);
}
componentDidMount() {
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Original Image</Text>
<Image source={{uri:imageUrl}} style={{width:320, height:191}} />
<Text style={styles.welcome}>Processed Image</Text>
{this.getImageProcessed()}

</View>
);
}
getImageProcessed() {
if (this.state.processedImage.length > 0) {
return <Image source={{uri:this.state.processedImage}} style={{width:320, height:191}} />
} else {
return <View style={{width:320, height:191, justifyContent:’center’, alignItems:’center’, backgroundColor:’grey’}}>
<Text>No image to display</Text>
</View>
}
}

}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: ‘center’,
alignItems: ‘center’,
backgroundColor: ‘#F5FCFF’,
},
welcome: {
fontSize: 20,
textAlign: ‘center’,
margin: 10,
}
});
Android à gauche et iOS à droite

Création de la méthode getBlackAndWhiteImageFromUrl sur iOS

#import “ImageProcessingTools.h”
@implementation ImageProcessingTools
RCT_EXPORT_MODULE()RCT_EXPORT_METHOD(getBlackAndWhiteImageFromUrl:(NSString *)imageUrl callback:(RCTResponseSenderBlock)callback) {}@end
  • UIKit — Framework des interfaces utilisateur d’iOS qui contient l’objet UIImage que nous allons utiliser dans le processus de création de l’image en Base64 ;
  • CoreImage — Framework de manipulation des images.
open ios/ImageProcessingTools.xcodeproj
#import “ImageProcessingTools.h”
#import <UIKit/UIKit.h>
#import <CoreImage/CoreImage.h>
@implementation ImageProcessingTools[…]@end
RCT_EXPORT_METHOD(getBlackAndWhiteImageFromUrl:(NSString *)imageUrl callback:(RCTResponseSenderBlock)callback) {
//Tout d'abord nous devons transformer notre chaine de caractère en une URL
NSURL *imageNSURL = [NSURL URLWithString:imageUrl];
//Puis nous allons stocker notre image dans un objet CIImage (CI pour Core Image), qui sera l'objet que nous allons pouvoir traiter
CIImage *originalCIImage = [CIImage imageWithContentsOfURL:imageNSURL];
//Nous allons créer un filtre avec l'effet Mono
CIFilter *blackAndWhiteFilter = [CIFilter filterWithName:@”CIPhotoEffectMono”];
//Nous allons appliquer ce filter à notre Image
[blackAndWhiteFilter setValue:originalCIImage forKey:kCIInputImageKey];
//Enfin nous en récupérons le resultat
CIImage *blackAndWhiteImage = blackAndWhiteFilter.outputImage;
//Pour convertir une image en Base64, la méthode la plus simple que j'ai trouvée est de passer par une UIImage
UIImage *anUIImage = [UIImage imageWithCIImage:blackAndWhiteImage];
NSData *imageData = UIImagePNGRepresentation(anUIImage);
NSString *imageDataAsString = [imageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
//Enfin nous renvoyons notre résulat au code natif
callback(@[imageDataAsString]);
}

Création de la méthode getBlackAndWhiteImageFromUrl sur Android

package dev.onthebeach.rnimageprocessingtools;import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
public class ImageProcessingToolsModule extends ReactContextBaseJavaModule {
private final ReactApplicationContext reactContext;
public ImageProcessingToolsModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}
@Override
public String getName() {
return “ImageProcessingTools”;
}
@ReactMethod
public void getBlackAndWhiteImageFromUrl(String imageUrl, Callback callback) {
}
}
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.util.Base64;
import java.io.ByteArrayOutputStream;
import java.net.URL;
import java.net.HttpURLConnection;
import java.io.InputStream;
import java.io.IOException;
public class ImageProcessingToolsModule extends ReactContextBaseJavaModule {
[...]
//Récupération d’une image d’une URL au format bitmap
public static Bitmap getBitmapFromURL(String imageSource) {
try {
URL imageUrl = new URL(imageSource);
HttpURLConnection connection = (HttpURLConnection) imageUrl.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap imageAsBitmap = BitmapFactory.decodeStream(input);
return imageAsBitmap;
} catch (IOException e) {
// TODO : Gestion des erreurs
return null;
}
}
//Transformation d'un objet Bitmap en noir et blanc
public static Bitmap applyGreyscale(Bitmap bitmapSource) {
//Définition des nuances pour obenir un noir et blanc
final double GS_RED = 0.299;
final double GS_GREEN = 0.587;
final double GS_BLUE = 0.114;
Bitmap bitmapOutput = Bitmap.createBitmap(bitmapSource.getWidth(), bitmapSource.getHeight(), bitmapSource.getConfig());
int A, R, G, B;
int pixel;
// Récupération de la taille de l’image
int width = bitmapSource.getWidth();
int height = bitmapSource.getHeight();
// On va modifier les pixels de l’image
for(int x = 0; x < width; ++x) {
for(int y = 0; y < height; ++y) {
//Récupération du pixel de l’image
pixel = bitmapSource.getPixel(x, y);
A = Color.alpha(pixel);
R = Color.red(pixel);
G = Color.green(pixel);
B = Color.blue(pixel);
R = G = B = (int)(GS_RED * R + GS_GREEN * G + GS_BLUE * B);
//Récupération du pixel transformé
bitmapOutput.setPixel(x, y, Color.argb(A, R, G, B));
}
}
// Renvoie du Bitmap transformé
return bitmapOutput;
}
@ReactMethod
public void getBlackAndWhiteImageFromUrl(String imageUrl, Callback callback) {
//On récupère l'image dans un BitMap qu'on transforme en Noir & Blanc
Bitmap imageBitmap = applyGreyscale(getBitmapFromURL(imageUrl));

//On va convertir notre Bitmap en image PNG puis en Data et enfin en une chaine de caractère encodée en Base64
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
imageBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream.toByteArray();
String imageDataAsString = Base64.encodeToString(byteArray, Base64.DEFAULT);
//On renvoie notre résultat à notre module
callback.invoke(imageDataAsString);
}
}

Implémentation de la méthode getBlackAndWhiteImageFromUrl dans notre module

import { NativeModules } from ‘react-native’;
const { ImageProcessingTools } = NativeModules;
class RNImageProcessingTools {
getBlackAndWhiteImageFromUrl(imageUrl, callback) {
ImageProcessingTools.getBlackAndWhiteImageFromUrl(imageUrl, (response) => {
const base64Image = ‘data:image/png;base64,’+response;
callback(base64Image);
});
}
}
const imageProcessingTools = new RNImageProcessingTools();export default imageProcessingTools;

Démo de la méthode getBlackAndWhiteImageFromUrl dans l’app React Native

ImageProcessingTools.getBlackAndWhiteImageFromUrl(imageUrl, (response) => {
this.setState({processedImage:response});
});
cd example
yarn ios
yarn android
Image transformée en noir et blanc sur iOS
Image transformée en noir et blanc sur iOS

Création de notre package

npm adduser
npm publish --access=public

--

--

Show Runner de projets mobiles. Développeur React Native et passionné par les challenges du monde mobile.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store