Skip to content

Instantly share code, notes, and snippets.

@blankg
Last active July 12, 2022 06:53
Show Gist options
  • Save blankg/d5537a458b55b9d15cb4fd78258ad840 to your computer and use it in GitHub Desktop.
Save blankg/d5537a458b55b9d15cb4fd78258ad840 to your computer and use it in GitHub Desktop.
For RN >= 0.57 please look at https://github.com/blankg/rn-webview-bridge-sample-new/blob/master/resources/index.html. Provides a sample implementation for sending and receiving messages to and from the React-Native WebView (using postMessage/onMessage WebView API).
/**
* Created by Guy Blank on 3/9/17.
*
* This is a sample provides an API to send & receive messages to and from the React-Native WebView (using postMessage/onMessage WebView API).
* A sample project that uses the bridge is available here https://github.com/blankg/rn-webview-bridge-sample
*
* webViewBridge.send('functionToInvoke', {mydata: 'test'}, function(){console.log('success')},function(){console.log('error')});
*
* The API is designed to be similar to the Cordova exec API so migration to it should be almost seamless.
* The API also provides solution to a React-Native WebView bug in iOS which causes sending consecutive postMessage calls to override each other.
*
* Handling message on the React-Native side:
* <WebView
* ref={webview => { this.myWebView = webview; }}
* onMessage={this.onWebViewMessage}
* />
*
* onWebViewMessage(event) {
* // post back reply as soon as possible to enable sending the next message
* this.myWebView.postMessage(event.nativeEvent.data);
*
* let msgData;
* try {
* msgData = JSON.parse(event.nativeEvent.data);
* }
* catch(err) {
* console.warn(err);
* return;
* }
*
* // invoke target function
* const response = this[msgData.targetFunc].apply(this, [msgData]);
* // trigger success callback
* msgData.isSuccessfull = true;
* msgData.args = [response];
* this.myWebView.postMessage(JSON.stringify(msgData))
* }
*
*/
(function(){
var promiseChain = Promise.resolve();
var promises = {};
var callbacks = {};
var init = function() {
const guid = function() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4();
}
window.webViewBridge = {
/**
* send message to the React-Native WebView onMessage handler
* @param targetFunc - name of the function to invoke on the React-Native side
* @param data - data to pass
* @param success - success callback
* @param error - error callback
*/
send: function(targetFunc, data, success, error) {
success = success || function(){};
error = error || function () {};
var msgObj = {
targetFunc: targetFunc,
data: data || {},
msgId: guid(),
};
var msg = JSON.stringify(msgObj);
promiseChain = promiseChain.then(function () {
return new Promise(function (resolve, reject) {
console.log("sending message " + msgObj.targetFunc);
promises[msgObj.msgId] = {resolve: resolve, reject: reject};
callbacks[msgObj.msgId] = {
onsuccess: success,
onerror: error
};
window.postMessage(msg);
})
}).catch(function (e) {
console.error('rnBridge send failed ' + e.message);
});
},
};
window.document.addEventListener('message', function(e) {
console.log("message received from react native");
var message;
try {
message = JSON.parse(e.data)
}
catch(err) {
console.error("failed to parse message from react-native " + err);
return;
}
//resolve promise - send next message if available
if (promises[message.msgId]) {
promises[message.msgId].resolve();
delete promises[message.msgId];
}
//trigger callback
if (message.args && callbacks[message.msgId]) {
if (message.isSuccessfull) {
callbacks[message.msgId].onsuccess.apply(null, message.args);
}
else {
callbacks[message.msgId].onerror.apply(null, message.args);
}
delete callbacks[message.msgId];
}
});
};
init();
}());
@aschenkel
Copy link

aschenkel commented Sep 13, 2017

This example is great! Really helped me.
One comment though.

I needed to change

               callbacks[message.msgId].onsuccess.apply(null, message.args);

to

               callbacks[message.msgId].onsuccess(message.args);

to make it work correctly

@blankg
Copy link
Author

blankg commented Sep 27, 2017

updates thanks @aschenkel

@Jancat
Copy link

Jancat commented Oct 22, 2017

Thanks a lot. I had realized the basic communication between RN and WebView based on this code.
But I found some faults in my practice :

  1. line 35: this.myWebView.postMessage(msgData) should be this.myWebView.postMessage(JSON.stringify(msgData))?
  2. line 117,120callbacks[message.msgId].onxxx.apply(message.args); should be callbacks[message.msgId].onxxx(message.args)?

In addition, is there possible to use promise.then() in webview, as an alternative beyong callback()?

@blankg
Copy link
Author

blankg commented Nov 2, 2017

Hi @Jancat, thanks for the feedback.
#1 is fixed.
#2 I actually ruined this in my previous change - it should be callbacks[message.msgId].onxxx.apply(null, message.args)
as args is an array of arguments you want to spread to your callback (perhaps callbacks[message.msgId].onxxx(...message.args) will work but I'm not sure about the spread operator support in webviews).

As for Promises there is no problem using them in webviews (as you can see I'm also using them), reason I'm using callbacks is actually because I wanted to be easier to convert from Cordova/Phonegap API.

@Jancat
Copy link

Jancat commented Nov 8, 2017

Could you show an example to use promise in webview communicated with RN? @blankg

And should I worry about the support of promise in webview?

@Timoteus78
Copy link

Nice @blankg Though has this been tested for android as well?

@okansahin
Copy link

use bind in ECMAScript5
onMessage={this.onWebViewMessage.bind(this)}

@blankg
Copy link
Author

blankg commented Jan 4, 2018

Created a sample project that uses the bridge https://github.com/blankg/rn-webview-bridge-sample

@neilhamilton
Copy link

@blankg this helped me huge

@EvDevNinja
Copy link

EvDevNinja commented Apr 26, 2018

I've added support for async Promises (React Native Side) here, thanks @blankg for the code!
https://gist.github.com/EvDevNinja/f3979e00c5f0734297fae5ed79f850b8

@mrikirill
Copy link

Good job!!! It works perfect for me!

@Nixen85
Copy link

Nixen85 commented Aug 16, 2018

Thx, it worked for me, but I had to change

window.postMessage(msg); to window.postMessage(msg, '*');

otherwise I got the error

“Failed to execute 'postMessage' on 'Window': 2 arguments required, but only 1 present.”

@ap050492
Copy link

Hi all,

I tried with WebViewBridge.js in my project but it's not working with some of the devices.
Can you please help me?

can you please find my problem in below link,
https://stackoverflow.com/questions/49444738/react-native-highcharts-call-method

@kwoktung
Copy link

good

@antonioaltamura
Copy link

this used to work with RN 0.54.0, now it doesn't work anymore..not sure what's the problem..it's a nighmare try to debug a RN webview..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment