Integration in a React Native app
This guide describes how to setup a React Native application that integrates with Seamly Web UI using a React Native Webview and how to retain data across initialisations.
Important
This guide assumes that there is a hosted app.html
which uses the built-in appStorageProvider
. Usually this will be provided in your implementation. For this demo we use https://developers.seamly.ai/clients/web-ui/demos/app/index.html.
A demo app for this example can be found at https://gitlab.com/seamly-app/examples/react-native-demo-app.
Components
React Native's own Webview component has been removed from Core since 0.63. For this implementation we will use the most common alternative, React Native Webview. If you use a different Webview implementation please refer to its documentation for details on how to run JavaScript. We also use React Native Async Storage to retain our data between launches.
Connecting the web page
To connect the page, we only have to load a Webview with the right source URI:
render() => (
WebView
source={{uri: 'https://developers.seamly.ai/clients/web-ui/demos/app/index.html'}}
/>
)
On the JavaScript side, the default appStorageProvider should be in place. At this point you can run the app and should be able to load a chat client, but it will not store anything yet. In the next two paragraphs we'll store and retrieve the data.
Storing data
React Native Webview comes with a message handler built in. All we have to do is connect this message handler to a specific function and store the data in our chosen data store. We use AsyncStorage
for this example, but you can substitute your own version.
onMessageReceived = event => {
if (event.nativeEvent?.data) {
AsyncStorage.setItem('@SeamlyBridge', event.nativeEvent.data);
}
};
Be sure to connect this function to the WebView
inside the component:
WebView
source={{uri: 'https://developers.seamly.ai/clients/web-ui/demos/app/index.html'}}
onMessage={this.onMessageReceived}
/>
In the Webview, the appStorageProvider
will post a message to the onMessage
handler. will With the React Native part in place, all that's left is to actually post a message from our storageProvider
. Again we will simply use the mechanism already built into the WebView
:
set: function(newData) {
if (window.ReactNativeWebView) {
window.ReactNativeWebView.postMessage(JSON.stringify(newData))
}
}
You should be able to build and run the app again, but nothing will change. The data will be stored inside your app, but it isn't being retrieved yet. Let's tackle that next.
Note
This solution will simply store all data posted by the loaded webpage. If you want to use the WebView
for other purposes, or in general want to have a bit more control, you need to implement your own storageProvider to change the message to include for instance a specific key. Make sure, however, to always send back the same object you receive.
Retrieving data
We will need the stored data on initialisation. The appStorageProvider
needs to retrieve the data directly when it is created. Since we use AsyncStorage
the data might not be available directly when the component is created. For this example, we will simply render the WebView
when we are sure data is loaded. Under normal circumstances this should be almost instantaneous, but your storage provider or other needs might require a more sophisticated approach here.
WebView
again helps us out by providing a prop for preloading JavaScript: injectedJavaScriptBeforeContentLoaded
. All we need to do is retrieve the data from storage, so let's start with a function for that:
preloadData = async () => {
let value;
try {
value = await AsyncStorage.getItem('@SeamlyBridge');
} catch (e) {
console.error("Couldn't retrieve data from storage: ", e);
}
this.setState({
loaded: true,
preloadedData: value ? 'window.seamlyBridgeData=' + value : null,
});
};
Next, we call this function when we load our component:
componentDidMount() {
this.preloadData();
}
And connect it to the WebView
(as mentioned, only rendering it when loading is finished):
render() {
if (!this.state.loaded) {
return null;
}
return (
<WebView
source={{uri: 'https://developers.seamly.ai/clients/web-ui/demos/app/index.html'}}
injectedJavaScriptBeforeContentLoaded={this.state.preloadedData}
onMessage={this.onMessageReceived}
/>
);
}
That's it for the React Native side. The appStorageProvider
uses the data left by our initialisation process
Our app will now build and run and when you start a chat you can leave it and come back and the chat will continue where you left of. Our storage solution is working! But there is one small detail we overlooked...
Avoiding synchronisation issues
We only transmit the stored data on initialisation. This should be fine, since normally the storageProvider get
function will only be called when the client starts. But if it ever gets called again, we would be providing it with older data as we never update the internal variable after initialisation. Fortunately this is an easy fix, we can simply return our data to the Javascript directly after receiving it. For this, we need to add a reference to our WebView
:
<Webview
ref={r => this.webView = r}
// ...
/>
And expand the onMessageReceived
handler:
onMessageReceived = event => {
if (event.nativeEvent?.data) {
AsyncStorage.setItem('@SeamlyBridge', event.nativeEvent.data);
this.webView.injectJavaScript(`window.seamlyBridgeData=${event.nativeEvent.data}`)
}
};