JavaScript in Uiwebview Callback to C/Objective-C

Javascript in UIWebView callback to C/Objective-C

Update - don't use UIWebView anymore. Use WKWebView, or better yet (if it fits your needs and you're building for iOS 9), a Safari View Controller.

But if you must use UIWebView, in your UIWebView delegate, provide an implementation for webView:shouldStartLoadWithRequest:navigationType:

In your HTML or Javascript files, add functions that send URLs to a custom scheme (for readability purposes, the custom scheme isn't required). All the URLs sent will be passed to your Objective-C method implementation, and then you can do what you'd like.

Trigger objective C method from javascript using JavaScriptCore in iOS 7 in ViewControllers

The Objective-C interface in the JavascriptCore framework introduced with iOS 7 permits calling Objective-C methods from Javascript. For a great intro to these features, check out the 2013 WWDC introduction "Integrating JavaScript into Native Apps" session on Apple's developer network: https://developer.apple.com/videos/wwdc/2013/?id=615

It does have a brief section towards the end on WebView (MacOs only not iOS)

The sample code below shows how to implement what you want for iOS.

- (void)viewDidLoad
{
[super viewDidLoad];

UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0,40,320,320)];
webView.delegate = self;
[self.view addSubview:webView];
NSString *pageSource = @"<!DOCTYPE html> <html> <head> </head> <body> <h1>My Mobile App</h1> <p>Please enter the Details</p> <form name=\"feedback\" method=\"post\" action=\"mailto:you@site.com\"> <!-- Form elements will go in here --> </form> <form name=\"inputform\"> <input type=\"button\" onClick=\"submitButton('My Test Parameter')\" value=\"submit\"> </form> </body> </html>";
[webView loadHTMLString:pageSource baseURL:nil];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; // Undocumented access
context[@"submitButton"] = ^(NSString *param1) {
[self yourObjectiveCMethod:param1];
};
}

- (void)yourObjectiveCMethod:(NSString *)param1 {
NSLog(@"User clicked submit. param1=%@", param1);
}

Things to note:

  • Accessing the JSContext of a UIWebView is undocumented. Two techniques are known (see Access the JavaScriptCore engine of a UIWebView). The first technique is used here.
  • You don't need to define the javascript function "submitButton" inside the page, you will define the function from Objective-C using the webView's JSContext
  • A page load in a webview causes its JSContext to get replaced, hence you should implement a delegate for the UIWebView and define your Objective-C callback in your implementation of the selector for -webViewDidFinishLoad:
  • I've assumed you will want to pass parameters to this callback, so I've shown a sample parameter. Although this is not covered in the video tutorial mentioned above (or the PDF equivalent), looking at JSValue.h shows that JavascriptCore offers built-in conversion between the following Objective-C and Javascript types:

.

  Objective-C type  |   JavaScript type
--------------------+---------------------
nil | undefined
NSNull | null
NSString | string
NSNumber | number, boolean
NSDictionary | Object object
NSArray | Array object
NSDate | Date object
NSBlock * | Function object *
id ** | Wrapper object **
Class *** | Constructor object ***

* Instances of NSBlock with supported arguments types will be presented to
JavaScript as a callable Function object. For more information on supported
argument types see JSExport.h. If a JavaScript Function originating from an
Objective-C block is converted back to an Objective-C object the block will
be returned. All other JavaScript functions will be converted in the same
manner as a JavaScript object of type Object.

** For Objective-C instances that do not derive from the set of types listed
above, a wrapper object to provide a retaining handle to the Objective-C
instance from JavaScript. For more information on these wrapper objects, see
JSExport.h. When a JavaScript wrapper object is converted back to Objective-C
the Objective-C instance being retained by the wrapper is returned.

*** For Objective-C Class objects a constructor object containing exported
class methods will be returned. See JSExport.h for more information on
constructor objects.

iOS UIWebView Javascript - insert data -receive callbacks?

You can call a javascript method and pass the value by using this

NSString *selectedValue = [webViewInstance stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"getAndReturnRadioValue(%d)", questionCounterIntValue]];

But to trigger back from javascript you need to use a workaround method,

Implement the following webViewDelegate method

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{

NSLog(@"passed data from web view : %@",[[request URL] query]);

if([[[request URL] query] isEqualToString:@"clickedOnLineNo"])
{
//Do your action here
}
}

You have to navigate to a fake URL from your javascript method like,

window.location = "someLink://yourApp/form_Submitted:param1:param2:param3";

on a button click or your required action.

You could take a look at my answer for this question
Submit a form in UIWebView

send a notification from javascript in UIWebView to ObjectiveC

There seems to be no official method of doing this. However, the standard workaround involves reading and parsing incoming URL requests, basically rolling your own serialized messaging protocol. The message handling should be done in the webView:shouldStartLoadWithRequest:navigationType method of your view controller.

Note: there are several free libraries (PhoneGap, QuickConnect, JS-to-Cocoa Bridge) which wrap this functionality (plus do a whole lot more). To reinvent the wheel (or know why it's round, so to speak), read on.

From JavaScript, you will invoke the callback by attempting to navigate to a new URL:

// In JavaScript
window.location = 'myapp:myaction:param1:param2'; // etc...

In Objective-C, implement the UIWebViewDelegate protocol in your .h file:

// In your header file
@interface MyAppViewController : UIViewController <UIWebViewDelegate> {
...
}
@end

Next, implement the method in your .m file:

// In your implementation file
-(BOOL)webView:(UIWebView *)webView2
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
// Break apart request URL
NSString *requestString = [[request URL] absoluteString];
NSArray *components = [requestString componentsSeparatedByString:@":"];

// Check for your protocol
if ([components count] > 1 &&
[(NSString *)[components objectAtIndex:0] isEqualToString:@"myapp"])
{
// Look for specific actions
if ([(NSString *)[components objectAtIndex:1] isEqualToString:@"myaction"])
{
// Your parameters can be found at
// [components objectAtIndex:n]
// where 'n' is the ordinal position of the colon-delimited parameter
}

// Return 'NO' to prevent navigation
return NO;
}

// Return 'YES', navigate to requested URL as normal
return YES;
}

Two important notes:

  1. Context: navigating to myapp:whatever will (of course) fail under any other context. Keep this in mind if you're loading cross-platform pages.

  2. Timing: if a second window.location = call is made before the first returns, it will get 'lost.' So, either lump your calls together, manually delay execution, or implement a queue which combines the above with JS queries into Objective-C objects.

How javascript in WebView/UIWebView and object-c object comunicate with each other?

See my answer here: NSString in UIWebview

How to call JavaScript Function in objective C

NSString *path;
NSBundle *thisBundle = [NSBundle mainBundle];
path = [thisBundle pathForResource:@"first" ofType:@"html"];
NSURL *instructionsURL = [NSURL fileURLWithPath:path];
[webView loadRequest:[NSURLRequest requestWithURL:instructionsURL]];

NSString * jsCallBack = [NSString stringWithFormat:@"myFunction()"];
[webView stringByEvaluatingJavaScriptFromString:jsCallBack];

[webView stringByEvaluatingJavaScriptFromString:@"myFunction()"];

If all this code called from single place, than it won't work, because page load is asynchronous and page with JavaScript code is not ready at that moment.
Try to call this methods [webView stringByEvaluatingJavaScriptFromString:@"myFunction()"]; in UIWebViewDelegate callback method –webViewDidFinishLoad:

Can you call a javascript function from native code (not in a callback) using PhoneGap and iOS?

Found the PhoneGap helper to accomplish this... Write javascript to the webView using:

    [super writeJavascript:@"alert('it works');"];

communication from javascript to objective-C

I used webviewjavascriptbridge to communicate javascript and objective C code.

In this example code, note the global variable of bridge.

<!doctype html>
<html><head>
<style type='text/css'>
html { font-family:Helvetica; color:#222; }
h1 { color:steelblue; font-size:24px; margin-top:24px; }
button { margin:0 3px 10px; font-size:12px; }
.logLine { border-bottom:1px solid #ccc; padding:4px 2px; font-family:courier; font-size:11px; }
</style>
</head><body>
<h1>WebViewJavascriptBridge Demo</h1>
<script>
window.onerror = function(err) {
alert('window.onerror: ' + err)
}
var bridge;
document.addEventListener('WebViewJavascriptBridgeReady', onBridgeReady, false)
function onBridgeReady(event){
alert("Onbridge ready call");

bridge = event.bridge
var uniqueId = 1
function log(message, data) {
var log = document.getElementById('log')
var el = document.createElement('div')
el.className = 'logLine'
el.innerHTML = uniqueId++ + '. ' + message + (data ? ': ' + JSON.stringify(data) : '')
if (log.children.length) { log.insertBefore(el, log.children[0]) }
else { log.appendChild(el) }
}
bridge.init(function(message) {
log('JS got a message', message)
})

bridge.registerHandler('open', function(data, response) {
log('JS handler testJavascriptHandler was called', data)
response.respondWith({ 'Javascript Says':'open open open!' })
})

bridge.registerHandler('testJavascriptHandler', function(data, response) {
log('JS handler testJavascriptHandler was called', data)
response.respondWith({ 'Javascript Says':'Right back atcha!' })
})

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')
}

document.body.appendChild(document.createElement('br'))

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)
})
}

}

function open(url, offset,e)
{
alert(bridge);

//alert("Open Webview with url:Yes Got it");

// alert(document.getElementById(offset).href);
// var bridge = event.bridge;
// alert(bridge);

window.location = url+'?offset='+offset//'myapp:myaction:url:offset'

//requestFromObjc("buttonColor&someParam=1");
}

function close()
{
alert("Open Webview with url:"+ url + " & Offset: " + offset);
}

function requestFromObjc(functionName, objcResult, callback)
{
if (!objcResult)
{
window.location = 'myapp:myaction:param1:param2'
// window.location = "myapp://objcRequest?function=" + functionName + "&callback=" + arguments.callee.name + "&callbackFunc=" + arguments.callee.caller.name;
}
else
{
window[callback](objcResult);
}
}

</script>
<div id='buttons'></div> <div id='log'></div>

<body>
<a id="55" href="javascript:open('http://www.tcm.com', '55',this)">Open</a><br>
<a href="javascript:close()">Close</a>
</body>

</body></html>

Reference: https://github.com/marcuswestin/WebViewJavascriptBridge



Related Topics



Leave a reply



Submit