This repository gives a template for using Rust functions in a React Native project through WebAssembly. Here's the TLDR:
- Create a
wasm-pack
project exposing the Rust functions you want to export. - Serve a web page exposing these functions through
message
events. - Use a React Native
WebView
of this web page. - Call Rust functions by sending messages to the
WebView
.
- Alternative is to use native modules but it is a pain to setup with Rust and React Native, and requires lots of different configurations for Android and iOS.
- Other alternative is to serve
wasm
files locally on the app and use them in aWebView
. This is also a pain to setup since local files need to be put in different places in Android and iOS, and you will get a bunch of permission errors along the way. - The way shown in this repository is very easy to setup, works out of the box, and allows for a clear separation between the Rust and React Native development.
- The device needs an Internet connection to download the web page.
- Mild privacy issue since one can track requests to the web server. But all computations using WebAssembly are done locally on the device.
- Probably quite slower than using the native modules.
This gives an example of this structure with a simple Rust function that adds two numbers.
Create a wasm-pack
project following the instructions given here. The src/lib.rs
exposes the functions you want to export. Here's ours:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add(a: u32, b: u32) -> u32 {
a + b
}
Then, build the package with wasm-pack build
and create a web directory with package.json
like:
/* -------- SNIP -------- */
"devDependencies": {
"rust": "file:../pkg",
"webpack": "^4.29.3",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.5",
"copy-webpack-plugin": "^5.0.0"
}
/* -------- SNIP -------- */
The WebAssembly functions can be called from Javascript using message
events. Here's our index.js
:
import * as wasm from "rust"; // Import the wasm package
let sum;
document.addEventListener("message", function(event) { // Receive parameters in a message
let {a: s1, b: s2} = JSON.parse(event.data); // Parse the parameters
sum = wasm.add(s1, s2); // Call the wasm function
window.ReactNativeWebView.postMessage(JSON.stringify({sum})); // Send a message to React Native with the result of the wasm function.
}, false);
Now, serve this web directory on a web server. For this prototype, we serve it on localhost:8080
.
Create a React Native project with react-native-webview
linked. Here is our App.js
that shows the basic way to call the WebAssembly functions served by our web server:
import React, { Component } from 'react';
import { View, Button, TextInput, Text } from 'react-native';
import { WebView } from 'react-native-webview';
export default class App extends Component {
webView = null; // Holds the reference to the WebView
state = { s1: null, s2: null, sum: null };
// Sends a message to the WebView with the parameters of the Rust function `add`.
sendMessage = () => {
this.webView.postMessage(JSON.stringify({ a: parseInt(this.state.s1), b: parseInt(this.state.s2) }));
}
// Listen for messages from the WebView containing the result of the wasm function.
onMessage = (event) => {
this.setState({sum: JSON.parse(event.nativeEvent.data).sum}, () => console.log(this.state));
}
render() {
return (
<View style={{ flex: 1 }}>
<Button
title="Press me"
onPress={this.sendMessage} // Sends a message on button press.
/>
<TextInput
placeholder="First summand"
onChangeText={(text) => this.setState({ s1: text })}
value={this.state.s1}
/>
<TextInput
placeholder="Second summand"
onChangeText={(text) => this.setState({ s2: text })}
value={this.state.s2}
/>
<WebView
style={{ height: 0 }}
useWebkit={true}
originWhitelist={['*']}
javaScriptEnabled={true}
source={{ uri: 'http://10.0.2.2:8080/' }} // Change to your webserver.
allowFileAccess={true}
cacheEnabled={false}
ref={(webView) => this.webView = webView} // Set ref.
onMessage={this.onMessage} // Listens for messages.
/>
{this.state.sum &&
<Text>The sum is {this.state.sum}</Text>}
</View>
)
}
}
This component gives a way to call the Rust function add
from React Native, through WebAssembly.