Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ WebViewJavascriptBridge is used by a range of companies and projects. This list
- Dojo4's [Imbed](http://dojo4.github.io/imbed/)
- [CareZone](https://carezone.com)
- [Hemlig](http://www.hemlig.co)
- [BrowZine](http://thirdiron.com/browzine/)

Setup & Examples (iOS & OSX)
----------------------------
Expand Down Expand Up @@ -150,6 +151,15 @@ Example:
NSLog(@"Current UIWebView page URL is: %@", responseData);
}];

##### `[bridge disableJavscriptAlertBoxSafetyTimeout:(BOOL)isDisabled]`

Disable the use of setTimeout when sending a message across the bridge. It is only safe to disable this timeout if you do not call any of the javascript popup box functions (alert, confirm, and prompt). If you call one of these functions from the bridged javascript code, the app will hang. When this setTimeout call is not used, there can be a significant decrease in the amount of time it takes to send and receive messages across the bridge.

Example:

[self.bridge disableJavscriptAlertBoxSafetyTimeout:YES];


#### Custom bundle
`WebViewJavascriptBridge` requires `WebViewJavascriptBridge.js.txt` file that is injected into web view to create a bridge on JS side. Standard implementation uses `mainBundle` to search for this file. If you e.g. build a static library and you have that file placed somewhere else you can use this method to specify which bundle should be searched for `WebViewJavascriptBridge.js.txt` file:

Expand Down Expand Up @@ -181,12 +191,14 @@ Example:
// Start using the bridge
}, false)

##### `bridge.init(function messageHandler(data, response) { ... })`
##### `bridge.init(function messageHandler(data, response) { ... }, disableAlertSafety)`

Initialize the bridge. This should be called inside of the `'WebViewJavascriptBridgeReady'` event handler.

The `messageHandler` function will receive all messages sent from ObjC via `[bridge send:(id)data]` and `[bridge send:(id)data responseCallback:(WVJBResponseCallback)responseCallback]`.

The `disableAlertSafety` parameter has the same effect as calling `[bridge disableJavscriptAlertBoxSafetyTimeout:(BOOL)isDisabled]`.

The `response` object will be defined if if ObjC sent the message with a `WVJBResponseCallback` block.

Example:
Expand Down
1 change: 1 addition & 0 deletions WebViewJavascriptBridge/WebViewJavascriptBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@ typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback);
- (void)callHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName data:(id)data;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
- (void)disableJavscriptAlertBoxSafetyTimeout:(BOOL)isDisabled;

@end
78 changes: 49 additions & 29 deletions WebViewJavascriptBridge/WebViewJavascriptBridge.js.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

var responseCallbacks = {}
var uniqueId = 1

var disableDispatchTimeout = false

function _createQueueReadyIframe(doc) {
messagingIframe = doc.createElement('iframe')
Expand All @@ -18,9 +20,10 @@
doc.documentElement.appendChild(messagingIframe)
}

function init(messageHandler) {
function init(messageHandler, disableAlertSafety) {
if (WebViewJavascriptBridge._messageHandler) { throw new Error('WebViewJavascriptBridge.init called twice') }
WebViewJavascriptBridge._messageHandler = messageHandler
_disableJavascriptAlertBoxSafetyTimeout(disableAlertSafety)
var receivedMessages = receiveMessageQueue
receiveMessageQueue = null
for (var i=0; i<receivedMessages.length; i++) {
Expand All @@ -40,11 +43,18 @@
_doSend({ handlerName:handlerName, data:data }, responseCallback)
}

function _disableJavascriptAlertBoxSafetyTimeout(safetyDisabled) {
if (safetyDisabled !== undefined) {
disableDispatchTimeout = safetyDisabled
}
}

function _doSend(message, responseCallback) {
if (responseCallback) {
var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime()
responseCallbacks[callbackId] = responseCallback
message['callbackId'] = callbackId
message['startTime'] = Date.now()
}
sendMessageQueue.push(message)
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE
Expand All @@ -57,38 +67,47 @@
}

function _dispatchMessageFromObjC(messageJSON) {
setTimeout(function _timeoutDispatchMessageFromObjC() {
var message = JSON.parse(messageJSON)
var messageHandler
var responseCallback
if (disableDispatchTimeout) {
_executeMessageFromObjC(messageJSON)
}
else {
setTimeout(function _timeoutDispatchMessageFromObjC() {
_executeMessageFromObjC(messageJSON)
}, 0)
}
}

if (message.responseId) {
responseCallback = responseCallbacks[message.responseId]
if (!responseCallback) { return; }
responseCallback(message.responseData)
delete responseCallbacks[message.responseId]
} else {
if (message.callbackId) {
var callbackResponseId = message.callbackId
responseCallback = function(responseData) {
_doSend({ responseId:callbackResponseId, responseData:responseData })
}
}

var handler = WebViewJavascriptBridge._messageHandler
if (message.handlerName) {
handler = messageHandlers[message.handlerName]
function _executeMessageFromObjC(messageJSON) {
var message = JSON.parse(messageJSON)
var messageHandler
var responseCallback

if (message.responseId) {
responseCallback = responseCallbacks[message.responseId]
if (!responseCallback) { return; }
responseCallback(message.responseData)
delete responseCallbacks[message.responseId]
} else {
if (message.callbackId) {
var callbackResponseId = message.callbackId
responseCallback = function(responseData) {
_doSend({ responseId:callbackResponseId, responseData:responseData })
}

try {
handler(message.data, responseCallback)
} catch(exception) {
if (typeof console != 'undefined') {
console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception)
}
}

var handler = WebViewJavascriptBridge._messageHandler
if (message.handlerName) {
handler = messageHandlers[message.handlerName]
}

try {
handler(message.data, responseCallback)
} catch(exception) {
if (typeof console != 'undefined') {
console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception)
}
}
})
}
}

function _handleMessageFromObjC(messageJSON) {
Expand All @@ -104,6 +123,7 @@
send: send,
registerHandler: registerHandler,
callHandler: callHandler,
_disableJavascriptAlertBoxSafetyTimeout: _disableJavascriptAlertBoxSafetyTimeout,
_fetchQueue: _fetchQueue,
_handleMessageFromObjC: _handleMessageFromObjC
}
Expand Down
13 changes: 13 additions & 0 deletions WebViewJavascriptBridge/WebViewJavascriptBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,19 @@ - (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
_messageHandlers[handlerName] = [handler copy];
}

- (void)disableJavscriptAlertBoxSafetyTimeout:(BOOL)isDisabled {
NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._disableJavscriptAlertBoxSafetyTimeout(%@);", isDisabled ? @"true" : @"false"];

if ([[NSThread currentThread] isMainThread]) {
[_webView stringByEvaluatingJavaScriptFromString:javascriptCommand];
} else {
__strong WVJB_WEBVIEW_TYPE* strongWebView = _webView;
dispatch_sync(dispatch_get_main_queue(), ^{
[strongWebView stringByEvaluatingJavaScriptFromString:javascriptCommand];
});
}
}

/* Platform agnostic internals
*****************************/

Expand Down