Ajax timeout in UIWebView - iphone

I'm working on an iOS app that displays a webpage through a UIWebView. The page contains between 20 and 40 buttons. Each one sends an ajax request to the server. It works for the most part, but the server is a desktop app and could close at any moment for any reason with no warning. With these ajax requests, there is no output displayed to the user so if the server went down, the user would have no idea. I would like to be able to be notified of an ajax timeout and then give the user the options to either try again or go back to the home screen. I can't seem to find a way to have the UIWebView be notified of any failed ajax requests.
At first I thought I could simply use the webView:didFailLoadWithError: method of the UIWebViewDelegate, but apparently it's not notified of failed ajax requests.
Any suggestions are greatly appreciated

If when a failure occurs, there is JavaScript code that is invoked in your Ajax failure handlers that makes a location.href change then the UIWebViewDelegate shouldStartLoadWithRequest: method will get called, giving you the chance to do something whenever there is a failure. i.e.
In the Ajax timeout handling code:
window.location.href = "myscheme://AjaxFailure";
In your UIWebViewDelegate handling code:
- (BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"shouldStartLoadWithRequest: url: %#", [[request URL] absoluteString]);
NSURL *URL = request.URL;
if ([URL.scheme isEqualToString:#"myscheme"])
{
... notify the user there was an error

Related

After authenticating token(ACCESS GRANTED), doesn't redirect to Callback URL

I'm implementing OAuth 1.0 in an iOS app using simple-oauth1 project. I use Withings api so I modified a little bit from simple-oauth1(like consumer oauth key, secret key, callback url...). And I inserted
NSLog(request.URL.absoluteString); like the following code(It's in
OAuth1Controller.m)
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)
request navigationType:(UIWebViewNavigationType)navigationType
{
if (_delegateHandler) {
NSString *urlWithoutQueryString = [request.URL.absoluteString componentsSeparatedByString:#"?"][0];
NSLog(request.URL.absoluteString);
if ([urlWithoutQueryString rangeOfString:OAUTH_CALLBACK].location != NSNotFound)
{
With that code,
I tap the OAuth login button then webview shows login page.
I enter ID/password.
Webview shows account allow page. I tap "Allow" button. (NSLog shows http://oauth.withings.com/account/authorize?acceptDelegation=true&oauth_consumer_key=blahblah&oauth_nonce=blahblah&oauth_signature=blahblah&oauth_signature_method=HMAC-SHA1&oauth_timestamp=blahblah&oauth_token=blahblah&oauth_version=1.0&userid=blahblah)
After above process, webview shows "ACCESS GRANTED" page with oauth_token= blahblah, oauth_verifier=blahblah. But it doesn't redirect to Callback url, it stays at the "ACCESS GRANTED"page.
I've been doing this for about 2 weeks but I cannot find the answer why is this happening.
I had the same issue (using php). The problem seems to be that you must provide a url callback at the request token step (first step, not the authorization).
Withings API not redirecting to my callback url (PHP / OAuth)

xcode, nslog weird hitch

The code below gives me an NSLog redirect location on a tinyurl link when clicking on a tinyurl link. However, if I remove the following piece of code [[UIApplication sharedApplication] openURL:[request URL]];, it will give me the actual website in NSLog and not the tinyurl link. How can I make it where I can have the actual website URL in NSLog while the share application code is presented?
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"Redirect Location: %#",[request.URL absoluteString]);
[[UIApplication sharedApplication] openURL:[request URL]];
}
Short answer:
Using UIWebViewDelegate method shouldStartLoadWithRequest is not a great mechanism for figuring out to which site you'll be redirected (especially since, judging from your code, you'll ultimately opening it in an external app, not your UIWebView), because you're getting shouldStartLoadWithRequest before the redirect takes place. You can, though, use NSURLConnection and NSURLConnectionDataDelegate methods to figure out where you'll eventually be redirected.
Long answer:
If you look at shouldStartLoadWithRequest, if you return YES, you'll let the UIWebView follow the redirect requests. Consider the following shouldStartLoadWithRequest:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"%#", request.URL);
return YES;
}
If you use this with the a random bit.ly URL, say, http://nyti.ms/Yi6EAk, you'll see the following log:
2013-03-12 21:23:31.418 webtest[6959:c07] http://nyti.ms/Yi6EAk
2013-03-12 21:23:31.511 webtest[6959:c07] http://bit.ly/Yi6EAk?cc=0d7134b272b1004cb954d0400076e9fa
2013-03-12 21:23:31.560 webtest[6959:c07] http://www.nytimes.com/2013/03/13/us/politics/ryans-plan-aims-to-balance-budget-in-10-years.html?hp&_r=0
That third call to shouldStartLoadWithRequest is the actual URL for which I defined the bit.ly redirect URL, i.e. the final destination of two consecutive redirects. But if your shouldStartLoadWithRequest returned NO, then you'll never figure out to which site it would ultimately be redirected. (And by the way, your shouldStartLoadWithRequest should definitely return YES or NO ... your sample doesn't return either.)
As you can see, because shouldStartLoadWithRequest happens before the redirect has a chance to take place, you'll see each and every redirect taking place. Depending upon what the resulting site does, you may see subsequent shouldStartLoadWithRequest calls as the page retrieves extra content. This makes this an awkward mechanism for finding out what site to which you were ultimately redirected.
If you really need the site you're being redirected to, you might want to use NSURLConnection, instead. While that is generally used for actually retrieving the data from the server, it can also be used for capturing the redirects (but not suffering from the problems of the UIWebViewDelegate method shouldStartLoadWithRequest, where it's tough to differentiate between genuine redirects from just random additional content that the page may subsequently request).
So consider the following:
self.url = [NSURL URLWithString:#"http://nyti.ms/Yi6EAk"];
NSURLRequest *request = [NSURLRequest requestWithURL:self.url];
[NSURLConnection connectionWithRequest:request delegate:self];
You can then implement connection:willSendRequest:redirectResponse:, a NSURLConnectionDataDelegate method, that will track the various redirects:
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
{
self.url = request.URL;
return request;
}
Clearly, because you're using NSURLConnection to track the redirects, but we don't really care about the responseData that we generally use NSURLConnection to retrieve, we can just cancel the connection as soon as you receive a good response (confident that we already know to which site were finally redirected):
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[connection cancel];
NSLog(#"Ok, we now know that the resulting URL is %#", self.url);
}
You might, by the way, also want to capture connection errors, for example:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Ok, we failed trying to retrieve data from %#", self.url);
}
In closing, it's worth pointing out that this technique, of making the HTTP request with NSURLConnection and letting it follow the whole series of redirects, is a pretty inefficient process, so you'll have to decide whether it's worth it. But this is one way to figure out where your HTTP redirects will lead you.
One final caveat, this captures traditional redirects, but if the page is doing any client-side JavaScript redirects, I don't think this technique will work. It works for the vast majority of redirected HTTP requests, though.

webView and 'timeout'

I have question, in my application I need to have a connection and a webview to display the web content.
The problem is that I have a username and a password which need to be right in order to get a connection. Now I want to have an alert that comes up if the username is wrong. The
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
does not work for this, because if the username or the password is wrong it will continue loading (even though it won't display anything).
Is there a possibilty to 'tell' webview to set a time interval and then display an alert?
THANK YOU SO MUCH!
If you're authenticating with your server via HTTP authentication (per your comment above) then I would do this differently. When the user enters their username/pwd, make a request to any page (something lightweight) using NSURLConnection. If the username/password are incorrect you can handle the failures in the NSURLConnectionDelegate methods and prompt your user for a new username/password.
You can also do this with the iOS 5 version of NSURLConnection instead of the delegate methods but the docs aren't very good.
When this first request succeeds then load the web view.
EDIT: added example of using NSURLConnection to check if HTTP auth succeeds
Ok, here's a simple example. You can make this better by allowing NSURLConnection to load your intended request and if it succeeds load the response data into the webView vs initiating a new request. Read the URL Loading System docs to learn more about how to use NSURLConnection.
In your webView delegate, initiate an NSURLConnection with a request to the same domain as the webView request with the user credentials as follows:
- (void)performHTTPAuthRequest
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://%#:%##test.starsightings.com", username, password]]];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
}
// if the request succeeds, credentials must be good so load the webView
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// request completed successfully so username/password must be good
[self.webView loadRequest:theRequest];
}
// ignore the failure
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}
// if we get an auth challenge, the credentials are bad so cancel the request and alert the user
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
[[challenge sender] cancelAuthenticationChallenge:challenge];
// alert user that username/pwd is invalid
}

Embedding a Smart URL inside an App

I want to embed a URL inside an iPhone app, that will take the user to a landing page which later on I can redirect that URL to an iTunes App link once it becomes available, all that without resubmitting the App for approval again. Is this possible?
Thanks
Yes. Use the openURL: method of UIApplication to open your landing page URL. When you want to swap it out, have your favourite server-side language perform a 301 or 302 redirect, depending on whether the redirect is permanent or not.
Having said that, if all you want to do is direct people to rate the app or something similar to that, you don't need to swap it out; the application ID is all you need to construct the URL and iTunes Connect gives you that when you register the app, before you upload the binary.
You can use a short URL or any other URL with a redirection, then in your code intercept the redirection to avoid opening Safari if it's not necessary:
Create a NSURLConnection:
NSURL *shortURL = [NSURL URLWithString:#"http://tinyurl.com/xxyy"];
NSURLConnection *conn = [[NSURLConnection alloc]
initWithRequest:[NSURLRequest requestWithURL:shortURL]
delegate:self
startImmediately:YES];
Define some delegate methods. In your class, define an NSURL property finalDestination to hold the final URL.
- (NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)response
{
finalDestination = [response URL];
return request;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (!finalDestination) {
/* redirect failed, use backup url */
finalDestination = /* backup url */
}
[[UIApplication sharedApplication] openURL:finalDestination];
}
Also, check Apple Technical Q&A Launching the App Store from an iPhone application.
(Note: ARC code)

timeout value for ios facebook sdk request

hey guys does the ios facebook sdk asynchronous request delegate allow developer to specify some sort of timeoutInterval value? i know that the nsURLRequest has a timeoutInterval value that u can set.
Thanks for your help.
scenario:
i make an async request call. the connection dies after the request call goes out and before the response comes back. currently, i have no way of detecting this. what i want is: i call line number 2. after 10 sec, no response comes back, line 5 would get execute. Or something along the line that there were an error.
1 - // async request call
2 - [facebook requestWithGraphPath:#"me/home" addParams:nil andDelegate:self];
3 - // delegate
4 - - (void) request :(FBRequest *)request didLoad:(id)result {}
5 - - (void) request :(FBRequest *)request didFailWithError :(NSError *)error {}
see Facebook ios SDK, FBRequest.m file at the beginning:
static const NSTimeInterval kTimeoutInterval = 180.0;
then it is used by a connect method. You can change it or patch the SDK so it could be set externally.
hope this helps
FBRequest.m applies it's timeoutInterval to NSMutableURLRequest, which ignores/overrides anything under 240 seconds. If your interested, read more about that here...
iPhone SDK: URL request not timing out
...otherwise it basically means the kTimeoutInterval in FBRequest.m is ignored if set to < 240, so don't bother with it.
I've just gone through this issue myself, I ended up using an NSTimer that will cancel the pending FBRequest's connection on timeout. see details here..
Facebook API - How to cancel Graph Request