How could connectionDidFinishLoading: run if no file is found on server? [duplicate] - objective-c

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Testing use of NSURLConnection with HTTP response error statuses
This is bizarre; I have an async connection like so:
NSString *url=[NSString stringWithFormat:#"http://www.whatever.com/file"];
NSURL *url2=[NSURL URLWithString:url];
NSURLRequest *req=[[NSURLRequest alloc] initWithURL:url2];
NSURLConnection*con=[[NSURLConnection alloc] initWithRequest:req delegate:self];
[req release];
if(con){
NSMutableData *data=[[NSMutableData alloc] init];
self.receivedData=data;
[data release];
}
else {
UIAlertView*alert=[[UIAlertView alloc] initWithTitle:#"Error!" message:#"Unable to connect to server." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
}
Then I have a bunch of standard delegate methods:
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
[receivedData setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[receivedData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
[connection release];
self.receivedData=nil;
UIAlertView*alert=[[UIAlertView alloc] initWithTitle:#"This app requires Internet" message:[NSString stringWithFormat: #"Connection failed.\n Please exit and check your\nInternet access."] delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
[alert release];
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection{
NSString *payload=[[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
self.downloaded=nil;
self.downloaded=payload;
[payload release];
[connection release];
self.receivedData=nil;
NSLog(#"This will display if connectionDidFinishLoading runs.");
}
The NSLog there at the end is running, even if the file is not on the server. Why? I would not expect it to have loaded anything, and instead resulted in an error and an alert view.
These methods seem clear at first, but there must be something going on here that I'm not getting about the async connection.

It DID finish loading. The fact that it finished with an HTTP error is beside the point.
The NSHTTPResponse object you got in didReceiveResponse will have a .responseCode property with the 404 in it.

Related

NSURLConnection delegates not being called even when run on main thread

I know that this kind of question has been asked many times, but all of them point to saying that the connection must be on a different thread.
-(void)distanceMatrix{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:distanceMatrixURL]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10];
connection2 = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection2 scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
NSLog(#"Is%# main thread", ([NSThread isMainThread] ? #"" : #" NOT"));
[connection2 start];
if (connection2)
{
responseData2 = [NSMutableData data];
connectionIsActive = YES;
} else {
NSLog(#"connection failed");
}
}
- (void)connection2:(NSURLConnection *)connection2 didReceiveResponse:(NSURLResponse *)response
{NSLog(#"recieved response");
[responseData2 setLength:0];
}
- (void)connection2:(NSURLConnection *)connection2 didReceiveData:(NSData *)data
{
[responseData2 appendData:data];
}
- (void)connection2:(NSURLConnection *)connection2 didFailWithError:(NSError *)error
{
connectionIsActive = NO;
NSLog(#"failed!!");
}
- (void)connection2DidFinishLoading:(NSURLConnection *)conn
{
connectionIsActive = NO;
SBJsonParser *json = [[SBJsonParser alloc] init];
NSString *responseString = [[NSString alloc] initWithData:responseData2 encoding:NSUTF8StringEncoding];
NSError *jsonError = nil;
NSDictionary *parsedJSON = [json objectWithString:responseString error:&jsonError];
travelTime= [[[[parsedJSON valueForKey:#"rows"] valueForKey:#"elements"] valueForKey:#"duration"] valueForKey:#"text"];
NSLog(#"traveltime = %#", travelTime);
}
When I log it, it says that it runs on the main thread. Connection2 is active but none of the delegates are called.
Also, this is the way I am calling distanceMatrix method
-(id)initWithJsonResultDict:(NSDictionary *)jsonResultDict andUserCoordinates: (CLLocationCoordinate2D)userCoords andTimeURL:(NSString*)timeURL
{
self.distanceMatrixURL = timeURL;
[self distanceMatrix];
//more code here for other purposes
}
Because you have added a 2 into the names of all of the delegate methods. That changes the method signature so you aren't implementing the correct methods. Remove all of the 2 at the start of the methods - (void)connection2: and it should work.

NSURLConnection bizarre crash

I'm creating a Mac app that must run on Mac OS X Tiger. For some bizarre reason, it keeps crashing. The debugger returns the following error:
0x90a594d1 <+0033> mov (%edi,%edx,4),%eax
I've tried to Google the answer, but I found nothing. What am I doing wrong?
-(IBAction)loadPage:(id)sender{
NSURL *URL = [NSURL URLWithString:#"http://www.google.com"];
[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:URL] delegate:self];
NSLog(#"STARTED!");
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(#"STARTED!2");
data = [[NSMutableData alloc]init];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)d{
NSLog(#"STARTED!3");
[data appendData:d];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(#"STARTED!4");
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", str);
[webView loadHTMLString:str baseURL:[NSURL URLWithString:[field stringValue]]];
[str release];
[data release];
}
The solution was to retain the NSURLConnection

Pull more than 1 echo from PHP

OK, I have a login screen that sends username and password to a PHP page. The PHP page echo's Yes or No based on the login status, but I would like it to echo the user id from the database for further use. Do I have to use JSON for this? I dont know how to retrieve more than one echo's. Currently the code is:
- (IBAction) login: (id) sender
{
NSString *post =[NSString stringWithFormat:#"username=%#&password=%#",usernameField.text, passwordField.text];
NSString *hostStr = #"https://www.mysite.com/login.php?";
hostStr = [hostStr stringByAppendingString:post];
NSData *dataURL = [NSData dataWithContentsOfURL: [ NSURL URLWithString: hostStr ]];
NSString *serverOutput = [[NSString alloc] initWithData:dataURL encoding: NSASCIIStringEncoding];
if([serverOutput isEqualToString:#"Yes"]){
UIAlertView *alertsuccess = [[UIAlertView alloc] initWithTitle:#"Business Manager" message:#"Login Successful"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertsuccess show];
[alertsuccess release];
RootViewController *viewMenu = [[RootViewController alloc]
initWithNibName:#"RootViewController" bundle:[NSBundle mainBundle]];
self.rootViewController = viewMenu;
[viewMenu release];
[self.navigationController pushViewController:self.rootViewController animated:YES];
} else {
UIAlertView *alertsuccess = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Username or Password Incorrect"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertsuccess show];
[alertsuccess release];
}
}
Now I just want to pull one more echo called "ID". Forming JSON for this seems arbitrary.
Technically you can use whatever response format you want -- though it's best to stick with something standard as you'll be able to find libraries made for you already. A REST service with a JSON response is a pretty easy way to facilitate communication between a server and client, so it would probably be a good idea to start there.

Method gets called two times

I am doing a NSURLConnection that downloads a file only if there is a new one (checked with the last-modified date). But to accomplish this I am using two methods with two different NSURLRequests and NSURLConnection. Well, they do the same.
- (IBAction)uppdatera:(id)sender
{
checkHeaders = YES;
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.forellgatan.se/site/ftp_files/Kapareskolan.zip"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection)
{
self.receivedData = [[NSMutableData data] retain];
}
else
{
UIAlertView *connectFailMessage = [[UIAlertView alloc] initWithTitle:#"Ingen internetanslutning! 1" message:#"Anslut dig till internet för att ladda ner!" delegate: self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[connectFailMessage show];
[connectFailMessage release];
}
}
- (void)downloadNewFile
{
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.forellgatan.se/site/ftp_files/Kapareskolan.zip"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
NSURLConnection *theConnection2 = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection2)
{
self.receivedData = [[NSMutableData data] retain];
}
else
{
UIAlertView *connectFailMessage = [[UIAlertView alloc] initWithTitle:#"Ingen internetanslutning! 2" message:#"Anslut dig till internet för att ladda ner!" delegate: self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[connectFailMessage show];
[connectFailMessage release];
}
checkHeaders = NO;
self.progressView.hidden = NO;
}
It goes through the didReceiveResponse method:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if (checkHeaders == YES)
{
NSHTTPURLResponse *test = (NSHTTPURLResponse *)response;
if ([test respondsToSelector:#selector(allHeaderFields)])
{
NSDictionary *metaData = [test allHeaderFields];
NSString *lastModifiedString = [metaData objectForKey:#"Last-Modified"];
NSString *savedString = [[NSUserDefaults standardUserDefaults] stringForKey:#"LastModified"];
if (![lastModifiedString isEqualToString:savedString])
{
[self downloadNewFile];
}
else if ([lastModifiedString isEqualToString:savedString])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Ingen uppdatering tillgänglig" message:#"Det finns ingen uppdatering att hämta just nu." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
[standardUserDefaults setObject:lastModifiedString forKey:#"LastModified"];
[standardUserDefaults synchronize];
}
}
[self.receivedData setLength:0];
self.fileSize = [[NSNumber numberWithLong: [response expectedContentLength]] retain];
}
Last the connectionDidFinishLaunching method:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (checkHeaders == NO)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
[self.receivedData writeToFile:[basePath stringByAppendingPathComponent:#"Kapareskolan.zip"] atomically:YES];
[self unzipDownloadedFile];
self.progressView.hidden = YES;
NSLog(#"Finished...");
}
[connection cancel];
}
I know the didFinishLaunching method gets called twice, but I want to know how I could get so the method doesn't get called twice if there is an update?
I know it's a lot asked and much code here but just give me a hint and I'll be very thankful.
If you're done with the first connection object in the didReceiveResponse method you should cancel it then. Otherwise it's going to make it to the connectionDidFinishLoading method.
I think this is what you want:
if (![lastModifiedString isEqualToString:savedString])
{
[connection cancel];
[self downloadNewFile];
}
Also It looks like you're setting checkHeaders to NO after you start the second request which could cause a race condition.
According to the programming guide, a connection "can be canceled any time before the delegate receives a connectionDidFinishLoading: or connection:didFailWithError: message by sending the connection a cancel message".
so why not try moving
[connection cancel];
from the connectionDidFinishLoading method to just after your if-else block in the didReceiveResponse delegate method? You want to cancel the "checkHeaders==YES" connection in either case; either you're about to kick off a new connection, or you already know all you need to know about the current connection.
UPDATED as requested:
if (![lastModifiedString isEqualToString:savedString]) {
[self downloadNewFile];
} else { // you've already implicitly checked for equality above
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Ingen uppdatering tillgänglig" message:#"Det finns ingen uppdatering att hämta just nu." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
// you've used the connection for everything that you need, so cancel it in either case
[connection cancel];
as downloadNewFile kicks off its NSURLConnection asynchronously, this should be okay in the event that the two strings are equal. Slightly safer would be to move the cancel method call to just prior to the if-else check.

Instructions in viewdidload not being taken care of

I am facing an annoying problem. I have an application who is basicly made of several methods:
viewDidload, connection:didReceiveResponse, connection:didReceiveData...
In my viewDidload, I define a NSURLRequest to a personal websiten, and right after and before it I added a label.text=#"xxx". I know the problem doesn't come from linking the label in IB because it used to display what I wanted.
But now it seems none of those two label.text instructions are working even though I know my NSURLRequest works because the number of bytes received changes when I change the website... Why is that ? And I'm guessing the other instructions that come after aren't working either.
I will give more details when I can in case anyone can enlighten me on this.
Have a good day and thanks for your help
- (void)viewDidLoad {
[super viewDidLoad];
label.text=#"rrr";
request=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://mywebsite.aspx?example=5"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
label.text=#"aeza";
NSURLConnection *connection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection) {
receiveddata=[[NSMutableData data] retain];
label.text=#"NO BUG";
}
else {
label.text=#"BUG";
}
datastring = [[NSString alloc] initWithData:receiveddata encoding:NSUTF8StringEncoding];
components=[datastring componentsSeparatedByString:#"|"];
label.text=datastring;
[datastring release];
}
-(void) connection:(NSURLConnection *)connection didReceiveResponse: (NSURLResponse *)response
{
[receiveddata setLength:0];
}
-(void) connection: (NSURLConnection *)connection didReceiveData: (NSData *)data
{
[receiveddata appendData:data];
}
-(void)connection: (NSURLConnection *)connection didFailWithError:(NSError *)error
{
[connection release];
[receiveddata release];
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[receiveddata length]);
[connection release];
[receiveddata release];
}
#end
I would move that setup logic to -viewWillAppear, rather than the -viewDidLoad.
Nevermind, I got this to work by moving instructions to another method.