Question about HTTP exception handling on iPhone - iphone

I try to add a method to handle exception, but the program just crashes instead of pop up an AlertView.
1) I set up the connection:
-(void)connect:(NSString *)strURL
{
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:strURL]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection)
{
// receivedData is declared as a method instance elsewhere
receivedData = [[NSMutableData data] retain];
}
else
{
// inform the user that the download could not be made
}
}
2)I add method to receive data and conver it to string:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// append the new data to the receivedData
// receivedData is declared as a method instance elsewhere
[receivedData appendData:data];
ReturnStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
}
3)I add exception handle method:
-(void) connection:(NSURLConnection *)connection didFailWithError: (NSError *)error {
UIAlertView *errorAlert = [[UIAlertView alloc]
initWithTitle: [error localizedDescription]
message: [error localizedFailureReason]
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[errorAlert show];
}
After I change the strURL to a wrong url, the program just crashes. Any ideas why the AlertView doesn't pop up?

Check out the error handling that I've got in this file. If you set the URL to an invalid URL, it does (in my example) come back with a nice dialog error message. I just tried it to be sure.
Relevant code in the linked file is:
-(void) connection:(NSURLConnection *)connection
didFailWithError: (NSError *)error {
UIAlertView *errorAlert = [[UIAlertView alloc]
initWithTitle: [error localizedDescription]
message: [error localizedFailureReason]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[errorAlert show];
[errorAlert release];
[activityIndicator stopAnimating];
NSLog (#"Connection Failed with Error");
}

Related

Connection Lost error while using NSURLConnection

I am facing connection lost issue while using NSURLConnection. I am using NSURLConnection for asynch download. I am downloading big file of size around 80MB. I am writing received data in file every time with proper file handling. After sometime I am getting error of connection "Connection Lost" in method of NSURLConnection delegate named didFailWithError. If I execute in simulator on Mac then it will take long time but file gets downloaded successfully without having Connection Lost error. Any suggestion how to avoid this error? or what is the reason behind this error?
Let me know if any detail is required. Please note that I have read similar kind of post but it didnt help me.
Find below code snippet and let me know if more information is required:
-(void) startDownloadFromURL:(NSString*)URLString
{
if(URLString == nil)
{
[delegate DownloadFailed:-1];
return;
}
//self.pstrBaseFilePath = filePath;
URLString = [URLString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSMutableURLRequest* pRequest = [[NSMutableURLRequest alloc] init];
[pRequest setURL:[NSURL URLWithString:URLString]];
if(gpUserDataManager.pstrSessionID == nil)
return;
[pRequest addValue:[#"ASessionID=" stringByAppendingString:gpUserDataManager.pstrSessionID] forHTTPHeaderField:#"Cookie"];
[pRequest setHTTPMethod:#"GET"];
[pRequest setTimeoutInterval:180];
self.urlConnection = [[NSURLConnection alloc] initWithRequest:pRequest delegate:self];
[urlConnection start];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if([httpResponse statusCode] == 200)
{
}
else
{
//Start.NOODLE-13304
/* NSInteger iResponseCode = [httpResponse statusCode];
NSString* pstrStr = [NSString stringWithFormat:#"%d", iResponseCode];
//pTheConnection = nil;
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Response Error", #"")
message:pstrStr
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK", #"")
otherButtonTitles:nil] show];
*/
[AUserDataManager ProcessResponseCode:httpResponse.statusCode];
[self.urlConnection cancel];
[delegate DownloadFailed:httpResponse.statusCode];
//End.NOODLE-13304
}
//[self.pRecvdata setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if(self.recvdData == nil)
{
self.recvdData = [[NSMutableData alloc] init];
}
[self.recvdData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// bIsResponseOK = FALSE;
//
// [NSThread detachNewThreadSelector: #selector(SpinEnd) toTarget:self withObject:nil];
//
// pTheConnection = nil;
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Connection Error", #"")
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK", #"")
otherButtonTitles:nil] show];
[self.urlConnection cancel];
[delegate DownloadFailed:-1];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection cancel];
[delegate DownloadCompleted:self.recvdData];
}
DownloadRequest *request = [[DownloadRequest alloc] init];
request.delegate = self;
[request startDownloadFromURL:strURL];

Message sent to deallocated instance in iPhone

I am new to iPhone,
I am creating NSURLConnection as suggested by apple here
but my app crashes when i Dismiss my view, i have tried concept of NSZombieEnabled which shows me -[CALayer release]: message sent to deallocated instance 0x68b8f40
I am displaying a webpage in Webview, When users clicks on download link in the webview then shouldStartLoadWithRequest method will be called inside this method i am creating NSURLConnection.
here is my code snippet,
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data1
{
[receivedData appendData:data1];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
DirPath=[self applicationDocumentsDirectory];
NSLog(#"DirPath=%#",DirPath);
[receivedData writeToFile:DirPath atomically:YES];
UIAlertView* Alert = [[UIAlertView alloc] initWithTitle:#"Download Complete !"
message:nil delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[Alert show];
[Alert release];
// release the connection, and the data object
[connection release];
[receivedData release];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error1
{
[connection release];
[receivedData release];
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error1 localizedDescription],
[[error1 userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
//CAPTURE USER LINK-CLICK.
Durl=[[url absoluteString]copy];
//Checking for Duplicate .FILE at downloaded path....
BOOL success =[[NSFileManager defaultManager] fileExistsAtPath:path];
lastPath=[[url lastPathComponent] copy];
if (success) //if duplicate file found...
{
UIAlertView* Alert = [[UIAlertView alloc] initWithTitle:#"This FILE is already present in Library."
message:#"Do you want to Downlaod again ?" delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Yes",#"No",nil];
[Alert show];
[Alert release];
}
else //if duplicate file not found directly start download...
{
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:Durl]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
receivedData = [[NSMutableData data] retain];
} else {
NSLog(#"Inform the user that the connection failed.");
}
return YES;
}
Any help will be appreciated.
Make sure that in your implementation of dealloc you are resetting the delegate to nil. Another place for doing so would be viewWillDisappear:.
The reason for your app to crash / access a zombie is that the UIWebView instance will possibly be trying to call back the viewController even though it has already been deallocated. To prevent that, you have to set the delegate of that UIWebView back to nil before the viewController goes out of scope. That is a general issue when working with delegation prior to iOS5's ARC implementation. iOS5 finally offers weak references, those actually nil themselves once their instance is getting deallocated.
Example A:
- (void)dealloc
{
[...]
_webView.delegate = nil;
}
Example B:
- (void)viewWillDisappaer:(BOOL)animated
{
[super viewWillDisappaer:animated];
_webView.delegate = nil;
}
Edit:
After reading your question once again, I realised that the zombie is a UIView or a UIControl since the message is sent to a CALayer. Make sure your problem actually is related to the web view by temporarily removing all webView related code.
I think issue with this NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
You are creating an instance of NSURLConnection with delegate self, when you dismiss the view everything get deallocated on that view. But when the NSURLConnection tries to call it's delegate method crash will occur.
So you need to set the delegate of NSURLConnection to nil in your viewWillDisappear for doing this, you need to create object of NSURLConnection in interface.
#interface yourClass
{
NSURLConnection *theConnection;
}
#end
in .m
theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
- (void)viewWillDisappaer:(BOOL)animated
{
[super viewWillDisappaer:animated];
theConnection.delegate = nil;
}

How to fetch the data from Google news by XML Parsing

I want to fetch Google news data by XML parsing and save it into an array. The XML is like this
http://news.google.co.in/news?pz=1&cf=all&ned=in&hl=en&output=rss
Well I parse xml some different way than others and being frank I really do not know which technique it is but I assure you it works fine for me and I have implemeted it successfully in so many projects. Have a look at my code where I load tweets from some profile
This is the function where I make call for parser.
-(void)loadtweet
{
#try
{
NSString *urlString = [NSString stringWithFormat:#"https://api.twitter.com/1/statuses/user_timeline.xml?screen_name=SrBachchan&count=5"];
NSLog(#"fetching data from--------> : %#",urlString);
NSString* escapedUrlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSMutableURLRequest *request1 = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:escapedUrlString]];
NSURLConnection *con=[[NSURLConnection alloc] initWithRequest:request1 delegate:self];
if(con)
truckData=[[NSMutableData data]retain];
}
#catch (NSException *exception)
{
UIAlertView *v = [[UIAlertView alloc] initWithTitle:#"ERROR" message:#"Please Try Again Later." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[v show];
[v release];
}
}
And these are the NSURLConnection delegate methods:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[truckData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[truckData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[tweets removeAllObjects];
#try
{
// [app.trucks removeAllObjects];
NSString *thexml=[[NSString alloc] initWithBytes:[truckData mutableBytes] length:[truckData length] encoding:NSUTF8StringEncoding];
NSArray *array=[thexml componentsSeparatedByString:#"<status>"];
NSLog(#"%d",[array count]);
for(int i=1;i<[array count];i++)
{
NSString *str=[array objectAtIndex:i];
NSArray *arr1=[str componentsSeparatedByString:#"<text>"];
NSString *data=[arr1 objectAtIndex:1];
NSRange ranfrom=[data rangeOfString:#"</text>"];
// nt.truckName=[data substringToIndex:ranfrom.location];
[tweets addObject:[data substringToIndex:ranfrom.location]];
}
}
#catch (NSException *exception)
{
UIAlertView *v = [[UIAlertView alloc] initWithTitle:#"ERROR" message:#"Please Try Again Later." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[v show];
[v release];
}
}
I have used some string functions to separate tags and stored the values in Array.
This is where you can start: NSXMLParser class reference.

[NSMutableURLRequest released]: message sent to deallocated instance

My app is calling the rqst_run method below in didViewLoad method but I've an error. Debugger reports the following error:
[NSMutableURLRequest released]: message sent to deallocated instance
I don't know where this variable get released
Declared in header file (interface section):
NSMutableString *rqst_error;
NSMutableData *rqst_data;
NSMutableDictionary *listing_items;
and I defined this method in implementation:
- (void)rqst_run
{
rqst_data = [[NSMutableData data] retain];
NSMutableURLRequest *http_request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.feedserver.com/request/"]];
[http_request setHTTPMethod:#"POST"];
NSString *post_data = [[NSString alloc] initwithFormat:#"param1=%#&param2=%#&param3=%#",rqst_param1,rqst_param2,rqst_param3];
[http_request setHTTPBody:[post_data dataUsingEncoding:NSUTF8StringEncoding]];
rqst_finished = NO;
[post_data release];
NSURLConnection *http_connection = [[NSURLConnection alloc] initWithRequest:http_request];
[http_request release];
if(http_connection)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
if([rqst_data length]>0)
{
NSString *rqst_data_str = [[NSString alloc] rqst_data encoding:NSUTF8StringEncoding];
SBJsonParser *json_parser = [[SBJsonParse alloc] init];
id feed = [json_parser objectWithString:rqst_data_str error:nil];
listing_items = (NSMutableDictionary *)feed;
[json_parser release];
[rqst_data_str release];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Feed" message:#"No data returned" delegate:self cancemButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Connection Problem" message:#"Connection to server failed" delegate:self cancemButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[rqst_data setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[rqst_data appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[rqst_data release];
[connection release];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[rqst_data release];
[connection release];
rqst_finished = YES;
}
Your initialization
NSMutableURLRequest *http_request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.feedserver.com/request/"]];
is a convenience constructor that has a built-in autorelease for the object you are initializing using the constructor. So by calling
[http_request release];
you are trying to release something that is auto-released. In other words, you are over-releasing.
You should only call release on objects that you allocate using the keywords,
"New", "Alloc", "Copy". or use "retain"
Line [http_request release]; is unnecessary as your object is autoreleased.
For creation of your NSMutableURLRequest you used method which returns aurora eased instance and you not responsible for it's release. If you use methods which require you perform -alloc, -copy, -retain than you responsible for releasing this instance.

Need help: My program should react when an other class is doing something specific - Not as easy as it sounds

I´m writing an program that uses twitter. I want that my app shows an UIAlertView when the user presses on the "Tweet"-Button and the username or password is wrong.
For my Twitterfunction I use the TwitterRequest.m/h from Brandon Trebitowski. If everthing works great and the username/password is right, this happens in my app:
TwitterRequest * t = [[TwitterRequest alloc] init];
(...);
[t statuses_update:twittermessage.text delegate:self requestSelector:#selector(status_updateCallback:)];
loadingActionSheet = [[UIActionSheet alloc] initWithTitle:#"Posting to Twitter..." delegate:nil
cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
[loadingActionSheet showInView:self.view];
}
- (void) status_updateCallback: (NSData *) content {
[loadingActionSheet dismissWithClickedButtonIndex:0 animated:YES];
[loadingActionSheet release];
NSLog(#"%#",[[NSString alloc] initWithData:content encoding:NSASCIIStringEncoding]);
}
But how can I show an UIAlertView when the username/password was wrong?
Here is the TwitterRequest.m:
#import "TwitterRequest.h"
#implementation TwitterRequest
#synthesize username;
#synthesize password;
#synthesize receivedData;
#synthesize delegate;
#synthesize callback;
#synthesize errorCallback;
-(void)friends_timeline:(id)requestDelegate requestSelector:(SEL)requestSelector{
isPost = NO;
// Set the delegate and selector
self.delegate = requestDelegate;
self.callback = requestSelector;
// The URL of the Twitter Request we intend to send
NSURL *url = [NSURL URLWithString:#"http://twitter.com/statuses/friends_timeline.xml"];
[self request:url];
}
-(void)statuses_update:(NSString *)status delegate:(id)requestDelegate requestSelector:(SEL)requestSelector; {
isPost = YES;
// Set the delegate and selector
self.delegate = requestDelegate;
self.callback = requestSelector;
// The URL of the Twitter Request we intend to send
NSURL *url = [NSURL URLWithString:#"http://twitter.com/statuses/update.xml"];
requestBody = [NSString stringWithFormat:#"status=%#",status];
[self request:url];
}
-(void)request:(NSURL *) url {
theRequest = [[NSMutableURLRequest alloc] initWithURL:url];
if(isPost) {
NSLog(#"ispost");
[theRequest setHTTPMethod:#"POST"];
[theRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[theRequest setHTTPBody:[requestBody dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]];
[theRequest setValue:[NSString stringWithFormat:#"%d",[requestBody length] ] forHTTPHeaderField:#"Content-Length"];
}
theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData that will hold
// the received data
// receivedData is declared as a method instance elsewhere
receivedData=[[NSMutableData data] retain];
} else {
// inform the user that the download could not be made
}
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
//NSLog(#"challenged %#",[challenge proposedCredential] );
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:[self username]
password:[self password]
persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential
forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
// inform the user that the user name and password
// in the preferences are incorrect
NSLog(#"Invalid Username or Password"); //THIS MUST be important. The console shows this message, if the username/password is wrong. Here is also the place, where I set the bool to TRUE
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// this method is called when the server has determined that it
// has enough information to create the NSURLResponse
// it can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is declared as a method instance elsewhere
//[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//NSLog([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
// append the new data to the receivedData
// receivedData is declared as a method instance elsewhere
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
[theRequest release];
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
if(errorCallback) {
[delegate performSelector:errorCallback withObject:error];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
if(delegate && callback) {
if([delegate respondsToSelector:self.callback]) {
[delegate performSelector:self.callback withObject:receivedData];
} else {
NSLog(#"No response from delegate");
}
}
// release the connection, and the data object
[theConnection release];
[receivedData release];
[theRequest release];
}
-(void) dealloc {
[super dealloc];
}
#end
I know that the important line in the TwitterRequest.m is in the else-cause of the -(void)connection-methode, because the Console writes always Invalid Username or Password, when they are wrong. So I tried to make there a bool as propertey, which will be set to TRUE, when the name/password is wrong(= when the else-cause will be used). In my ViewController I made this:
if (t.stimmtNicht == TRUE) {
[loadingActionSheet dismissWithClickedButtonIndex:0 animated:YES];
[loadingActionSheet release];
UIAlertView *alert;
alert = [[UIAlertView alloc] initWithTitle:#"Ouch!"
message:#"Your Username or Password is wrong!"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[alert show];
[alert release];
}
But he always doesn't use the if-cause, even if the password is wrong. I think the code go faster through the if-quere than the TwitterRequest set it to TRUE. What can I do?
Thanks for your help and sorry for this stupid question, but I'm learning Objective-C and programming in general since just one week and I don´t know correctly how to interact from my ViewController with other classes.
Also sorry for my bad English!
Two ways I can think of:
Apparently your TwitterRequest class has a "delegate" ivar; try calling the delegate to tell him (her?) that your credentials are wrong.
Send an NSNotification through the NSNotificationCenter
In both cases, the class who gets notified should display the login view (with the username and password fields), and that's it.