In iOS, how can I call an Objective-C method from Javascript in a UIWebView
and have it send data back to the Javascript? I know that this could be done on OS X using the Webkit library, but is this possible on iOS? How does PhoneGap achieve this?

- 5,753
- 72
- 57
- 129

- 12,099
- 23
- 89
- 155
3 Answers
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.)
-
1Great example. And from PHP you can encode like this: `$fragment = array("key"=>"val"); $url = 'myApp://actionType#'.rawurlencode(json_encode($fragment));` – William Entriken Aug 26 '12 at 15:00
-
Should NSString *actionType = request.URL.path; be NSString *actionType = request.URL.host; ? – Paweł Brewczynski Feb 26 '14 at 14:36
-
I mean the in "myScheme://myAction#jsonEscapedParameter" URL.host should return *myAction* part. Look at the documentation https://developer.apple.com/library/ios/documentation/cocoa/reference/foundation/classes/NSURL_Class/Reference/Reference.html#//apple_ref/occ/instm/NSURL/host – Paweł Brewczynski Feb 26 '14 at 14:48
-
@bluesm Yeah, that's a typo from my last edit. Fixed; thanks. – zekel Feb 27 '14 at 18:43
-
@zekel, the shouldStartLoadWithRequest method signature should end with a { not a semicolon!! – dev4life Aug 28 '14 at 17:25
-
when should "- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;" be called? – Yadnesh Sep 03 '14 at 12:19
-
@Yadnesh You can use `stringByEvaluatingJavaScriptFromString:` to run Javascript code directly from your Objective-C code. For example, you could have a UIButton call that method to run some Javascript code in your UIWebView. – zekel Sep 03 '14 at 18:31
-
so this is for testing, and when in production ill put the same code on my webpage? – Yadnesh Sep 04 '14 at 05:43
-
basically what goes in server for production env? – Yadnesh Sep 04 '14 at 06:33
-
@Yadnesh This code isn't just for testing. The `stringByEvaluatingJavaScriptFromString` lets you communicate from ObjC -> Javascript. In order to communicate the other way, from Javscript -> ObjC, you must serialize the URLs in the method described above. – zekel Sep 04 '14 at 16:37
-
if you already have a url with fragment like http://www.google.com#1234 and you append #5678 to that like http://www.google.com#1234#5678 iOS won't recognise it... – hariszaman Feb 05 '15 at 17:51
-
I think now there is a better option: http://blog.impathic.com/post/64171814244/true-javascript-uiwebview-integration-in-ios7 – huggie Feb 18 '15 at 09:40
-
Does Anyone know if this has ever ended in an rejected appstore review? Is the review somehow concerned about using this technique? – Colonize.bat Jul 26 '15 at 01:15
-
1@Colonize.bat This approach is widely used and should not get you rejected. (Of course nothing in life is certain except death and taxes but I've been doing this for over five years with no issue whatsoever.) – zekel Jul 27 '15 at 02:37
-
@zekel Many thanks for this approach. I'm getting the following error: error: The URL can’t be shown: [NSErrorFailingURLStringKey: myfakeappname://myJavascriptActionType#%7B%22name%22%3A%22AAPL%22%2C%22exchange%22%3A%22NASDAQ%22%2C%22description%22%3A%22Apple%20Inc.%22%2C%22type%22%3A%22stock%22%2C%22interval%22%3A%22D%22%7D, NSLocalizedDescription: The URL can’t be shown, NSErrorFailingURLKey: myfakeappname://myJavascriptActionType#%7B%22name%22%3A%22AAPL%22%2C%22exchange%22%3A%22NASDAQ%22%2C%22description%22%3A%22Apple%20Inc.%22%2C%22type%22%3A%22stock%22%2C%22interval%22%3A%22D%22%7D] – Ace Green Oct 03 '15 at 21:17
-
1ive just had a problem of myAppScheme being passed to the objective c in lower case despite sending with upper case and checking against upper case. Just a heads up – PowerMan2015 Oct 31 '15 at 13:41
Changing page's location can cause several issues:
- All setInterval and setTimeout immediatly stop on location change
- Every innerHTML won’t work after a canceled location change!
- You may get other really weird bugs, really hard to diagnose …
The solution, as offered here, is:
Keep using the same URL technique (on js and objective-c), just change the location with an iframe:
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "js-frame:myObjectiveCFunction";
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
Hope that helps

- 6,633
- 7
- 53
- 96
-
Not working in iOS 10 please check my question http://stackoverflow.com/q/43867801/3378413 – Mihir Oza May 19 '17 at 19:22
From Objective-C: You can pass a javascript function in a string to UIWebView. The web page will execute it and return a result. This way you can pass variables and get data back from Javascript.
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
Example:
NSString *script = @"document.getElementById('myTextField').value";
NSString *output = [myWebView stringByEvaluatingJavaScriptFromString:script];
From Javascript: Pass your data inside the URL. Intercept URL requests in UIWebViewDelegate. Get the data and abort URL request by returning NO.
<script>window.location = "url://key1/value1"; </script>
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

- 8,419
- 6
- 43
- 51