Skip to content

Commit

Permalink
Remove the error parameter from all response callbacks. This change…
Browse files Browse the repository at this point in the history
… breaks API backwards compatibility!

Previously, responses were sent using either `response.respondWith(data)` or `response.respondWithError(error)`. Response handlers were then required to take two parameters:

`
function responseHandler(error, responseData) {
    if (error) { /* handle error */ }
	...
}
`

However, this approach causes confusion, as seen in GH issue marcuswestin#18. Instead, we do away with the notion of errors in responses and simply have response callback blocks with a single data parameter:

`responseCallback(data)`

and

`function responseHandler(responseData) { ... }`

To migrate from old ObjC code, simple replace all instances of `WVJBResponse* response` with `WVJBResponseCallback responseCallback`, and replace all instances of `response.respondWith(data)` with `responseCallback(data)`.

To migrate from old Javascript code, replace all instances of `response.respondWith(data)` with `response(data)`.
  • Loading branch information
marcuswestin committed Dec 9, 2012
1 parent 318e72a commit 4ab41bb
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 106 deletions.
27 changes: 18 additions & 9 deletions ExampleApp/ExampleApp.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<h1>WebViewJavascriptBridge Demo</h1>
<script>
window.onerror = function(err) {
alert('window.onerror: ' + err)
log('window.onerror: ' + err)
}
document.addEventListener('WebViewJavascriptBridgeReady', onBridgeReady, false)
function onBridgeReady(event) {
Expand All @@ -20,24 +20,33 @@ <h1>WebViewJavascriptBridge Demo</h1>
var log = document.getElementById('log')
var el = document.createElement('div')
el.className = 'logLine'
el.innerHTML = uniqueId++ + '. ' + message + (data ? ': ' + JSON.stringify(data) : '')
el.innerHTML = uniqueId++ + '. ' + message + (data ? ':<br/>' + JSON.stringify(data) : '')
if (log.children.length) { log.insertBefore(el, log.children[0]) }
else { log.appendChild(el) }
}
bridge.init(function(message) {
bridge.init(function(message, responseCallback) {
log('JS got a message', message)
var data = { 'Javascript Responds':'Wee!' }
log('JS responding with', data)
responseCallback(data)
})

bridge.registerHandler('testJavascriptHandler', function(data, response) {
log('JS handler testJavascriptHandler was called', data)
response.respondWith({ 'Javascript Says':'Right back atcha!' })
bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
log('ObjC called testJavascriptHandler with', data)
var responseData = { 'Javascript Says':'Right back atcha!' }
log('JS responding with', responseData)
responseCallback(responseData)
})

var button = document.getElementById('buttons').appendChild(document.createElement('button'))
button.innerHTML = 'Send message to ObjC'
button.ontouchstart = function(e) {
e.preventDefault()
bridge.send('Hello from JS button')
var data = 'Hello from JS button'
log('JS sending message', data)
bridge.send(data, function(responseData) {
log('JS got response', responseData)
})
}

document.body.appendChild(document.createElement('br'))
Expand All @@ -46,9 +55,9 @@ <h1>WebViewJavascriptBridge Demo</h1>
callbackButton.innerHTML = 'Fire testObjcCallback'
callbackButton.ontouchstart = function(e) {
e.preventDefault()
log("Calling handler testObjcCallback")
log('JS calling handler "testObjcCallback"')
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
log('Got response from testObjcCallback', response)
log('JS got response', response)
})
}
}
Expand Down
23 changes: 12 additions & 11 deletions ExampleApp/ExampleAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,18 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(

[WebViewJavascriptBridge enableLogging];

_bridge = [WebViewJavascriptBridge bridgeForWebView:webView handler:^(id data, WVJBResponse *response) {
_bridge = [WebViewJavascriptBridge bridgeForWebView:webView handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"ObjC received message from JS: %@", data);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"ObjC got message from Javascript:" message:data delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
responseCallback(@"Response for message from ObjC");
}];

[_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponse *response) {
[_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"testObjcCallback called: %@", data);
[response respondWith:@"Response from testObjcCallback"];
responseCallback(@"Response from testObjcCallback");
}];

[_bridge send:@"A string sent from ObjC before Webview has loaded." responseCallback:^(id error, id responseData) {
if (error) { return NSLog(@"Uh oh - I got an error: %@", error); }
NSLog(@"objc got response! %@ %@", error, responseData);
[_bridge send:@"A string sent from ObjC before Webview has loaded." responseCallback:^(id responseData) {
NSLog(@"objc got response! %@", responseData);
}];

[_bridge callHandler:@"testJavascriptHandler" data:[NSDictionary dictionaryWithObject:@"before ready" forKey:@"foo"]];
Expand Down Expand Up @@ -54,12 +52,15 @@ - (void)renderButtons:(UIWebView*)webView {
}

- (void)sendMessage:(id)sender {
[_bridge send:@"A string sent from ObjC to JS"];
[_bridge send:@"A string sent from ObjC to JS" responseCallback:^(id response) {
NSLog(@"sendMessage got response: %@", response);
}];
}

- (void)callHandler:(id)sender {
[_bridge callHandler:@"testJavascriptHandler" data:[NSDictionary dictionaryWithObject:@"Hi there, JS!" forKey:@"greetingFromObjC"] responseCallback:^(id error, id response) {
NSLog(@"testJavascriptHandler responded: %@ %@", error, response);
NSDictionary* data = [NSDictionary dictionaryWithObject:@"Hi there, JS!" forKey:@"greetingFromObjC"];
[_bridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) {
NSLog(@"testJavascriptHandler responded: %@", response);
}];
}

Expand Down
62 changes: 28 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,34 +36,32 @@ To use a WebViewJavascriptBridge in your own project:
3) Instantiate a UIWebView and a WebViewJavascriptBridge:

UIWebView* webView = [[UIWebView alloc] initWithFrame:self.window.bounds];
WebViewJavascriptBridge* bridge = [WebViewJavascriptBridge bridgeForWebView:webView handler:^(id data, WVJBResponse* response) {
WebViewJavascriptBridge* bridge = [WebViewJavascriptBridge bridgeForWebView:webView handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"Received message from javascript: %@", data);
[response respondWith:@"Right back atcha"];
// or [response respondWithError:]
responseCallback(@"Right back atcha");
}];

4) Go ahead and send some messages from ObjC to javascript:

[bridge send:@"Well hello there"];
[bridge send:[NSDictionary dictionaryWithObject:@"Foo" forKey:@"Bar"]];
[bridge send:@"Give me a response, will you?" responseCallback:^(id error, id responseData) {
NSLog(@"objc got its response! %@ %@", error, responseData);
[bridge send:@"Give me a response, will you?" responseCallback:^(id responseData) {
NSLog(@"ObjC got its response! %@ %@", responseData);
}];

4) Finally, set up the javascript side:

document.addEventListener('WebViewJavascriptBridgeReady', function onBridgeReady(event) {
var bridge = event.bridge
bridge.init(function(message, response) {
bridge.init(function(message, responseCallback) {
alert('Received message: ' + message)
if (response) {
response.respondWith("Right back atcha")
// or use response.respondWithError("Booh!")
if (responseCallback) {
responseCallback("Right back atcha")
}
})
bridge.send('Hello from the javascript')
bridge.send('Please respond to this', function responseCallback(error, responseData) {
console.log("javascript got its response", error, responseData)
bridge.send('Please respond to this', function responseCallback(responseData) {
console.log("Javascript got its response", responseData)
})
}, false)

Expand All @@ -77,20 +75,20 @@ API Reference

Create a javascript bridge for the given UIWebView.

The `WVJBResponse` will not be `nil` if the javascript expects a response.
The `WVJBResponseCallback` will not be `nil` if the javascript expects a response.

Optionally, pass in `webViewDelegate:(UIWebViewDelegate*)webViewDelegate` if you need to respond to the [UIWebView's lifecycle events](http://developer.apple.com/library/ios/documentation/uikit/reference/UIWebViewDelegate_Protocol/Reference/Reference.html).

Example:

[WebViewJavascriptBridge bridgeForWebView:webView handler:^(id data, WVJBResponse response) {
[WebViewJavascriptBridge bridgeForWebView:webView handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"Received message from javascript: %@", data);
if (response) {
[response respondWith:@"Right back atcha"];
if (responseCallback) {
responseCallback(@"Right back atcha");
}
}]

[WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:self handler:^(id data, WVJBResponse response) { /* ... */ }];
[WebViewJavascriptBridge bridgeForWebView:webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) { /* ... */ }];

##### `[bridge send:(id)data]`
##### `[bridge send:(id)data responseCallback:(WVJBResponseCallback)responseCallback]`
Expand All @@ -101,8 +99,7 @@ Example:

[bridge send:@"Hi"];
[bridge send:[NSDictionary dictionaryWithObject:@"Foo" forKey:@"Bar"]];
[bridge send:@"I expect a response!" responseCallback:^(id error, id responseData) {
if (error) { return NSLog(@"Uh oh, I got an error: %@", error); }
[bridge send:@"I expect a response!" responseCallback:^(id responseData) {
NSLog(@"Got response! %@", responseData);
}];

Expand All @@ -112,8 +109,8 @@ Register a handler called `handlerName`. The javascript can then call this handl

Example:

[bridge registerHandler:@"getScreenHeight" handler:^(id data, WVJBResponse response) {
[response respondWith:[NSNumber numberWithInt:[UIScreen mainScreen].bounds.size.height]];
[bridge registerHandler:@"getScreenHeight" handler:^(id data, WVJBResponseCallback responseCallback) {
responseCallback([NSNumber numberWithInt:[UIScreen mainScreen].bounds.size.height]);
}];

##### `[bridge callHandler:(NSString*)handlerName data:(id)data]`
Expand All @@ -124,8 +121,7 @@ Call the javascript handler called `handlerName`. Optionally expect a response b
Example:

[bridge callHandler:@"showAlert" data:@"Hi from ObjC to JS!"];
[bridge callHandler:@"getCurrentPageUrl" data:nil responseCallback:^(id error, id responseData) {
if (error) { return NSLog(@"Huston, we got a problem: %@", error); }
[bridge callHandler:@"getCurrentPageUrl" data:nil responseCallback:^(id responseData) {
NSLog(@"Current UIWebView page URL is: %@", responseData);
}];

Expand Down Expand Up @@ -153,37 +149,35 @@ The `response` object will be defined if if ObjC sent the message with a `WVJBRe

Example:

bridge.init(function(data, response) {
bridge.init(function(data, responseCallback) {
alert("Got data " + JSON.stringify(data))
if (response) {
response.respondWith("Right back atcha!")
// or, response.respondWithError("It went wrong!")
if (responseCallback) {
responseCallback("Right back atcha!")
}
})

##### `bridge.send("Hi there!")`
##### `bridge.send({ Foo:"Bar" })`
##### `bridge.send(data, function responseCallback(error, responseData) { ... })`
##### `bridge.send(data, function responseCallback(responseData) { ... })`

Send a message to ObjC. Optionally expect a response by giving a `responseCallback` function.

Example:

bridge.send("Hi there!")
bridge.send("Hi there!", function(error, responseData) {
if (error) { return alert("Uh oh, we got an error: "+error) }
alert("I got a response! "+JSON.stringify(data))
bridge.send("Hi there!", function(responseData) {
alert("I got a response! "+JSON.stringify(responseData))
})

##### `bridge.registerHandler("handlerName", function(error, responseData) { ... })`
##### `bridge.registerHandler("handlerName", function(responseData) { ... })`

Register a handler called `handlerName`. The ObjC can then call this handler with `[bridge callHandler:"handlerName" data:@"Foo"]` and `[bridge callHandler:"handlerName" data:@"Foo" responseCallback:^(id error, id responseData) { ... }]`
Register a handler called `handlerName`. The ObjC can then call this handler with `[bridge callHandler:"handlerName" data:@"Foo"]` and `[bridge callHandler:"handlerName" data:@"Foo" responseCallback:^(id responseData) { ... }]`

Example:

bridge.registerHandler("showAlert", function(data) { alert(data) })
bridge.registerHandler("getCurrentPageUrl", function(data, response) {
response.respondWith(document.location.toString())
bridge.registerHandler("getCurrentPageUrl", function(data, responseCallback) {
responseCallback(document.location.toString())
})


Expand Down
11 changes: 2 additions & 9 deletions WebViewJavascriptBridge/WebViewJavascriptBridge.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#import <UIKit/UIKit.h>

@class WVJBResponse;
typedef void (^WVJBResponseCallback)(id error, id responseData);
typedef void (^WVJBHandler)(id data, WVJBResponse* response);
typedef void (^WVJBResponseCallback)(id responseData);
typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback);

@interface WebViewJavascriptBridge : NSObject <UIWebViewDelegate>
+ (id)bridgeForWebView:(UIWebView*)webView handler:(WVJBHandler)handler;
Expand All @@ -16,9 +15,3 @@ typedef void (^WVJBHandler)(id data, WVJBResponse* response);
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
- (void)reset;
@end

@interface WVJBResponse : NSObject
- (WVJBResponse*) initWithCallbackId:(NSString*)callbackId bridge:(WebViewJavascriptBridge*)bridge;
- (void) respondWith:(id)responseData;
- (void) respondWithError:(id)error;
@end
19 changes: 8 additions & 11 deletions WebViewJavascriptBridge/WebViewJavascriptBridge.js.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,14 @@

if (message.responseId) {
var responseCallback = responseCallbacks[message.responseId]
responseCallback(message.error, message.responseData)
responseCallback(message.responseData)
delete responseCallbacks[message.responseId]
} else {
var response
var responseCallback
if (message.callbackId) {
var callbackResponseId = message.callbackId
response = {
respondWith: function(responseData) {
_doSend({ responseId:callbackResponseId, responseData:responseData })
},
respondWithError: function(error) {
_doSend({ responseId:callbackResponseId, error:error })
}
responseCallback = function(responseData) {
_doSend({ responseId:callbackResponseId, responseData:responseData })
}
}

Expand All @@ -85,9 +80,11 @@
}

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

0 comments on commit 4ab41bb

Please sign in to comment.