2

I have some difficulties to set up the correct configuration relative to sendAsynchronousRequest:queue:completionHandler: method (NSURLConnection class).

My scenario is the following:

I set up a singleton class that manages different NSURLConnections. This singleton istance has a NSOperation Queue (called downloadQueue) that makes a request to a web server and retrieves a string path (1). Once done, the path is used to download a file within a web server (2). Finally, when the file has been correctly downloaded, I need to update the UI (3).

I figured out only the first request: the one through which I'm able to download the path. Could you suggest me a way to perform the other two steps?

Few questions here:

  • the download queue (downloadQueue) is not the main one, is it possible to open a new NSURLConnection in that queue? In other words, is it correct? (See comments in code snippets)

  • if the previous question is correct, how can I grab the main queue and update the UI?

Here the code snippet I use to perform the first step where downloadQueue is an instance variable that can be obtain through accessor mehods (@property/@synthesized);

// initializing the queue...
downloadQueue = [[NSOperation alloc] init];    

// other code here...
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[self downloadQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

    if([data length] > 0 && error == nil) {

        // here the path (1)
        // how to perform a second connection?
        // what type of queue do I have to use?              
    }
}];
secretformula
  • 6,414
  • 3
  • 33
  • 56
Lorenzo B
  • 33,216
  • 24
  • 116
  • 190

2 Answers2

5

You're on the right track for performing your first download.

In the completion handler block after the first download, you're computing the URL that you'll need for a second download, right? Then you can perform that second download the same way: call +[NSURLConnection sendAsynchronousRequest:...] again with the new URL and the same queue. You can do this within the completion block for the first download.

To update the UI after the second download is done, switch to the main queue within the completion block for that download. You can do this with dispatch_async() or dispatch_sync() (in this case it doesn't matter which because you don't have further work to do on the download queue) and dispatch_get_main_queue(), or with -[NSOperationQueue addOperationWithBlock:] and +[NSOperationQueue mainQueue].

Your code should look something like this:

// init download queue
downloadQueue = [[NSOperationQueue alloc] init];    

// (1) first download to determine URL for second
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[self downloadQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    if([data length] > 0 && error == nil) {
        // set newURLRequest to something you get from the data, then...
        // (2) second download
        [NSURLConnection sendAsynchronousRequest:newURLRequest queue:[self downloadQueue] completionHandler:^(NSURLResponse *newResponse, NSData *newData, NSError *newError) {
            if([newData length] > 0 && newError == nil) {
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    // (3) update UI
                }];
            }
        }];
    }
}];
anthonylawson
  • 761
  • 9
  • 24
rickster
  • 124,678
  • 26
  • 272
  • 326
  • +1 for your support. Could you explain why I can use the same queue to perform the second call? thank you. – Lorenzo B Apr 18 '12 at 07:50
  • 1
    It's a queue: operations you put onto it execute in first in, first out order. So if you enqueue another operation during execution of the first (and after the main work -- the download -- from the first is complete), then the second operation will come after the first. Does that answer your question? (I'm not sure just what you're looking for in terms of "why does it work". It might be easier to answer if you said why do you think it wouldn't work.) – rickster Apr 18 '12 at 16:24
  • I have a similar problem, the difference is that in the first download I get lots of URLs, so I go trough a for loop and send multiple async requests, I tried to implement the code above but it doesn't work. What am I doing wrong? – Eduardo Mar 23 '13 at 16:54
  • @Edu: post more details in a new question and we'll see what we can do to help. – rickster Mar 25 '13 at 02:39
  • A NSOperationQueue is only serial if its maxConcurrentOperationCount is set to 1. By default NSOperationQueue is as concurrent as the dynamic system situation allows. – MacMark Apr 11 '13 at 19:55
1

For updating the ui, as far as I know, you have to do that on the main thread. The ui could be updated from other threads but those updates are not fully reliable. In an app that I put together that made request to a web service, I make use of dispatch_async() to get access to the main queue and then I update, in my case a table view, from that call.

dispatch_async(dispatch_get_main_queue(), ^{
    //block to be run on the main thread
    [self.tableView reloadData];
});

I hope this helps.

swallace
  • 408
  • 1
  • 4
  • 16
  • Thank you for your reply. The question I posted is about performing another request within *sendAsynchronousRequest:queue:completionHandler:*. In other words, I would like to know how to perform a chain of requests using the above method. Cheers – Lorenzo B Apr 18 '12 at 07:54