Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shouldStartLoadWithRequest is not called when using AJAX/XMLHttpRequest

I am trying to send method invocations from JavaScript to Objective-C and vice versa. Everything works fine for window.location triggered urls, which are catched by shouldStartLoadWithRequest. Now if I try to use an AJAX call instead, shouldStartLoadWithRequest is not called. Is there a way to do this? Mainly I do not want to be restricted to the max URL size on data that can be passed from JavaScript to Objective-C.

My UIWebViewDelegate implements:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
 NSString *url = [[request URL] absoluteString];
 NSRange urlrange = [url rangeOfString:@"myScheme://"];
 if(urlrange.length > 0){
  NSLog(@"this is an objective-c call, do not load link: %@", [url substringWithRange:NSMakeRange(urlrange.location, [url length])] );
  return NO;
 } else {
  NSLog(@"not an objective-c call, load link: ", url );
  return YES;
 }
}

My JavaScript calls:

// works
window.location.href = "myScheme://readyHref";  

// fails
var xmlHttpReq = false;
if (window.XMLHttpRequest) {
 xmlHttpReq = new XMLHttpRequest();
} else if (window.ActiveXObject) {
 xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlHttpReq.open('GET', "myScheme://readyAJAX", false);
xmlHttpReq.send();
like image 806
el_migu_el Avatar asked Apr 25 '10 11:04

el_migu_el


3 Answers

If you'd like to have a cleaner way of transferring data from JavaScript to Objective-C you could follow these steps:

  1. Once you have JS data you want to send back to Objective-C then within JS store it in some globally accesible variable e.g. var myTempData = ...
  2. Use the above-mentioned technique of document.location = 'myapp://fire-some-event'
  3. Once in your Objective-C code execute [myWebView stringByEvaluatingJavaScriptFromString:@"myTempData"].
  4. I think you're done, watch for threading/object-to-string-via-json-encoding.
like image 157
ohadpr Avatar answered Oct 14 '22 04:10

ohadpr


Although I could not find the answer why AJAX requests are not intercepted by shouldStartLoadWithRequest I found two workarounds. I assume that JavaScript Requests are not being intercepted but only HTML-associated / -originated requests are. If any one could verify or correct this, I'd be happy.

The two workarounds are simple. On the one hand it seems that the restriction of 256 characters in the URL does not affect MobileSafari or at least the url in that state. I read that Safari allows 80'000 charcters. However I did not feel well with that. So the workaround #2 is to use a html form that uses the POST method and set the action to your scheme. The data can be stored in the form as the value of an input field or the like. After submitting the form with JavaScript shouldStartLoadWithRequest is triggered and the data can be retrieved from HTTPBody.

like image 31
el_migu_el Avatar answered Oct 14 '22 04:10

el_migu_el


I'm not sure if this will work for you, but if you're trying to use the shouldStartLoadWithRequest in order to access native phone functions from javascript you could use PhoneGap. It is a handy and open-source objective-c framework that is able to bridge javascript and objective-c.

I needed a way to asynchronously cause the phone to vibrate from javascript and I looked at the PhoneGap source to see how they do it. After some testing I found that $.ajax wouldn't be intercepted by shouldStartLoadWithRequest and also that it was very particular about when it would intercept http requests. You could get everything working nicely though by calling document.location instead of $.ajax and by using another protocol such as gap:// instead of http://.

So the javascript code looks like:
document.location = "gap://vibrate";

And the objective-c code:

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *url = request.URL;
    NSString *urlString = url.relativeString;

  if ([urlString isEqualToString:@"gap://vibrate"])
  {
    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
  };

    return YES;
}
like image 2
Julian Avatar answered Oct 14 '22 03:10

Julian