I have part of my app written in JS and running inside of a WebView. I'm using the UIWebView shouldStartLoadWithRequest method to capture http requests as a means of communicating between JS and obj-c. This works great until I attempt to load a Modal View Controller over my webview from inside the shouldStartLoadWithRequest method. Once this happens, shouldStartLoadWithRequest is no longer called. Sometimes I need to dismiss this modal view controller and go back to the webview and do some things and then re-present the modal controller. The modal controller comes up the first time just fine, then I dismiss it and attempt to present it again by navigating to a URL from javascript and it no longer will present itself. NSLogs inside shouldStartLoadWithRequest are never run.
In my javascript I do something like this:
window.location='myapp:whateverMethod';
objective c code looks like this:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSString *requestString = [[request URL] absoluteString];
NSLog(@"REQUEST URL: %@", requestString);
if([requestString hasPrefix:@"myapp:"]) {
NSArray *components = [requestString componentsSeparatedByString:@":"];
NSString *function = [components objectAtIndex:1];
if([self respondsToSelector:NSSelectorFromString(function)]) {
[self performSelector:NSSelectorFromString(function)];
}
return NO;
}
return YES;
}
-(void) whateverMethod {
NSLog(@"whateverMethod called!");
// This is a quick way to grab my view controller from the storyboard, so assume it exists
UIViewController *splash = [self.storyboard instantiateViewControllerWithIdentifier:@"splashViewController"];
[self presentModalViewController:splash animated:NO];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
[self dismissModalViewController:splash animated:NO];
});
}
At this point my webview is still visible. I navigate from page to page in my webapp and all javascript works great in it but the "shouldStartLoadWithRequest" delegate method of the webview no longer is called. I cannot figure out why. Does anyone have any ideas?
I noticed that Cordova doesn't set the window.location property. Instead it has two options: it either creates an iframe and sets the src of the iframe to that url, or it creates an XMLHttpRequest object e.g. in the iOSExec() function:
if (bridgeMode) {
execXhr = execXhr || new XMLHttpRequest();
// Changeing this to a GET will make the XHR reach the URIProtocol on 4.2.
// For some reason it still doesn't work though...
execXhr.open('HEAD', "file:///!gap_exec", true);
execXhr.setRequestHeader('vc', cordova.iOSVCAddr);
if (shouldBundleCommandJson()) {
execXhr.setRequestHeader('cmds', nativecomm());
}
execXhr.send(null);
} else {
execIframe = execIframe || createExecIframe();
execIframe.src = "gap://ready";
}
That being said, it may be beneficial to use something like Cordova instead of trying to roll it yourself (even if it's just embedding their view controller), since they handle a lot of the headaches that come up with webview delegates.
I've just had the same problem, but related to using a href="#" anchor.
This Stack Overflow answer sorted it
There are more answers on that thread that deal with widow.location, so you may have luck with them.
Checked out Cordova and they have their own queuing system, not really a help. But...
Disobedient Media's answer gave me an idea. Instead of window.location, why not try window.location.hash.
Now some JS code for logging is:
function sendMsgToNative(msg)
{
window.location.hash = '~cmd~' + msg;
}
console.log = function (msg) { sendMsgToNative('log~js ' + msg); };
and the Objective-C code is:
NSString *req = [request.URL absoluteString];
NSArray *components = [req componentsSeparatedByString:@"~"];
// Check for your protocol
if ([components count] > 1 && [(NSString *)[components objectAtIndex:1] isEqualToString:@"cmd"])
{
// Look for specific actions
if ([(NSString *)[components objectAtIndex:2] isEqualToString:@"log"])
{
NSString *logStr = [(NSString *)[components objectAtIndex:3] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
LOGI("%@", logStr);
}
}
You get the full URL including 'http:...' so I chose tilde instead of colon, and incremented the indices.
Now you can log all willy-nilly and send whatever amount of commands you want and they will all get through :-)
I (embarrassingly) spent a couple of hours working on this today, and realised that in my viewDidDisappear: I was setting the UIWebViewDelegate to nil!
All I needed to do to fix was once the modal was dismissed, re-set the UIWebViewDelegate and everything worked again.