marcuswestin / WebViewJavascriptBridge

An iOS/OSX bridge for sending messages between Obj-C and JavaScript in UIWebViews/WebViews
http://marcuswest.in
MIT License
14.29k stars 2.98k forks source link

Return values for native functions called from JS #18

Closed tanis2000 closed 11 years ago

tanis2000 commented 12 years ago

First of all, thanks for this great library! It works great :)

I was wondering if you've got any idea about how to implement returning values to JS from native calls. I've not been able to come up with a good solution so far, but it'd be awesome to be able to calla a native Obj-C function and get a return value back.

marcuswestin commented 12 years ago

Hi tanis,

Thanks for the encouragement!

There is, and I have implemented that on top of WWJB in projects where I use it. But it does make sense to make it part of WWJB directly. I can't promise I'll get to it soon, but the gist of it is that you associate a unique ID with each request and store a callback function/block indexed by that ID. If such a callback ID is present, then the ObjC/Javascript should provide a callback function which, when called, sends a message in response with the unique callback ID in it.

It sounds convoluted but is straight forward. I hope to have it implemented for us all in the foreseeable future.

On Wed, Sep 12, 2012 at 1:41 AM, tanis2000 notifications@github.com wrote:

First of all, thanks for this great library! It works great :)

I was wondering if you've got any idea about how to implement returning values to JS from native calls. I've not been able to come up with a good solution so far, but it'd be awesome to be able to calla a native Obj-C function and get a return value back.

— Reply to this email directly or view it on GitHubhttps://github.com/marcuswestin/WebViewJavascriptBridge/issues/18.

marcuswestin commented 12 years ago

@tanis2000, I took the time to actually make this happen. Enough people have asked for it and will benefit from a standardized solution that it's worthwhile.

Please see the implementation and example app in v2 branch: https://github.com/marcuswestin/WebViewJavascriptBridge/tree/v2

Note: The readme file and documentation has not been updated, but the example app runs as expected.

Cheers, Marcus

tanis2000 commented 12 years ago

Thanks for the effort Marcus! I'm going to clone that branch and try it out. I'll let you know if I find any issues. Thanks again!

Valerio

tanis2000 commented 12 years ago

@marcuswestin, I noticed something weird.. if you register a handler like this:

[jsBridge registerHandler:@"getCurrentBoxData" callback:^(id data, WVJBCallback callback){
    NSLog(@"testObjcCallback called: %@", data);
    NSDictionary *d2 = [box jsData];
    if (callback != nil)
        callback(d2);
}];

And the [box jsData method is like this:

-(NSDictionary *)jsData
{
    return data;
}

What happens is that when I call that handler from JavaScript, the application crashes insider the _flushMessages function. I tried debugging that and the result is that the real crash is in this call:

handler([message objectForKey:@"data"], responseCallback);

I don't understand why this happens.. if, for example, I create that d2 dictionary by alloc+init, it works fine. It's just like the call to [box jsData] is bad.. could it be a threading issue? The weird thing is that setting a breakpoint on NSLog inside the callback of my registered handler does not trigger when using [box jsData], while it triggers if I substitute that with d2 = [[NDictionary alloc] init]

tanis2000 commented 12 years ago

Here's a gist demonstrating the issue live (replace your ExampleAppDelegate.m with this snippet): https://gist.github.com/3714377

tanis2000 commented 12 years ago

I found that with ARC enabled I can solve the problem with this kind of workaround:

int i = [self test2];

void (^block) (id, WVJBCallback);
block = ^(id data, WVJBCallback callback) {
    NSLog(@"%d", i);
};

[self.javascriptBridge registerHandler:@"testObjcCallback" callback:block];

But it does not work without ARC (like in my project). I've still got to find a workaround :-P

marcuswestin commented 12 years ago

Hi tanis,

I hope to get a chance to look at this today, but can't promise.

My gut says the issue is in how I'm using blocks. If you have the time to read up on blocks (especially how they manage memory) then that may be worthwhile.

Keep in mind that while the branch has the features you want, it's only a day old and hasn't been battle tested yet.

Cheers, Marcus

-- while mobile

On Sep 13, 2012, at 8:22 AM, tanis2000 notifications@github.com wrote:

I found that with ARC enabled I can solve the problem with this kind of workaround:

int i = [self test2];

void (^block) (id, WVJBCallback); block = ^(id data, WVJBCallback callback) { NSLog(@"%d", i); };

[self.javascriptBridge registerHandler:@"testObjcCallback" callback:block]; But it does not work without ARC (like in my project). I've still got to find a workaround :-P

— Reply to this email directly or view it on GitHub.

meadlai commented 12 years ago

if I use self, it occur problem. how to visit "self" in block? or may I replace the block with a delegate?thanks,

[_jsb registerHandler:@"mead_action" handler:^(id data, WVJBResponseCallback responseCallback) { NSLog(@"mead_action called: %@", data); CommonWebViewController* wvc = [[CommonWebViewController alloc]init]; wvc.path = [data objectForKey:@"path"]; wvc.fileName = [data objectForKey:@"fileName"]; wvc.arg = [data objectForKey:@"arg"]; wvc.navigationItem.title = [data objectForKey:@"viewTitle"]; //if I use self, it occur problem. how to visit "self" in block? or may I replace the block with a delegate?thanks, [self.navigationController pushViewController:wvc animated:YES]; responseCallback(@"Response:OK"); }];

marcuswestin commented 12 years ago

Hi @meadlai, see http://stackoverflow.com/questions/5023566/objective-c-calling-self-methodname-from-inside-a-block for a solution. You can also google "ObjC use self inside block". I suggest you read up on blocks, they are a little quirky but very powerful.

I hope that helps! Cheers, Marcus

meadlai commented 12 years ago

@marcuswestin thank you very much. it's a little difficult for me, as a fresher. if there is a demo, would be better. thank you.

Mead

tanis2000 commented 12 years ago

@marcuswestin in the end I went for the path of setting up WVJB as a static library using ARC and include that library in my non-ARC project. All I had to take care of was to retain the object returned by javascriptBridgeForWebView and the blocks I made in my own code. That solved everything.

Now my next step is to try and port this library to Android as I need it on that platform too ;)

marcuswestin commented 12 years ago

Fantastic! Thanks for keeping me posted.

On android you should have a significantly easier time, as there is a direct interface between java and javascript. When you have something working, will you let me know? I'll want to link to it from wvjb.

-- while mobile

On Sep 25, 2012, at 8:00 AM, tanis2000 notifications@github.com wrote:

@marcuswestin in the end I went for the path of setting up WVJB as a static library using ARC and include that library in my non-ARC project. All I had to take care of was to retain the object returned by javascriptBridgeForWebView and the blocks I made in my own code. That solved everything.

Now my next step is to try and port this library to Android as I need it on that platform too ;)

— Reply to this email directly or view it on GitHub.

tanis2000 commented 12 years ago

Of course I'll keep you posted. I'll try to get something working and upload it to my fork as soon as there's something you can try out :)

marcuswestin commented 12 years ago

Hey,

I've started a "WebViewJavascriptBridge in the wild" list - do you use it in production? If yes, mind if I list your company/project in the README?

Cheers!

tanis2000 commented 12 years ago

Hi Marcus, sorry for the late reply. You can add Altralogica s.r.l. ( http://www.altralogica.it ) to the company list. I can't tell you which project it is right now as it's for a client of ours and we can't disclose that information yet. But I'll let you know as soon as the app is released to the wild.

marcuswestin commented 12 years ago

Great, thanks! I just added Altralogica to the list of companies.

Cheers! Marcus

On Tue, Nov 13, 2012 at 11:30 PM, tanis2000 notifications@github.comwrote:

Hi Marcus, sorry for the late reply. You can add Altralogica s.r.l. ( http://www.altralogica.it ) to the company list. I can't tell you which project it is right now as it's for a client of ours and we can't disclose that information yet. But I'll let you know as soon as the app is released to the wild.

— Reply to this email directly or view it on GitHubhttps://github.com/marcuswestin/WebViewJavascriptBridge/issues/18#issuecomment-10357394.

KarthickR commented 11 years ago

https://github.com/marcuswestin/WebViewJavascriptBridge/tree/v2 link is not working.Please help me

KarthickR commented 11 years ago

Please some one help me,Following is the code, which i am using to make two way communication and to return the objects from objc to js but i could not get it working

viewcontroller.m

my js file

function ChooseContactTapped(){ bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) { alert('inside testObjcCallback') }) }

//my html file in my html file, i have just created a button

when ChooseContactTapped method is called, i could get the method inside viewcontroller.m to be called but i could not get back the response my alert inside the ChooseContactTapped method is not displayed.Please correct me i am doing wrong.

muralikrish commented 11 years ago

i am runnig the example code provided along with the library,

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

var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button')) callbackButton.innerHTML = 'Fire testObjcCallback' callbackButton.ontouchstart = function(e) { e.preventDefault() log("Calling handler testObjcCallback") bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) { log('Got response from testObjcCallback', response) alert(response) }) }

In the above code i am getting response as undefined, but inside the Webviewjavascriptbridge.m file. i could see the response printed correctly.Please help me to solve this issue

marcuswestin commented 11 years ago

Hi guys,

I've just returned from my wedding celebrations and will be available to help tomorrow. In the meantime I ask for your patience or that you dig in to the source code to figure out that's wrong if anything.

Cheers! Marcus

-- while mobile

On Dec 6, 2012, at 9:37 AM, KarthickR notifications@github.com wrote:

Please some one help me,Following is the code, which i am using to make two way communication and to return the objects from objc to js but i could not get it working

viewcontroller.m

(void)viewDidLoad { [super viewDidLoad]; [WebViewJavascriptBridge enableLogging]; _bridge = [WebViewJavascriptBridge bridgeForWebView:_webView handler:^(id data, WVJBResponse *response) { NSLog(@"ObjC received message from JS: %@", data); }];

NSString path=[[NSBundle mainBundle]pathForResource:@"index" ofType:@"html"]; NSURLRequest request=[NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]]; [_webView loadRequest:request];

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

my js file

function ChooseContactTapped(){ bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) { alert('inside testObjcCallback') }) }

my html file

<!doctype html>

when ChooseContactTapped method is called, i could get the method inside viewcontroller.m to be called but i could not get back the response my alert inside the ChooseContactTapped method is not displayed.Please correct me i am doing wrong.

— Reply to this email directly or view it on GitHub.

KarthickR commented 11 years ago

Hi marcus,

Thank you very much for your reply, i have dig in to the source code. Since i dont have much experience in programming, i could not understand the code. I will be waiting for your help,once again thanks a lot.

marcuswestin commented 11 years ago

@KarthickR, @muralikrish Hey, I'm back. A couple of things: