Using NSURLConnection Multiple times - ios

I am trying to start a second NSURLConnection after starting the first one. My first one works perfectly: the appropriate delegates are all called, and everything executes as planned. However, after the first one finishes, I create a second one, and nothing happens (the delegate is never called). What could be happening? I know I can't reuse the same NSURLConnection, but I reinitialise it before using it again, so it should be a completely new connection.
Here's my code for starting (both) connections. It's the same instance variable, but it's reinitialised. Also note that the second one is not started until the first one has finished running completely.
if (connection) {
[connection cancel];
}
currentResponse = nil;
error = nil;
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
if (!connection) {
NSLog(#"Connection could not be initialized.");
[self connectionFinished];
} else {
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[connection start];
}

You haven't shared how you're running it "on a different thread", but the typical problem is if you are using dispatch queues or operation queues, your connection is running asynchronously, itself, and therefore the dispatched operation is completing and getting released and you're losing your connection.
A couple of possible solutions:
You can perform your network operations synchronously in this background operation of yours (this is the only time you should do synchronous network operations). This is the simplest solution, though you haven't explained what you're doing with your NSURLConnection, so this technique may or may not work for you. But if you're just trying to download something from a URL, you can do:
NSData *data = [NSData dataWithContentsOfURL:url
options:0
error:&error];
This approach doesn't work if you're doing any a little more complicated that requires the NSURLConnectionDataDelegate methods, such challenge-response authentication or if you are streaming using didReceiveData to reduce your app's memory footprint or for performances reasons, etc. But if you're just trying to download data from a remote server (e.g. retrieving an image, an XML/JSON feed, etc.), this is easiest.
In a similar vein (i.e. you don't need the NSURLConnectionDataDelegate methods), but you're you're creating some rich NSURLRequest for your connection, then you can use either sendAsynchronousRequest or sendSynchronousRequest.
If you need the NSURLConnectionDataDelegate calls, you can use the setDelegateQueue (designating a NSOperationQueue) or scheduleInRunLoop (designating a NSRunLoop), and it will automatically dispatch the connection updates to the appropriate queue/runloop. Just make sure to initWithRequest using the startImmediately option of NO, set the delegate queue or runloop, and then start the connection. With this technique, you preserve the full richness of NSURLConnectionDataDelegate if you absolutely need it.
Alternatively, if you're not using operation queues, you could also keep your background operation alive until the connection is done. This approach gives you synchronous behavior in you background operation (keeping your connection alive), while preserving the NSURLConnectionDataDelegate methods. This technique is demonstrated by Apple's XMLPerformance Sample (see the downloadAndParse method of CocoaXMLParser.m and LibXMLParser.m), where they initiate the NSURLConnection and then use the following construct to keep the background operation alive until the NSURLConnectionDataDelegate methods end up setting the done instance variable:
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
} while (!done);
I confess that I find this final approach vaguely dissatisfying and would lean towards the other alternatives, depending upon what flexibility and functionality you need from your NSURLConnection. For us to provide more meaningful counsel, you just need to provide more information about (a) the sort of work you're doing in your NSURLConnectionDataDelegate methods; and (b) which technology you're using to run your code in the background.
For a few more options, also feel free to see GCD and async NSURLConnection.

Related

AFHttpRequestOperation inside NSOperation

Been doing iOS for a while, but now I'm working in a complex client-server app that requires me to:
Send a request to the server (using AFHttpRequestOperation)
The response is either an interval or a Dictionary
If I get an interval, I need to wait (interval)-seconds and call again the server again (wait / sleep?)
this continues until I receive an expired message, the user cancels or a Dictionary (as mentioned above) is received.
IF a dictionary is received, I have to pause the calling to the server until the user takes an action on the information I display to him.
Based on user decision, I need to resume and keep calling, wait, calling, and so on until i do receive the dictionary or it expires (if the user rejected the information) OR if the user accepts, stop asking and further process the information (but I wont keep calling the serve)
Being new to threading in iOS, what's the best approach:? I have been working with dispatch_asynch + performSelector:onThread:withObject but it's giving me all sort of problems!
Question is: What's the best approach / approach to achieve pause, cancel, wait / sleep in an NSOpertation subclass that internally uses an AFHttpRequestOperation?
I'm a rookie at threading, specially under iOS, so your help is greatly appreciated!!!!
I would suggest not to use threading with networking . While it doesn’t seem clear enough but implementing a asynchronous network requests take very less CPU time.
Many try to do this and end up with delegates methods of an asynchronous network calls not being called. If network calls are performed on a background thread, the thread might probably exit before the delegates are finished calling.
The asynchronous calls can be cancelled just by calling the cancel/pause method. If you feel that main thread is very slow, make sure you avoid heavy operation on it, like parsing, calling CameraView. I used NSrunLoop for just testing how to do this, without much advantages
-(void)start
{
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSURL* url = [[NSURL alloc] initWithString:#"http:your url"];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; // ivar
[request release];
[url release];
// Via the run loop
NSPort* port = [NSPort port];
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:port forMode:NSDefaultRunLoopMode];
[connection scheduleInRunLoop: runLoop forMode:NSDefaultRunLoopMode];
[connection start];
[runLoop run];
}
You can replace the native api's with your library ones. Hope this helps in some way.

Making threaded NSURLConnections

I have a lot of connections going when my app starts, so I wanna put them on background threads so I can make new connections other than the starting connections before they all complete.
Below, threadedRequest: is a method that's starting a NSURLConnection, but when I call performSelectorInBackground:withObject: in the if clause, the connection starts, but never finishes. The else clause works fine and returns data from the connection
if (background)
{
[self performSelectorInBackground: #selector(threadedRequest:) withObject: args];
}
else
{
[self performSelector: #selector(threadedRequest:) onThread: [NSThread mainThread] withObject: args waitUntilDone: NO];
}
If you want to perform NSURLConnection in the background, you must be sensitive to special conditions that apply to NSURLConnectionDataDelegate methods in background threads. You have several options:
Use one of the non-delegate alternatives. Given that you're performing this in the background, you could use sendSynchronousRequest, or if just requesting data from a simple URL, you could use NSData class method dataWithURL.
If you really need the delegate version (because you need progress updates via didReceiveData or because you need one of the NSURLConnectDelegate methods for authentication or the like, you have a couple of basic options:
You can create a NSOperationQueue and set the delegate queue for the connection. For example, rather than:
- (void)startConnection:(NSURL *)url
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
You could do:
- (void)startConnection:(NSURL *)url
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection setDelegateQueue:self.connectionDelegateQueue];
[connection start];
}
Alternatively, you could do what AFNetworking does (see AFURLConnectionOperation.m, for sample implementation):
Create a dedicated NSThread for the NSURLConnectionDataDelegate calls;
Start a NSRunLoop on that thread;
Use the same startImmediately:NO rendition of the NSURLConnection init method as above; and
Use the scheduleInRunLoop option for the NSURLConnection before you start it.
Or, easiest, you could just use AFNetworking
Two observations:
I might be inclined to use operation or dispatch queues instead of performSelectorInBackground. See Concurrency Programming Guide.
By the way, you should be aware that there is a limit as to how many concurrent NSURLConnection requests iOS can perform simultaneously (it's 5 or 6, I believe). Thus, if you initiate a dozen background requests and then initiate a "foreground" request, the "foreground" request may not start immediately. You may want to limit how many background requests that can operate concurrently if you want to avoid this potential problem.
Personally, when I want to constrain the number of network requests, I add my background requests to a NSOperationQueue and set the number of maximum concurrent operations (e.g. I generally use 4), enjoying the benefits of concurrent operations, while not trying to perform more simultaneous connections than iOS will permit. By the way, when availing yourself of operation queue's maxConcurrentOperations feature, you have to make sure that the requests are synchronous (or wrap the asynchronous connection in a custom NSOperation that won't terminate until the connection is done or fails).

Using NSUrlConnection inside either NSThread or NSOperation

I am developing a static library that needs to do some stuff in the background, without interacting with the main thread. To give you an idea, think of just logging some user events. The library must keep doing this stuff until the user exits the app or sends it to the background (pushes the home button) - in other words it needs to keep doing stuff inside a loop.
The only interaction between the main app thread and the spawned thread is that occasionally the main app thread will put some stuff (an event object) into a queue that the spawned thread can read/consume. Other than that, the spawned thread just keeps going until the app exists or backgrounds.
Part of what the spawned thread needs to do (though not all of it) involves sending data to an HTTP server. I would have thought that it would be easy to subclass NSThread, override its main method, and just make a synchronous call to NSUrlConnection with some sort of timeout on that connection so the thread doesn't hang forever. For example, in Java/Android, we just subclass Thread, override the start() method and call a synchronous HTTP GET method (say from Apache's HttpClient class). This is very easy and works fine. But from what I have seen here and elsewhere, apparently on iOS it is much more complicated than this and I'm more than a bit confused as to what the best approach is that actually works.
So should I subclass NSThread and somehow use NSUrlConnection? It seems the asynchronous NSUrlConnection does not work inside NSThread because delegate methods don't get called but what about the synchronous method? Do I somehow need to use and configure the RunLoop and set up an autorelease pool? Or should I use an NSOperation? It seems to me that what I am trying to do is pretty common - does anyone have a working example of how to do this properly?
As I understand it, to use NSURLConnection asynchronously you need a runloop. Even if you use an NSOperation you still need a runloop.
All the examples I have seen use the Main Thread to start NSURLConnection which has a runloop. The examples using NSOperation are set up so the operation is Concurrent which tells NSOperationQueue not to provide it's own thread, they then make sure that NSURLConnection is started on the main thread, for example via a call to performSelectorOnMainThread:
Here is an example:
Pulse Engineering Blog: Concurrent Downloads using NSOperationQueues
You can also search the Apple documentation for QRunLoopOperation in the LinkedImageFetcher sample which is an example class showing some ins and outs of this kind of thing.
(Although I'm not sure I actually saw any code that example showing how to run your own runloop, again this example relies on the main thread.)
I've used the grand central dispatch (GCD) methods to achieve this. Here is an example that worked for me in a simple test app (I'm not sure if it applies in a static library, but may be worth a look). I'm using ARC.
In the example, I am kicking off some background work from my viewDidLoad method, but you can kick it off from anywhere. The key is that "dispatch_async(dispatch_get_global_queue…" runs the block in a background thread. See this answer for a good explanation of that method: https://stackoverflow.com/a/12693409/215821
Here is my viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL),
^(void) {
[self doStuffInBackground];
});
}
The doStuffInBackground method is running in the background at this point, so you can just use NSURLConnection synchronously. In my example here, the method loops making network calls until presumably some other code sets backgroundStuffShouldRun = false. A network call is made with a 10 second timeout. After the call, I'm updating a UI label just to show progress. Note that the UI update is performed with "dispatch_async(dispatch_get_main_queue()…". This runs the UI update on the UI thread, as required.
One potential issue with this background work: there isn't a way to cancel the http request itself. But, with a 10 second timeout, you'd be waiting a max of 10 seconds for the thread to abort itself after an outsider (likely some event in your UI) sets backgroundStuffShouldRun = false.
- (void)doStuffInBackground
{
while (backgroundStuffShouldRun) {
// prepare for network call...
NSURL* url = [[NSURL alloc] initWithString:#"http://maps.google.com/maps/geo"];
// set a 10 second timeout on the request
NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLCacheStorageAllowed timeoutInterval:10];
NSError* error = nil;
NSURLResponse *response = nil;
// make the request
NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// were we asked to stop the background processing?
if (!backgroundStuffShouldRun) {
return;
}
// process response...
NSString* status = #"Success";
if (error) {
if (error.code == NSURLErrorTimedOut) {
// handle timeout...
status = #"Timed out";
}
else {
// handle other errors...
status = #"Other error";
}
}
else {
// success, handle the response body
NSString *dataAsString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", dataAsString);
}
// update the UI with our status
dispatch_async(dispatch_get_main_queue(), ^{
[statusLabel setText:[NSString stringWithFormat:#"completed network call %d, status = %#", callCount, status]];
});
callCount++;
sleep(1); // 1 second breather. not necessary, but good idea for testing
}
}

On iOS, can you make a synchronous network request (but not on the main thread) and still get progress callbacks (on a separate, non-main thread)?

On iOS, can you make a synchronous network request (off the main thread) and get progress callbacks (on a separate, non-main thread)?
I have have a serial (one-operation-at-a-time) background queue that runs all of time-consuming jobs that don't need to finish right now. I do want to show progress for the download jobs though. It doesn't look like you can instantiate an NSURLConnection and configure a delegate, start synchronous connection, and then get progress callbacks.
Is there a way to make a synchronous request on that background queue (synchronous in that the job behind it doesn't start until its done), and still get setProgress: callbacks which could be sent to update a progressbar? (Callbacks would have to be on a different queue thread, since my serial queue's thread is blocked until the request is finished.)
Apple's docs for NSURLConnection say that the synchronous request is actually built on top of the asynchronous behind the scenes. Do I have to re-implement that? I need a way to block a thread until the request finishes/fails. The best leads I have so far are NSOperationQueue's waitUntilFinished method, but I don't want to start async and continually poll on the synchronous method.
NSURLConnection Discussion
A synchronous load is built on top of the asynchronous loading code made available by the class. The calling thread is blocked while the asynchronous loading system performs the URL load on a thread spawned specifically for this load request. No special threading or run loop configuration is necessary in the calling thread in order to perform a synchronous load.
Reimplementing a synchronous request on top of an asynchronous request is not that hard. You just need to manually spin the thread's run loop until you see that the request has finished (or failed). Here's a rough example:
NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:queryURL];
// Start the download and wait until it finishes
self.downloadFinished = NO;
self.downloader = [NSURLConnection connectionWithRequest:downloadRequest delegate:self];
while (!self.isDownloadFinished)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
Here are the relevant NSURLConnectionDelegate methods:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
{
self.downloadFinished = YES;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
{
NSLog(#"An error occurred: %#", error);
self.receivedData = nil;
self.downloadFinished = YES;
}

Serializing NSURLConnection Requests (iOS) - Use Synchronous Request?

I'm looping through a list of dates and making a request to a web server for each date in the list.
I would like each date to be processed completely before the subsequent request is sent to the server. To do this, I have set up a serial dispatch queue using GCD. Each time through the date loop, a block is added to the queue.
The problem I am having is that my NSURLConnection is set up using the standard asynchronous call. This results in requests not blocking any subsequent requests. They are thus overrunning each other.
My question: Is this a case where it would make sense for me to use the synchronous NSURLConnection (within the dispatch queue) or is there some other way to make it work using the standard asynchronous call?
There are number of ways to do this. Whatever method you choose, starting the connection needs to be tied to completion of your processing task.
In each block you add to your serial queue, use a synchronous request. This is probably the quickest solution given your current implementation as long as you're ok with the limited error handling of a synchronous request.
Don't use a serial queue. Start the first asynchronous connection and process the response. When processing is complete start the next asynchronous connection. Rinse and repeat.
I think that using the synchronous NSURLConnection API is a fine idea. You have a few other options. One would be to write a wrapper object around NSURLConnection that used the asynchronous NSURLConnection APIs, so you get the nice information that the asynchronous API callbacks provide, including download progress, you can easily continue to update your UI while the request is happening, but which presents its own synchronous method for doing whatever it is you need to do. Essentially, something like:
#implementation MyURLConnectionWrapper
- (BOOL)sendRequestWithError:(NSError **)error
{
error = error ? error : &(NSError *){ nil };
self.finishedLoading = NO;
self.connectionError = nil;
self.urlConnection = [][NSURLConnection alloc] init...]
while (!self.finishedLoading)
{
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]];
}
if (self.connectionError != nil)
{
*error = self.connectionError;
return NO;
}
return YES;
}
#end
(This is all typed off the top of my head, and is heavily abbreviated, but should give you the basic idea.)
You could also do something like fire off each request in the completion delegate method for the previous one, forgoing the use of a serial dispatch queue altogether:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
{
[self sendNextRequest];
}
Either way, you need to think about how to handle connection errors appropriately. I've used both approaches in different places with good success.

Resources