how do I check an http request response status code from iOS? - iphone

I am sending an http request from iOS (iPad/iPhone) to my python google app engine server using the NSURLConnection and NSURLRequest classes.
How do I read the response's status, i.e. the value set by app engine using response.set_status(200, message="Success") for instance?
I'm not able to find where I can read these status codes once I receive the NSURLConnection's connectionDidFinishLoading delegate call on the client end.

If you are sending a synchronous request, you could get the response code from NSHTTPURLResponse.
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:URL_LOGIN]];
NSData *respData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"~~~~~ Status code: %d", [response statusCode]);
Hope this will help you :)

The connection:didReceiveResponse: delegate method is called when a response is received, which gives you an NSURLResponse to play with.
If you've made an HTTP request, then it'll actually be an NSHTTPURLResponse object, which has a statusCode method.

Related

iOS app crashing if data is inaccessible

I'm using the following to request data using NSJSONSerialization. The problem I'm having is that if the data is inaccessible (e.g. no network connection) the app crashes. How could I go about stopping the app from crashing if the network or server is down?
I'm calling [self requestData]; in the viewDidLoad: method
-(void)requestData {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL
URLWithString:#"http://example.com/api/nodes"]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSError *jsonParsingError = nil;
NSDictionary *publicData = [NSJSONSerialization JSONObjectWithData:response
options:0
error:&jsonParsingError];
publicDataArray = [publicData objectForKey:#"data"];
for(publicDataDict in publicDataArray) {
NSLog(#"data output is %#",[publicDataDict objectForKey:#"title"]);
}
}
thanks for any help
Some thoughts:
Use Reachability for checking network connection
Always use asynchronous request, else it'll block your UI till the app get the response from server.
Always use exception handling
Here the issue is:
You are calling a synchronous request in the viewDidLoad using sendSynchronousRequest. But the server is down, so you won't get the result, and it still expect any data to come. But your app won't load untill that request finishes. Due to this springboards application-watchdog will terminate your app.
What is Watch dog ?
watchdog — In order to keep the user interface responsive, iOS
includes a watchdog mechanism. If your application fails to respond to
certain user interface events (launch, suspend, resume, terminate) in
time, the watchdog will kill your application and generate a watchdog
timeout crash report. The amount of time the watchdog gives you is not
formally documented, but it's always less than a network timeout.
Please check this Technical question on Apple site.
Why don't you check if [NSURLConnection sendSynchronousRequest:] got any error?
NSError *requestError = nil;
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&requestError];
if (requestError)
{
NSLog(#"sync. request failed with error: %#", requestError);
}
else
{
// handle data
}
And you really should check if NSJSONSerialization had an error too:
NSError *jsonParsingError = nil;
NSDictionary *publicData = [NSJSONSerialization JSONObjectWithData:response
options:0
error:&jsonParsingError];
if (jsonParsingError)
{
NSLog(#"JSON parsing failed with error: %#", jsonParsingError);
}
else
{
// do something
}

how to compress request using ASIFormDataRequest?

I'm trying to to send some post data to a Apache server from iPad application using the ASIHttp library.
actually I need to send huge data to the server and that means I need to compress the request body so I write some code to send the data and compress the request BUT there are no parameters received on the server !!!
the iOS code is :
NSURL * URL = [NSURL URLWithString:myURL];
ASIFormDataRequest *ASIRequest = [ASIFormDataRequest requestWithURL:URL];
ASIRequest.shouldCompressRequestBody=YES;
ASIRequest setPostValue:data forKey:#"data"];
[ASIRequest startSynchronous];
NSError *error = [ASIRequest error];
if (!error) {
NSString *response = [ASIRequest responseString];
NSLog(#"response %#" , response);
}
PS: if I removed the ASIRequest.shouldCompressRequestBody=YES; everything works fine and I can see the data but when use it I see nothing on the server
the request can be seen on the server but with no parameter
noway to send such data over GET method.
the server configuration are fine.
any solution ? any comment or idea can help ?
By default, most web servers do not support compression on POSTs. The accepted answer here does a really good job explainining it: Why can't browser send gzip request?
According to official documentation, this feature has only been tested with Apache servers.
EDIT:
Here is a code snipt that compresses the actual post data:
if ([self shouldCompressRequestBody]) {
NSError *err = nil;
NSData *compressedBody = [ASIDataCompressor compressData:[self postBody] error:&err];
if (err) {
[self failWithError:err];
return;
}
[self setCompressedPostBody:compressedBody];
[self setPostLength:[[self compressedPostBody] length]];
}
Source: http://forums.three20.info/discussion/77/tturlrequest-vs-asihttprequest/p1

iPhone objective c Asyncron HTTPS request in NSThread

I need a good solution for my little issue.
What I have at the moment:
My App works with asyncron https request very well. At the moment the app uploads an JSON object, so far so good. After that I save the object in an sqlite database.
Now I have to change to determine what the received server response code is.
I will get an response code like 000 if the uploaded data was valid, and a code like 151 if not. For that I have to wait for the server response to save the response code in the database as well.
I experimented with NSThread, too. But that didn't work out as well as I expected.
Somebody any suggestions? I mean, it should be one of the most common things ;) I just don't see it.
thx, dominik
I usually use an NSOperationQueue to manage my communication. This lets you use a synchronous request instead of asynchronous, and handle the whole shebang in one method.
Example:
-(void)doIt {
NSInvocationOperation *op = [[[NSInvocationOperation alloc] initWithTarget:self selector:#selector(doIt_) object:nil] autorelease];
[opQueue addOperation:op];
}
-(void)doIt_ {
NSData *data;
NSMutableURLRequest *request;
NSURLResponse *response = nil;
NSError *error = nil;
// Build request to spec
data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:error];
// do something with data
[self performSelectorOnMainThread:#selector(yadda) withObject:yaddayadda waitUntilDone:NO];
}
If you do use the asynchronous loading methods, you have to implement a delegate to catch the response data as it comes down, and do something with it when finished.
I recommend that you take a look at ASIHTTPRequest which is a wrapper for the CFNetwork API. Particularly look into the mechanism it offers for asynchronous HTTP requests, where you can define blocks to be executed once the request has completed or failed and can you can inspect the response data easily. Your code would be something like this:
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setRequestMethod:#"POST"];
// add your json object to the request
[request setCompletionBlock:^{
int responseCode = [request responseStatusCode];
NSData *responseData = [request responseData];
// do whatever you want with this info
}];
[request setFailedBlock:^{
NSError *error = [request error];
// handle the error
}];
[request startAsynchronous];

Why does the activity indicator never finish when using ASIHTTPRequest?

I use ASIHTTPRequest to do http requests in my iPhone app. ASIHTTPRequet comes with that feature that starts the activity indicator when issuing a request and stops it when finished. The problem is, once I started a request the indicator never stops and keeps spinning as long as my app runs.
Here is my code, a little utility method that fetches some content from the web synchroniously (since it gets started in a different thread):
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL: [NSURL URLWithString: url]];
[request startSynchronous];
NSError *error = [request error];
NSString *response = nil;
if (error) {
NSLog(#"error %#", error);
return nil;
}
int statusCode = [request responseStatusCode];
response = [NSString stringWithString: [request responseString]];
NSLog(#"status code: %d response: %#", statusCode, response);
if (statusCode != 200) {
return nil;
}
return response;
The above code works just fine, I get the contents of the given URL as a NSString only the indicator keeps spinning. My question is: Why does the indicator never stop and how to fix it? Do I have to release some resources here?
This is a bug that was fixed very recently in the development version of ASIHTTPRequest:
http://github.com/pokeb/asi-http-request/commit/35ea592084145b3332861344f36b52dbcaafa351
(It only affects synchronous requests started on a secondary thread)
Can you try the same thing with an asynchronous request and see if that changes it? I use ASIHTTPRequest and I've never noticed this behavior, but I also never use synchronous requests.

iPhone SDK Remote file exists, without cache

I need to check if a file exists on my server without using cache. The methods I have used are all returning a 200, even if the file does not exist, so I can only assume there is a cache problem, or theres a problem with my code.
Heres my code: for arguments sake..the URL is changed in this example, but the url is correct in my code.
NSString *auth = [NSString stringWithFormat:#"http://www.mywebsite.com/%#.txt",[self aString]];
NSURL *authURL = [NSURL URLWithString:auth];
NSURLRequest* request = [NSURLRequest requestWithURL:authURL
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:5.0];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request
delegate:self];
NSHTTPURLResponse* response = nil;
NSError* error = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"statusCode = %d", [response statusCode]);
if ([response statusCode] == 404)
NSLog(#"MISSING");
else
NSLog(#"EXISTS");
the response is always 200, even if I rename the file on the server.
There are a couple of potential problems with your code. First, when you create conn using connectionWithRequest:delegate: you are starting an asynchronous request. The response would be received in the delegate's (self in your case) connection:didReceiveResponse: method. Are you trying to do the request asynchronously? From the rest of your code though, it looks like you are actually trying to do a synchronous request. That's what sendSynchronousRequest:returningResponse:error: is for. If that's what you intend, then you don't need the earlier call to create a connection.
Assuming thats the case, you need to capture and check the value returned from calling sendSynchronousRequest:returningResponse:error:. It will return nil if the connection failed, which is what I suspect is happening. You can then look at the error returned to figure out what is going on. Try something like:
NSData * result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (result != nil) {
NSLog(#"statusCode = %d", [response statusCode]);
if ([response statusCode] == 404)
NSLog(#"MISSING");
else
NSLog(#"EXISTS");
} else {
NSLog(#"%#", error);
}
Is it possible it's caching on the server side? If so you could try NSURLRequestReloadIgnoringLocalAndRemoteCacheData instead of NSURLRequestReloadIgnoringCacheData.