NSURLConnection sendSynchronousRequest/sendAsynchronousRequest fails after app killed by iOS - objective-c

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..

Related

How to avoid app from crashing in synchronous request

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.

NSURLConnection sendAsynchronousRequest:queue:completionHandler not working in iOS 4.3

I am using [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) in my app. By using this my app is terminated in iOS 4.3 but it is working fine in iOS 5.0.
How to use this in iOS 4.3 can any one help me.
Here's a full implementation that works for me. Feel free to rename it and add as a category on NSURLConnection, or just add it as a local method in the class you're working in.
-(void)sendAsynchronousRequest:(NSURLRequest*)request queue:(NSOperationQueue*)queue completionHandler:(void(^)(NSURLResponse *response, NSData *data, NSError *error))handler
{
__block NSURLResponse *response = nil;
__block NSError *error = nil;
__block NSData *data = nil;
// Wrap up synchronous request within a block operation
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
}];
// Set completion block
// EDIT: Set completion block, perform on main thread for safety
blockOperation.completionBlock = ^{
// Perform completion on main queue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
handler(response, data, error);
}];
};
// (or execute completion block on background thread)
// blockOperation.completionBlock = ^{ handler(response, data, error); };
// Execute operation
[queue addOperation:blockOperation];
}
EDIT
I had to modify the method because I was making UIKit calls in my completion block (e.g. updating labels etc). So it's actually a bit safer to call completion block on the main thread. (original version commented out)
The method you are trying to use is only available on iOS 5. For earlier OSes, consider using
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
and wrapping it into a new thread to achieve async behavior.
Both H2CO3 and Ken Thomases suggestions are right.
In addition, you could take a look at ios4-implementation-of-nsurlconnection-sendasynchronousrequestqueuecompletio.
If you use the main queue as the queue where the completion handler performs, you could use (as Tom suggested) the delegate pattern. To avoid duplicate code, you could use a wrapper on NSURLConnection delegates mechanism.
In the other case, if you want to maintain the async behaviour and you don't want to deal with sync call (as H2CO3 suggested, note that his suggestion is also valid) and the completion handler performs in a different queue, then I suggest you to wrap the async delegate pattern in a NSOperation class. This approach is quite difficult but you can find a good way of do this in Concurrent Operations Demystified (see both the posts).
Hope it helps.

GCD and async NSURLConnection

I know that if I create an NSURLConnection (standard async one), it will call back on the same thread. Currently this is on my main thread. (work fine too).
But i'm now using the same code for something else, and I need to keep my UI snappy....
If i do
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* and inside here, at some NSURLConnection is created */
});
.. is it possible that my NSURLConnection is created but my thread disappears before the url connection has returned?
I'm new to GCD. How would one keep the thread alive until my url connection returned, or is there a better way I could be doing this?
So really the issue isn't the lifetime of the thread on which your block runs, it's the fact that this particular thread is not going to have a runloop configured and running to receive any of the events coming back from the connection.
So how do you solve this? There are different options to think about. I can list a few, and I'm sure others will list more.
1 - You could use a synchronous connection here. One disadvantage is that you won't get callbacks for authentication, redirection, caching, etc. (All the normal disadvantages of synchronous connections.) Plus each connection will of course block a thread for some period of time, so if you're doing a lot of these then you could potentially have a few threads blocked at once, which is expensive.
2 - If your connection is simple and you are using iOS5 then you can use this method:
+ (void)sendAsynchronousRequest:(NSURLRequest *)request
queue:(NSOperationQueue*) queue
completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))
This will start an asynchronous connection and then allow you to specify a completion handler (for success or failure) and a NSOperationQueue on which you want that block to be scheduled.
Again, you have the disadvantages of not getting the callbacks you might need for authentication, caching, etc. But at least you don't have threads hanging around blocked by connections that are in flight.
3 - Another option for iOS5 is to set the queue for all delegate callbacks:
- (void)setDelegateQueue:(NSOperationQueue*) queue NS_AVAILABLE(10_7, 5_0);
If you use this, then all of the delegate methods will be executed in the context of whatever NSOperationQueue you specify. So this is similar to option #2, expect that you get all of the delegate methods now to handle authentication, redirection, etc.
4 - You could set up your own thread that you control specifically for managing these connections. And in setting up that thread, you configure a runloop appropriately. This would work fine in iOS4 and 5 and obviously gives you all of the delegate callbacks that you want to handle
5 - You might think about what parts of your asynchronous connection handling are really interfering with your UI. Typically kicking off the connection or receiving delegate callbacks are not that expensive. The expensive (or indeterminate) cost is often in the processing of the data that you collect at the end. The question to ask here is are you really saving time by scheduling a block on some queue just to start an asynchronous connection that will go off immediately and do its thing on another thread anyway?
So you could just start the connection from the main thread, and receive all of the delegate callbacks on the main thread, and then in your implementation of those delegate methods fire off whatever expensive work you need to do on some other queue or thread.
So something like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// go ahead and receive this message on the main thread
// but then turn around and fire off a block to do the real expensive work
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Parse the data we've been collecting
});
}
Again, this is not comprehensive. There are many ways to handle this, depending on your specific needs here. But I hope these thoughts help.
Just as an answer to why your thread was disppearing (and for future reference) the NSURLConnection needs a runloop. If you had added
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
You'd see that the connection runs properly and the thread doesn't disappear untill the connection was completed.
First off, your block and every variable you use within it will get copied to GCD, so the code will not be executed on your thread but on the global queue.
If you want to get your data back on the main thread, you can nest an async call after your data has been fetched:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"www.stackoverflow.com"]];
NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (error) {
// handle error
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
// do something with the data
});
});
But why not use NSURLConnection's built in asynchronous support? You need an NSOperationQueue, but if you are doing alot of network fetches it is the way to go anyway:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"www.stackoverflow.com"]];
[NSURLConnection sendAsynchronousRequest:request
queue:self.queue // created at class init
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
// do something with data or handle error
}];
Personally, I use a library like AFNetworking or ASIHTTPRequest to make networking even easier, which both support blocks (the former utilizes GCD and is a bit more modern).
dispatch_queue_t queue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[btnCreateSmartList setEnabled:NO];
[dbSingleton() createEditableCopyOfDatabaseIfNeeded];
[dbSingleton() insert_SMART_PlaceList:txtListName.text :0:txtTravelType.text: [strgDuration intValue]:strgTemprature:Strgender:bimgdt];
[self Save_items];
//*********navigate new
dispatch_async(dispatch_get_main_queue(), ^{
[activityIndicator stopAnimating];
[self performSelector:#selector(gonext_screen) withObject:nil afterDelay:0.0];
});
});

How can I download data from a server in cocoa (not touch)?

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

Simultaneous NSURLDownloads

I have a Cocoa Mac application set up to download files to a specific folder using NSURLDownload. This works great with a single download at a time. However, if I attempt to start multiple downloads, all but the last will fail immediately.
Is there any way to use NSURLDownload for multiple simultaneous downloads? Or what would be a good way to queue up multiple URLs to be downloaded in order? Or is there a more appropriate way to accomplish this (NSURLConnection seemed possible but I was unsure if I could set the download location and filename as I can with NSURLDownload)?
Each NSURLDownload represents a single downloading instance. You're probably trying to reuse the same one multiple times. It's an inherently asynchronous system that already used background threads. Here's an example based on Apple's sample code:
- (void)startDownloadingURL:sender
{
// Create a couple requests.
NSURLRequest *requestOne = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.apple.com"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLRequest *requestTwo = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://stackoverflow.com"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// Create two download instances
NSURLDownload *downloadOne = [[NSURLDownload alloc] initWithRequest:requestOne delegate:self];
NSURLDownload *downloadTwo = [[NSURLDownload alloc] initWithRequest:requestTwo delegate:self];
if (downloadOne) {
// Set the destination file.
[downloadOne setDestination:#"/tmp" allowOverwrite:YES];
} else {
// inform the user that the download failed.
}
if (downloadTwo) {
// Set the destination file.
[downloadTwo setDestination:#"/tmp" allowOverwrite:YES];
} else {
// inform the user that the download failed.
}
}
- (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error
{
// Release the connection.
[download release];
// Inform the user.
NSLog(#"Download failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringErrorKey]);
}
- (void)downloadDidFinish:(NSURLDownload *)download
{
NSLog(#"The download %# has finished.", download)
// Release the download connection.
[download release];
}
If you attempt to use the same NSURLDownload for both NSURLRequests, then it will kill the previous connection.
I'd second using NSOperation if your on 10.5+ or greater. You could just throw 1 operation onto a queue for each download. Or you could even just use sendSynchronous request and use it with NSOperationQUeue's addOperationWithBlock (10.6+) method and then in your block you are throwing onto the queue you can just use [[NSOperationQueue mainQueue] addOperationWithBlock:^{ when you are doe with the code you need to execute or just periodically need to refresh the UI on the main thread, like so...
[myQueue addOperationWithBlock:^{
//download stuff here...
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//update main thread UI
}];
}];
you would just need to do this for each download.
If you're targeting 10.5+, you should look at NSOperation. It should allow you to build a generic solution for a single download, and then use the built-in queue facilities to manage dependencies if you require certain operations finish downloading before others begin.
Keep in mind that these APIs often expect delegate methods involved to be run on the main thread, so you'll need ensure that occurs if you're working with asynchronous APIs that function via delegate methods. (You can do this pretty simply by using performSelectorOnMainThread: and friends)