How to Invoke Objective-C Method from JavaScript and Send Back Data to JavaScript in Ios

How to invoke Objective-C method from Javascript and send back data to Javascript in iOS?

There is an API to call JavaScript directly from Objective-C, but you cannot call Objective-C directly from Javascript.

How to tell your Objective-C code to do something from the Javascript in your WebView

You have to serialize your Javascript action into a special URL and intercept that URL in the UIWebView's delegate's shouldStartLoadWithRequest method.

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

There you can deserialize that special URL and interpret it to do what you want on the Objective-C side. (You should return NO in the above shouldStartLoadWithRequest method so the UIWebView doesn't use your bogus URL to actually make an HTTP request to load a webpage.)

How to Run Javascript Code from Objective-C

Then you can run Javascript from Objective-C by calling this method on your webview.

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

Example Code

I recommend using a bogus URL scheme so it will be easy to tell the difference between your action URLs and legit requests. You can make this request in the Javascript along these lines:

// JavaScript to send an action to your Objective-C code
var myAppName = 'myfakeappname';
var myActionType = 'myJavascriptActionType';
var myActionParameters = {}; // put extra info into a dict if you need it

// (separating the actionType from parameters makes it easier to parse in ObjC.)
var jsonString = (JSON.stringify(myActionParameters));
var escapedJsonParameters = escape(jsonString);
var url = myAppName + '://' + myActionType + "#" + escapedJsonParameters;
document.location.href = url;

Then in the UIWebView.delegate's shouldStartLoadWithRequest method, you can inspect the URL scheme and fragment to check if it's a normal request or one of your special actions. (The fragment of a URL is what comes after the #.)

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

// these need to match the values defined in your JavaScript
NSString *myAppScheme = @"myfakeappname";
NSString *myActionType = @"myJavascriptActionType";

// ignore legit webview requests so they load normally
if (![request.URL.scheme isEqualToString:myAppScheme]) {
return YES;
}

// get the action from the path
NSString *actionType = request.URL.host;
// deserialize the request JSON
NSString *jsonDictString = [request.URL.fragment stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];

// look at the actionType and do whatever you want here
if ([actionType isEqualToString:myActionType]) {
// do something in response to your javascript action
// if you used an action parameters dict, deserialize and inspect it here
}


// make sure to return NO so that your webview doesn't try to load your made-up URL
return NO;
}

(Read this answer if you need help deserializing your json string into an NSDictionary.)

Is it possible to call an Objective-C function that returns a value to javascript?

As you are expecting, passing data from obj C to JS use stringByEvaluatingJavaScriptFromString / passing data from JS to Obj C use JSContext

<!-- save as .html and load into UIWebview -->

<!DOCTYPE html>
<html>
<head>
<script>
function loadData(x){
alert(x);
}
function myMeg(){
passDatatoObjC("success");
}
</script>
</head>
</html>

function returnSomething(x){
loadData(x);
}

function passDatatoObjC(x){
passDatatoObjC(x);
}

#pragma mark - UIWebview dataource

-(void)webViewDidFinishLoad:(UIWebView *)webView{

[self.svgWebMapView stringByEvaluatingJavaScriptFromString:@"returnSomething(10)"];

JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
context[@"passDatatoObjC"] = ^(NSString *msg) {
NSLog(@"%@",msg);

});
}

Call Javascript function with parameters from iOS ObjC

You need to build a string containing the passed argument and use that as parameter for evaluateJavascript.
Like this:

-(void) callJavascriptFunc: (NSString*)val{
NSString* script = [NSString stringWithFormat:@"exampleFuncName('%@', '\(someConstant)')", val];
[self.webview evaluateJavascript:script completionHandler: nil];
}

Depending on where the value of the parameter comes from, however, you should make sure that no code injection is possible!

Call objective-c method from javascript

The most usual approach that I used to interact from javascript within obj-c is to changing hash. On required event in your js write
window.location.hash = '#cmd_alertMessage';
after this your - (BOOL)webView: shouldStartLoadWithRequest: navigationType: will be called:

   - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSArray * compenents = [request.URL.absoluteString componentsSeparatedByString:@"#"];
if (compenents.count > 1) {
NSString * cmd = compenents[1];
if ([cmd rangeOfString:@"cmd_alertMessage"].location != NSNotFound) {
// Call your obj-c method and get appropriated param
NSString * jsFunction = [NSString stringWithFormat:@"setParameterFromAAAMethod('%@')", [self aaa]];
// Return this param back to js
NSString * alertMessage = [webView stringByEvaluatingJavaScriptFromString:jsFunction];
}
}


return YES;
}

- (NSString *)aaa
{
return @"This is special parameter that you need";
}

So, it will work in 3 steps:

  1. Invoke hash changing to request parameter from obj-c code in
    js
    ;
  2. Handle hash changed (parameter request) and return in back to
    js in obj-c;
  3. Handle recieved parameter in js again

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.

Send javascript function to objective-C using JavascriptCore

Please mark Tayschrenn's answer as correct. I don't know how he knew this or where it's documented, but this is what I figured out by trial and error:

- (void)newWithFunc: (JSValue*)func
{
[func callWithArguments:@[]]; // will invoke js func with no params
}

Declaring the parameter (id)func apparently causes the javascript-cocoa bridge to convert it to an NSDictionary (as you noticed), rendering it unusable as a callable JSValue.

Invoke method in objective c code from HTML code using UIWebView

To call method of Objective-C in JS:

the below url helps in doing that

How to invoke Objective C method from Javascript and send back data to Javascript in iOS?

There is no way of executing, we make workaround by a navigation to other page and during the navigation, a webview delegate will watch for prefix of the navigation and execute the method we specified.

sample.html

<html>
<head>
<script type='text/javascript'>
function getText()
{
return document.getElementById('txtinput').value;
}
function locationChange()
{
window.location = 'ios:webToNativeCall';
}
</script>
</head>
<body style='overflow-x:hidden;overflow-y:hidden;'>
<h2 style = "font-size:16px;" align='center'>Hi Welcome to Webpage</h2>
<br/>
<p align='center' style = "font-size:12px;">Please Click the button after entering the text</p>
<br/>
<center>
<input type='text' style='width:90px;height:30px;' id='txtinput'/>
</center>
<br/>
<center>
<input type='button' style='width:90px;height:30px;' onclick='locationChange()' value='click me'>
</center>
</body>
</html>

Objective-C code

when you click button in html page the below delegate will fired and navigation cancelled because we return NO and respective method is called

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if ([[[request URL] absoluteString] hasPrefix:@"ios:"]) {

// Call the given selector
[self performSelector:@selector(webToNativeCall)];
// Cancel the location change
return NO;
}
return YES;
}

- (void)webToNativeCall
{
NSString *returnvalue = [self.webviewForHtml stringByEvaluatingJavaScriptFromString:@"getText()"];

self.valueFromBrowser.text = [NSString stringWithFormat:@"From browser : %@", returnvalue ];
}


Related Topics



Leave a reply



Submit