-
-
Save blankg/d5537a458b55b9d15cb4fd78258ad840 to your computer and use it in GitHub Desktop.
/** | |
* 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(); | |
}()); |
updates thanks @aschenkel
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 :
- line
35
:this.myWebView.postMessage(msgData)
should bethis.myWebView.postMessage(JSON.stringify(msgData))
? - line
117
,120
:callbacks[message.msgId].onxxx.apply(message.args)
; should becallbacks[message.msgId].onxxx(message.args)
?
In addition, is there possible to use promise.then()
in webview, as an alternative beyong callback()
?
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.
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?
Nice @blankg Though has this been tested for android as well?
use bind in ECMAScript5
onMessage={this.onWebViewMessage.bind(this)}
Created a sample project that uses the bridge https://github.com/blankg/rn-webview-bridge-sample
@blankg this helped me huge
I've added support for async Promises (React Native Side) here, thanks @blankg for the code!
https://gist.github.com/EvDevNinja/f3979e00c5f0734297fae5ed79f850b8
Good job!!! It works perfect for me!
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.”
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
good
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..
This example is great! Really helped me.
One comment though.
I needed to change
to
to make it work correctly