I am posting some JSON data to a server using JSONKit.
Before posting the data, I am checking the internet connection.
But if the internet connection is lost after sending request, after this line:
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
my app crashes.
I don't want to use asynchronous method here.
Is there any particular way, where I can show an alert rather than having my app crash in this situation?
I got it. if there is no internet connection, then response variable will be nil. I can check that if (response == nil) and give my alert here.
Related
In my app I use following methods to POST/GET data from a remote server.
postData = [self sendSynchronousRequest:request returningResponse:&response error:&error];
- (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
{
NSError __block *err = NULL;
NSData __block *data;
BOOL __block reqProcessed = false;
NSURLResponse __block *resp;
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable _data, NSURLResponse * _Nullable _response, NSError * _Nullable _error) {
resp = _response;
err = _error;
data = _data;
reqProcessed = true;
}] resume];
while (!reqProcessed) {
[NSThread sleepForTimeInterval:0];
}
*response = resp;
*error = err;
return data;
}
I have basic error handling for no network connectivity, and app directs users to no network connectivity viewController. But I would like to account for situations when for instance server is down or data format of api has changed, Just wondering how would I catch such errors and prevent the app from crashing.
A couple of general points:
Don't do requests synchronously. Pass in a block with code that should run when the request completes. There's no reason to tie up a thread waiting for a completion handler to run, and it is even worse if that thread is basically continuously running as yours is.
If you absolutely must do that (why?), at least use a semaphore and wait on the semaphore. That way, you aren't burning a CPU core continuously while you wait.
The way you avoid crashing is to sanity check the data you get back, e.g.
Check the length of the data and make sure you didn't get nil/empty response data.
Assuming the server sends a JSON response, wrap your NSJSONSerialization parsing in an #try block, and if it fails or throws an exception, handle the error in some useful way.
Generally assume that all data is invalid until proven otherwise. Check to make sure the data makes sense.
If you're in charge of the server side, consider passing an API version as an argument, and as you modify things in the future, make sure that incompatible changes occur only when responding to clients that request a newer API version (or, alternatively, send a response that tells the client that it needs to be upgraded, and then refuse to provide data).
All the server connections in my app goes the same way:
dispatch_async to a 2nd thread.
sendSynchronousRequest
fail/success blocks on main thread.
The following is not the full code, just the concept:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:#"someURL"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error)
{
dispatch_async(dispatch_get_main_queue(), ^{ _failureBlock(error); });
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{ _successBlock(data); });
});
Well the app works great! I can use it for thousands of server connections, and it always works when I run a fresh load of the app.
The issue starts when the following occurs:
Send the app to BG.
Wait some time...
The app get killed by the iOS.
Open again, the iOS loads the whole app from scratch.
(see splash screen followed by the first screen, not the one I left to BG..)
In that scenario I get NSURLConnectionErrors!! All kind of them! Code 1003, Code 1009, Code 9... Just name it!
I get errors for all server connections that starts right when the app loads. Any other connections after that works fine! including the refresh of the ones that got failed!
It almost feel like i'm trying to reach my server too quick or too early, BUT I do check and pass a reachabillity test before intiating a connection.
Could really use some help here. Thanks in advance.
UPDATE: As suggested below, tried to use sendAsynchronousRequest - gave me the exact same behaviour. (err: 1001 this time).
OK got it! The appDelegate built the tabBar wich loaded all viewControllers wich sent some NSURLConnections... So for the iOS thought it's too long to wait for the responses and failed them all! (No idea way the first launch is ok and only after the app killed its not)
Anyway, I changed all server loads to perform with selector with 0.1 delay. That way the appDelegate could finish running and all is GOOD! :)
You should look at using this method and send the data Asynchronously
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
the NSOpertationQueue mainQueue uses the mainthread however allowing you to update UI also on the main thread i.e an activityindicator for example..
Is there some method to wait until the call is completed? This is the code:
NSString *strImmagineURL = [NSString stringWithFormat:#"http://www.xxxgo.net/Scxxs/Evento/WIG2ricexxx.php?evento=%#",idLocale];
NSData *dataImmagineURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:strImmagineURL]];
immagine = [UIImage imageWithData:dataImmagineURL];
[`arrayFoto` addObject:immagine];
I noticed that, when the connection in slow, addObject crashes my app because immagine is still nil.
I want to wait for immagine and afterward fill arrayFoto.
It's nil not because it's slow, but because the request failed. dataWithContentsOfURL: is a synchronous method, meaning execution of your program will not continue until the request completes or fails.
Properly handling error cases in HTTP requests requires a lot more setup. You should use something like ASI HTTP Request to handle your requests and success/error handlers more gracefully.
Like it's written in the title, How can I download data from a server in my Cocoa Application?
So far I looked for, I found this.
If you're not downloading a lot of things in parallel and you're doing a simple GET request, the easiest way to do it is to dispatch a synchronous request to one of the global queues:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com/"]];
NSURLResponse* response = nil;
NSError* error = nil;
NSData* result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// There will be response data in response now, like the http status code
// etc. You should check this information to make sure you didn't get a 404
// or some other http status error
if( result ) {
// you have a good result, do something with it like create a new object or
// pass it to a method on this object, etc.
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomethingWithResponseData:result];
});
} else {
// You got an error making the connection, so handle it
NSLog(#"Error making connection: %#", error);
}
});
**note: this sample code uses GCD and therefore will only run on Snow Leopard (10.6) or better. If you need to target Leopard or Tiger, you can do the same thing using dispatched thread selectors, but not as in-line.
ASIHTTPRequest works for iPhone and Mac
How would I get returningResponse (into say, a NSString) from the following code:
NSURLResponse* response;
NSError* error;
NSData* result = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&response error:&error];
I think I can get the info that I need into a string but I can't call anything on response because it's null. Therefore, I assume that I need to call something on result. The problem is, I don't know what to call.
(The URL request has been coded prior to the code sample. I know that that works.) I want to be able to detect if the request as successful.
According to the documentation for the URL loading system:
If NSURLConnection is unable to download the URL the method will return nil and any available NSError instance by-reference in the appropriate parameter.
So, see what's in your "error" parameter to find out what the problem is.