How to handle app URLs in a UIWebView? - iphone

I recently found that my UIWebView was choking on ITMS links. Specifically, from the UIWebView in my app, if I navigate to a site such as this one and click the "Available on the App Store" link, UIWebView would error out with "Error Domain=WebKitErrorDomain Code=101 The URL can't be shown."
After a bit of Googling, I realized that I needed to catch requests for app links and have iOS handle them. I started out by looking to see if the scheme starts with "itms" in -webView:shouldStartLoadWithRequest:navigationType:, but realized that there might be other kinds of app links that the system can handle. So I came up with this, instead:
- (void)webView:(UIWebView *)wv didFailLoadWithError:(NSError *)error {
// Give iOS a chance to open it.
NSURL *url = [NSURL URLWithString:[error.userInfo objectForKey:#"NSErrorFailingURLStringKey"]];
if ([error.domain isEqual:#"WebKitErrorDomain"]
&& error.code == 101
&& [[UIApplication sharedApplication]canOpenURL:url])
{
[[UIApplication sharedApplication]openURL:url];
return;
}
// Normal error handling…
}
I have two questions about this:
Is this sane? I'm specifically checking for the error domain and error code and fetching the URL string from the userInfo. Is that stuff likely to remain?
This does work for the above-linked app store link, but when I switch back to my app, there appears to have been a subsequent failed request that failed with "Frame load interrupted". how can I get rid of that? It doesn't happen when I have the OS handle the request from -webView:shouldStartLoadWithRequest:navigationType:, so it's a bit annoying.
How do you handle such requests?

Here's what I came up with. In webView:shouldStartLoadWithRequest:navigationType:, I ask the OS to handle any non-http and non-https requests that it can, like so:
- (BOOL)webView:(UIWebView *)wv shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
// Determine if we want the system to handle it.
NSURL *url = request.URL;
if (![url.scheme isEqual:#"http"] && ![url.scheme isEqual:#"https"]) {
if ([[UIApplication sharedApplication]canOpenURL:url]) {
[[UIApplication sharedApplication]openURL:url];
return NO;
}
}
return YES;
}
This works very well except for the bloody "Frame Load Interrupted" error. I had thought that by returning false from webView:shouldStartLoadWithRequest:navigationType: that the web view would not load the request and therefore there would be no errors to handle. But even though I return NO above, I still "Frame Load Interrupted" error. Why is that?
Anyway, I'm assuming it can be ignored in -webView:didFailLoadWithError::
- (void)webView:(UIWebView *)wv didFailLoadWithError:(NSError *)error {
// Ignore NSURLErrorDomain error -999.
if (error.code == NSURLErrorCancelled) return;
// Ignore "Fame Load Interrupted" errors. Seen after app store links.
if (error.code == 102 && [error.domain isEqual:#"WebKitErrorDomain"]) return;
// Normal error handling…
}
And now iTunes URLs work properly, as do mailto:s and app links.

Starting with Theory's code, examine the URL for "itms" scheme(s) (this method can be called multiple times due to redirects). Once you see an "itms" scheme, stop the webView from loading and open the URL with Safari. My WebView happens to be in a NavigationController, so I pop out of that after opening Safari (less flashing).
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request
navigationType:(UIWebViewNavigationType)navigationType
{
if ([[[request URL] scheme] isEqualToString:#"itms-apps"]) {
[webView stopLoading];
[[UIApplication sharedApplication] openURL:[request URL]];
[self.navigationController popViewControllerAnimated:YES];
return NO;
} else {
return YES;
}
}

Does it help if you register your app for handling itms: links?
e.g.
http://inchoo.net/iphone-development/launching-application-via-url-scheme/
You may start with scheme http but then get an itms redirect, which could fail if your app is not registered as handling that scheme.

Related

Capturing an specific logout URL in a webView to interact with an embedded html app

I have a simple app, with only a login page. When the user sing-in, I load a webview running an entire html5 app.
The point is that I would like to capture when the user has decided to logout.
Is there any way to listen when a specific url has been loaded?
If so, when the user press logout and the /logout.html would be loaded and I would have the opportunity of returning the control to the iOS app.
Conform to UIWebViewDelegate in your .h file
Set your delegate of UIWebview
Implement the below method:
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"URL Request = %#",[[[webView request] URL] lastPathComponent]);
// if (check stringlogout.html) {
// return NO;
// }
// else{
// return YES;
// }
return YES;
}
Check your last component string if it is logout.html then you can do the required stuff and in else you can do what if it is not logout.html action.
Return YES if you want to load the page and return NO if you do not want to load the page.
Good luck with your app...

WebKitErrorFrameLoadInterruptedByPolicyChange what is that?

I get WebKitErrorFrameLoadInterruptedByPolicyChange in
(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
using FBDialog when trying to share an URL for the first time in
all next try are OK.
Do you have an idea why?
Thanks
Are you sure that you receive WebKitErrorFrameLoadInterruptedByPolicyChange?
There is a problem with SSO in Facebook connect. When an application become active after giving authorization in Safari or Facebook App, web view fails to load your initial share request.
I receive Error Domain=NSURLErrorDomain Code=-999. You can change if statement in (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error as follow to fix it:
if (!(([error.domain isEqualToString:#"NSURLErrorDomain"] && error.code == -999) ||
!([error.domain isEqualToString:#"WebKitErrorDomain"] && error.code == 102))) {
[self dismissWithError:error animated:YES];
}
https://github.com/ShareKit/ShareKit/issues/56

Parse url so that only maps links open in external app from within UIWebView?

I have web content inside a UIWebView in my app. My goal is to have all links open normally inside the app, but any links starting with "http://maps", get opened in safari, so they can in turn be opened in the external iphone maps app. If you have a solution for this problem stop reading now, below I'm going to propose my solution. Currently all links are opened inside the app, so http://maps links open to m.google.com inside the app. The solution I'm thinking of involves this code which uses openURL to open all links in safari:
(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
\[\[UIApplication sharedApplication] openURL:request.URL];
return false;
}
return true;
}
Obviously the problem with this code is that all links are opened in safari, and I only want map links. Can you suggest a way to parse through links and only pass ones that start with http://maps through the function? Also a more simple question, how do I delegate UIWebView so I can run this code, and also is the viewcontroller.m the right place to put this code?
If you guys could suggest an entire function, including the openURL part above and the link parsing to make sure only maps links get passed through the function that would be awesome. Again, if you have another solution or workaround I would love to hear it. Thanks so much for your help, stackoverflow has been a lifesaver, I'm almost finished with my first project ever!
Try something like this:
-(BOOL)webView:(UIWebView *)inWeb
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)inType {
// let UIWebView deal with non-click requests
if (inType != UIWebViewNavigationTypeLinkClicked) {
return YES;
}
// URL starts with "http://maps"?
if ([[request.URL description] hasPrefix:#"http://maps"]) {
// open URL in Safari and return NO to prevent UIWebView from load it
[[UIApplication sharedApplication] openURL:[request URL]];
return NO;
}
// otherwise let UIWebView deal with the request
return YES;
}
NSString *linkPath = [[request url] path];
if ([linkPath hasPrefix:#""http://maps"]) {
//open in safari
}
else {
//do whatever
}

How to open link in Safari from an HTML hosted in a web browser control?

We have an iphone app that hosts a web browser control which points to a website whose sole purpose is to display information for the application. I would like to know if it is possible to have an anchor/button open Safari using HTML/JavaScript. If it is possible, how so?
Thanks...
You can setup a delegate for the UIWebview you are using. In this delegate, write something like this:
-(bool) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
//You might need to set up an "interceptLinks"-Bool since you don't want to intercept the initial loading of the content
if (self.interceptLinks) {
NSURL *url = request.URL;
//This launches Safari with your URL
[[UIApplication sharedApplication] openURL:url];
return NO;
}
//No need to intercept the initial request to fill the WebView
else {
self.interceptLinks = TRUE;
return YES;
}
}
If you only want to intercept some links, you can parse the url and only open Safari if necessary.
Set the delegate property of your UIWebView object (what you call the 'web browser control') to an object of your own that implements the method "webView:shouldStartLoadWithRequest:navigationType:" of the UIWebViewDelegate protocol:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIWebViewDelegate_Protocol/Reference/Reference.html%23//apple_ref/occ/intf/UIWebViewDelegate
You can then detect if you desired URL is being requested and in that case open it on an external Safari instance using UIApplication's openURL method.
You can use something like this:
myWebView.ShouldStartLoad += LoadHook;
[...]
bool LoadHook (UIWebView sender, NSUrlRequest request, UIWebViewNavigationType navType){
var requestString = request.Url.AbsoluteString;
// determine here if this is a url you want to open in Safari or not
if (WantToOpenInSafari (requestString)){
UIApplication.SharedApplication.OpenUrl (new NSUrl (requestString));
return true;
} else {
return false;
}
}

Sending loadRequest messages to UIWebView before current view is finished loading causes error

I have a webview as the detail view of a tableView navigation based app. There are "UP" and "DOWN" arrows on the navigationBar that the user can use to page through the different detail views.
Everything works fine as long as the user only clicks the "UP" or "Down" arrows once. But if the arrows are clicked multiple times and the loadRequest message is sent twice to the UIWebView, I get the error message "NSURLErrorDomain error -999" from my didFailLoadWithError method.
It seems like if a loadRequest is sent while the page is currently loading a view the error is sent. As long as the page is finished loading everything works fine.
I've tried a variety of solutions, all with the same result.
Thanks for the help!
I had the same problem today - try also this approach:
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"didFail: %# stillLoading:%# error code:%i", [[webView request]URL], (webView.loading?#"NO":#"YES"), [error code]);
if ([error code] != -999) {
// Handle your other errors here
}
}
Reference
I solved the problem by calling:
[self.myWebView stopLoading];
myWebView.delegate = nil;
before reloading the new URL.
Before I send the new loadRequest I call:
myWebView.delegate = self;