retrying a request after it failed - ios

I am using the default classes provided by app to do my networking tasks in my app.
At some point, some of my requests time out (regardless of the reason for the timeout)
I would like to retry some of these requests
I have subclassed the NSURLConnection class and added some parameters to it.
However, in the method "connection:didFailWithError:"
con.retries seems to always be 1, never incremented. Why ?
- (void)connection:(NSURLConnection*) connection didFailWithError:(NSError *)error
{
[[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
NSLog(#"%#",[NSString stringWithFormat:#"Did recieve error: %#", [error description]]);
NSLog(#"%#",[NSString stringWithFormat:#"%#", [[error userInfo]description]]);
WBURLConnection *con = (WBURLConnection *)connection;
if([con shouldRetryRequest:error]){
con.retries ++;
[con start];
}else{
[con cancel];
[con.data setLength:0];
if(!self.alert){
self.alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[self.alert show];
}else {
if(![self.alert isVisible]){
[self.alert show];
}
}
}
}
-(BOOL)shouldRetryRequest:(NSError *)error{
[self.retryCount appendString:[NSString stringWithFormat:#"%#:%ld",error,(long)self.retries]];
LogInfo(#"retries:%#",self.retryCount);
if([error code] == -1004){
return NO;
}
return self.retries<3;
}

Related

Handling NSError with error.code

I am trying to handle specific NSErrors with UIAlertView but none of the codes in the if statements are called when the webview didFailLoadWithError is called. Below is my condition statement:
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
loadFailedBool = YES;
NSLog(#"Error code %ld", (long)[error code]);
if ([error code] != -999) {
//loading was cancelled and another one initiated. Don't show alert
return;
}
else if ([error code] == NSURLErrorTimedOut || [error code] == kCFURLErrorTimedOut) {
//connection timed out
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Network Connection timed out" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
alert.tag = 1;
}
else if ([error code] == -1005) {
//connection lost
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Network Connection to the host is lost" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
alert.tag = 1;
}
else if ([error code] == kCFURLErrorNotConnectedToInternet || [error code] == -1009) {
//no internet connection
NSLog(#"Error here");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Looks like you are not connected to the internet" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
alert.tag = 1;
}
else if ([error.domain isEqualToString:#"WebKitErrorDomain"] && error.code == 102) {
return;
}
}
My question is, how do i get the condition called because the log prints -1009 sometimes when there is no internet. Thanks
The thing is that you will never get past this line:
if ([error code] != -999) {
return;
}
So if the error code is -1009, or anything but -999, it's too late - you have already said return and the whole thing is over. The later code won't execute.

How to display alert dialog while connecting to server?

An async request has been sent to the server and here is my connection delegate.
in RKYLoginDelegate.m file, i made an alert to tell user that the member is verifying when receiving data.
didReceivedData
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[_receivedData appendData:data];
loginAlertView = [[UIAlertView alloc] initWithTitle:#"message"
message:#"verifying member..."
delegate:self
cancelButtonTitle:nil
otherButtonTitles:nil];
[loginAlertView performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
NSLog(#"Received data: %#", [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding]);
}
and in finish loading data, if nothing return, then shows the error message.
didFinishLoading
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
in the code, it will verify return state and parse value, if not respond then alert a dialog to notify user error message
if ([jsonDataDictionary count] > 0) {
// add member into data
RKYMemberManager *rkyMemberManager = [RKYMemberManager new];
[rkyMemberManager addMember:jsonDataDictionary];
// navigate to main
UIStoryboard *rkyMainStoryboard = [UIStoryboard storyboardWithName:#"RKYMainStoryboard" bundle:nil];
RKYMainViewController *rkyMainViewController =
[rkyMainStoryboard instantiateViewControllerWithIdentifier:#"RKYMain"];
[[[UIApplication sharedApplication] delegate].window.rootViewController.navigationController presentViewController:rkyMainViewController animated:YES completion:nil];
}
else {
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#"message"
message:#"Cannot login!"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
[alertView show];
NSLog(#"cannot login");
}
Those alert dialogs did show its message, but will cause an error:
Thread 1:EXC_BAD_ACCESS(code=2, address=0xc)
as title, am I doing correctly?
if yes, how to solve the problem that caused?

UIWebView and authentication challenge unable to authenticate

http://m-qa-www.comixology.net/ this is the url when i am loading it on safari it works great.
but when i load it on uiwebview it does not do anything because of authentication challenge so i added support for authentication it works in for some url but it does not work for this above url.following is the code i am using for it.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
{
[self showNetworkIndicator];
if (!authed && !conectionAlready) {
authed = NO;
conectionAlready=YES;
urlCon=[[NSURLConnection alloc] initWithRequest:request delegate:self];
return YES;
}
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
NSLog(#"webViewDidStartLoad");
[webView stringByEvaluatingJavaScriptFromString:#"window.alert=null;"];
[self showNetworkIndicator];
authed=NO;
}
-(void) webViewDidFinishLoad:(UIWebView *)webView
{
//conectionAlready=NO;
//[iWebView stringByEvaluatingJavaScriptFromString:#"window.alert=null;"];
NSString *currentURL = webView.request.URL.absoluteString;
NSLog(#"current url %#",currentURL);
URLBar.text=currentURL;
if([iWebView canGoBack])
{
[backButton setEnabled:true];
}
else
{
[backButton setEnabled:false];
}
if([iWebView canGoForward])
{
[fwdButton setEnabled:true];
}
else
{
[fwdButton setEnabled:false];
}
[self hideNetworkIndicator];
}
-(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
NSLog(#"url error ->%#",error.localizedDescription);
[self hideNetworkIndicator];
NSString *errorStr=error.localizedDescription;
if ([errorStr rangeOfString:#"NSURLErrorDomain"].location==NSNotFound) {
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:nil message:#"A server with the specified hostname could not be found." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
}
-(void) showNetworkIndicator
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
-(void) hideNetworkIndicator
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{
authed = YES;
authChallenge=challenge;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Aithentication Required"
message:scRequest.URL.absoluteString
delegate:self cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Log In", nil];
[alert setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
[alert show];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
{
[connection cancel];
conectionAlready=NO;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if ([response respondsToSelector: # selector (allHeaderFields)]) {
NSDictionary * dictionary = [httpResponse allHeaderFields];
NSLog (# "dictionary =%#", dictionary);
}
if (authed)
{
NSLog(#"remote url returned error %d %#",[httpResponse statusCode],[NSHTTPURLResponse localizedStringForStatusCode:[httpResponse statusCode]]);
NSLog(#"The response is =%#",response);
authed=YES;
NSString *newUrl = [NSString stringWithFormat:#"%#", response.URL];
NSLog(#"newURL%#",newUrl);
scURL =[NSURL URLWithString:newUrl];
scRequest=[NSMutableURLRequest requestWithURL:scURL];
conectionAlready=NO;
[iWebView loadRequest:scRequest];
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[self hideNetworkIndicator];
authed=NO;
conectionAlready=NO;
NSLog(#"oops localizedDescription:%#",error.localizedDescription);
NSLog(#"oops localizedFailureReason:%#",error.localizedFailureReason);
}
- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)buttonIndex
{
//NSLog(#"textFieldAtIndex0 %#",[alert textFieldAtIndex:0].text);
//NSLog(#"textFieldAtIndex1 %#",[alert textFieldAtIndex:1].text);
if (buttonIndex==0) {
[[authChallenge sender] cancelAuthenticationChallenge:authChallenge];
}
else
{
[[authChallenge sender] useCredential:[NSURLCredential credentialWithUser:[[alert textFieldAtIndex:0] text] password:[[alert textFieldAtIndex:1] text] persistence:NSURLCredentialPersistenceNone] forAuthenticationChallenge:authChallenge];
}
}
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
{
return YES;
}
Remember the webView is not so competent as a native browser. The native browser handles internally many exception which the webView is not at all competent of. I would suggest if you have the code for the link that you are hitting than try traversing the code and find errors for which the webView doesn't load. I encountered the same issue where some of my JS file were non-responsive. There is no other way you can find bugs in a webView,as it provides limited set of delegated to actually traverse. :)

Login and Password Input with NSURLConnection

I am a newbie in the development of iOS applications.
I need to reach different url loaded from a remote XML file,
some of these url require a http basic authentication.
Authentication with NSURLCredential, it works correctly.
Now, I want to require the user to enter their credentials.
When the connection fails I visualize the interface
login UIAlertView with setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput.
At this point I verify the correspondence
username and password entered by the user with textFieldAtIndex: 0 and 1
but I can and use them for a new connection attempt with NSURLConnection.
Can someone help me?
This is my code:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge previousFailureCount] == 0)
{
NSLog(#"received authentication challenge");
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:#""
password:#""
persistence:NSURLCredentialPersistenceForSession];
NSLog(#"credential created");
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
NSLog(#"responded to authentication challenge");
}
else
{
NSLog(#"previous authentication failure");
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Login"
message:#"Enter your username & password"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Login", nil];
[message setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
[message show];
}
}
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
NSString *inputText = [[alertView textFieldAtIndex:0] text];
if( [inputText length] >= 1 )
{
return YES;
}
else
{
return NO;
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Login"])
{
UITextField *username = [alertView textFieldAtIndex:0];
UITextField *password = [alertView textFieldAtIndex:1];
NSLog(#"Username: %#\npassword: %#", username.text, password.text);
if ([username.text isEqualToString:#"myname"] && [password.text isEqualToString:#"1234"])
NSLog(#"Success");
}
[xmlDataBuffer setLength:0];
}

UIAlertView within mapViewDidFailLoadingMap:withError:

I will improve my iOS Programm by handling errors for loading a MapView. If there is no internet connection I will display an alert. But the the method mapViewDidFailLoadingMap will get called again and again so the UIAlert.
How can I achieve that the the Alert will only showed once?
- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error {
NSString *domain = [error domain];
NSInteger code = [error code];
NSLog(#"userInfo: %#", [error userInfo]);
NSLog(#"localizedDescription: %#", [error localizedDescription]);
NSLog(#"localizedFailureReason: %#", [error localizedFailureReason]);
NSLog(#"localizedRecoverySuggestion: %#", [error localizedRecoverySuggestion]);
NSLog(#"localizedRecoveryOptions: %#", [[error localizedRecoveryOptions] description]);
if ([domain isEqualToString:NSURLErrorDomain]) {
if (code == NSURLErrorNotConnectedToInternet) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Map Loading Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}
}
If your program logic allows then you can set the delegate for the MKMapView object to nil when the failure/error occurs and it will stop sending failure/error messages to the delegate thereby preventing display of multiple alerts.
You can set delegate to nil within mapViewDidFailLoadingMap:withError method.
You can use a BOOL flag to ensure that the error is shown only once for the MKMapView:
- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error
{
if (displayErrors) {
// display the error...
displayErrors = NO;
}
}
If you reset the flag in viewWillAppear for the corresponding view controller, the error will be shown once for every time the view is accessed:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
displayErrors = YES;
}

Resources