Reputation: 1702
I've been using WebViewJavascriptBridge to bridge Objective-C and JavaScript in an iOS application. It works great but it lacks support for returning values to the calling JavaScript code from the called native Objective-C function.
I'm pretty sure that Cordova (PhoneGap) does that in some way with callbacks but I've been having a hard time trying to understand how the underlying mechanics work.
Is there anyone who's had the same problem and that managed to find a solution?
Upvotes: 3
Views: 3902
Reputation: 31
WebViewJavascriptBridge
have no maintain for a long time.
Maybe you should change to use this library.
self.bridge.consolePipeBlock = ^(id _Nonnull water) {
NSLog(@"Next line is javascript console.log------>>>>");
NSLog(@"%@",water);
};
This can easy to get javascript console.log.
Also have Swift language Version.
bridge.consolePipeClosure = { water in
guard let jsConsoleLog = water else {
print("Javascript console.log give native is nil!")
return
}
print("Next line is Javascript console.log----->>>>>>>")
print(jsConsoleLog)
}
Also have h5 demo for your partner.
Upvotes: 0
Reputation: 7698
If you mean, "return" in the sense that your JS caller will see the results as returned from the call, I don't know how to do it. I suspect it would take a level of thread manipulation not available in JS. Instead, you can recode your JS side logic to create a result handler function. In pseudo code:
res = CallObjC(args);
work with res...
Becomes
CallObjC(args, function(res) { work with res...});
Admittedly a bit awkward but get used to it - it is a very common pattern. Internally, the JS bridge code creates a mapping of the request to the callback function. When the ObjC code has the result, it uses WebView's stringByEvaluatingJavaScriptFromString to call the JS bridge code that locates and invokes the callback.
@Richard - Be a bit careful with the solution you posted. Setting window.location to invoke shouldStartLoadWithRequest can result in both lost functionality in the webview and also lost messages to ObjectiveC. Current practice is to use an iframe and have some kind of message queue that can buffer up messages.
Upvotes: 2
Reputation: 55553
Now, I've never used WebViewJavascriptBridge, but I have done this in objective-c using a simple delegate, so maybe this will help you:
MyScript.js
// requestFromObjc
// functionName (required):
// the name of the function to pass along to objc
// returnedResult (not used by caller):
// the result given by objc to be passed along
// callback (not used by caller):
// sent by objc, name of the function to execute
function requestFromObjc(functionName, objcResult, callback)
{
if (!objcResult)
{
window.location = "myapp://objcRequest?function=" + functionName + "&callback=" + arguments.callee.name + "&callbackFunc=" + arguments.callee.caller.name;
}
else
{
window[callback](objcResult);
}
}
function buttonClick(objcResult)
{
if (!objcResult)
{
// ask for the color from objc
requestFromObjc("buttonColor&someParam=1");
}
else
{
// do something with result (in this case, set the background color of my div)
var div = document.getElementById("someDiv");
div.style.background = objcResult;
}
}
MyPage.html
<html>
<head>
<script type="text/javascript" src="MyScript.js"></script>
</head>
<body>
<div id="someDiv">
This is my div! Do not make fun of it, though.
</div>
<button onClick="buttonClick(undefined)">
Click Me!
</button>
</body>
</html>
ViewController.m
-(NSString *) javaScriptResultForRequest:(NSDictionary *) params
{
if ([params[@"function"] isEqualToString:@"buttonColor"])
{
NSArray *colors = @[ @"red", @"yellow", @"blue", @"green", @"purple" ];
return colors[arc4random_uniform(colors.count)];
}
else
{
return @"undefined";
}
}
-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if ([[[request URL] scheme] isEqualToString:@"myapp"])
{
// parse the URL here
NSURL *URL = [request URL];
if ([URL.host isEqualToString:@"objcRequest"])
{
NSMutableDictionary *queryParams = [NSMutableDictionary dictionary];
NSArray *split = [URL.query componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"&="]];
for (int i = 0; i < split.count; i += 2)
{
queryParams[split[i]] = split[i + 1];
}
NSString *result = [self javaScriptResultForRequest:queryParams];
NSString *jsRequest = [NSString stringWithFormat:@"%@(\"%@\", \"%@\", \"%@\")", queryParams[@"callback"], queryParams[@"function"], result, queryParams[@"callbackFunc"]];
// now we send this to the target
[self.webView stringByEvaluatingJavaScriptFromString:jsRequest];
return NO;
}
}
return YES;
}
Obviously this is much slower than trying to do the equivalent function in pure JS, because of all the loops it has to jump through. However, if there is something you need to use in your JS code that's in your ObjC code already, this may be for you.
Upvotes: 4