74

Two part question

Part one: I am trying to create an ASynchronous request to my database. I am currently doing it Synchronously however I want to learn both ways to better my understanding of whats going on.

Currently I have set up my Synchronous call like this.

- (IBAction)setRequestString:(NSString *)string
{
    //Set database address
    NSMutableString *databaseURL = [[NSMutableString alloc] initWithString:@"http://127.0.0.1:8778/instacodeData/"]; // imac development

    //PHP file name is being set from the parent view
    [databaseURL appendString:string];

    //call ASIHTTP delegates (Used to connect to database)
    NSURL *url = [NSURL URLWithString:databaseURL];

    //SynchronousRequest to grab the data
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSError *error;
    NSURLResponse *response;

    NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    if (!result) {
        //Display error message here
        NSLog(@"Error");
    } else {

        //TODO: set up stuff that needs to work on the data here.
        NSString* newStr = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
        NSLog(@"%@", newStr);
    }
}

I think what I need to do is replace the call

NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

with the ASynchronous version

sendAsynchronousRequest:queue:completionHandler:

however I'm not sure what to pass to queue or completionHandler... Any examples/solutions would be greatly appreciated.

Part two: I have been reading about multi tasking and I would like to support it by making sure my connection requests complete if there is an interrupt. I have been following this example

In it it explains how to gain more time if an interrupt occurs, I understand what its doing.. but not how to apply it to this connection? if you have any examples/tutorials to help me figure out how to apply it that would be awesome!

C.Johns
  • 10,185
  • 20
  • 102
  • 156

6 Answers6

114

PArt 1:

NSURL *url = [NSURL URLWithString:urlString];

NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
    if ([data length] > 0 && error == nil)
        [delegate receivedData:data];
    else if ([data length] == 0 && error == nil)
        [delegate emptyReply];
    else if (error != nil && error.code == ERROR_CODE_TIMEOUT)
        [delegate timedOut];
    else if (error != nil)
        [delegate downloadError:error];
}];
mbh
  • 3,302
  • 2
  • 22
  • 24
  • for the delegate calls.. what delegate am I supposed to include in my header file? – C.Johns Feb 14 '12 at 02:43
  • You should delegate it to self – mbh Feb 14 '12 at 02:44
  • Ah okay, so I have declared the delegate in my header file (myviewcontroller *delegate) and then synthesized it in my .m file. but im still getting an error on each of those delegate calls. I'm supposing I have to call those methods – C.Johns Feb 14 '12 at 02:55
  • 2
    Those methods are in your class. You can rename those methods as you like. Or not have them. but put the code in the if then else itself. or you can call like this [self receivedData:data]; – mbh Feb 14 '12 at 03:37
  • okay.. I'm pretty sure I get it.. minus setting up the delegate but going to look at that abit later on. thanks for the help. – C.Johns Feb 14 '12 at 03:42
  • 12
    I believe `NSURLErrorTimedOut` is the accepted value over `ERROR_CODE_TIMEOUT`. – Joe Masilotti Aug 04 '12 at 22:17
  • 2
    Note: `sendAsynchronousRequest:urlRequest queue:queue completionHandler:` has been deprecated in iOS 9 – Richard Sep 09 '15 at 14:12
38

Here is a sample:

NSString *urlAsString = @"http://www.cnn.com";
NSURL *url = [NSURL URLWithString:urlAsString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];


[NSURLConnection
         sendAsynchronousRequest:urlRequest
         queue:[[NSOperationQueue alloc] init]
         completionHandler:^(NSURLResponse *response,
                             NSData *data,
                             NSError *error) 
        {

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

                        // DO YOUR WORK HERE

         }
         else if ([data length] == 0 && error == nil)
         {
             NSLog(@"Nothing was downloaded.");
         }
         else if (error != nil){
             NSLog(@"Error = %@", error);
         }

     }];
Flea
  • 11,176
  • 6
  • 72
  • 83
  • 1
    Aren't you leaking the NSOperationQueue object? It looks like it. Or do you use ARC and just didn't mention it? – Lukas Petr Dec 07 '12 at 21:10
  • 3
    I am using ARC, just didn't mention it. Thanks for the question! – Flea Dec 09 '12 at 17:36
  • Should the line 'sendAsynchronousRequest:request' be 'sendAsynchronousRequest:urlRequest'? – Mick Apr 17 '13 at 21:37
  • @Flea Please see my related question here. http://stackoverflow.com/questions/24857919/how-can-i-receive-my-data-in-pieces-with-using-nsurlconnections-sendasynchronou – Tyler Pfaff Jul 21 '14 at 05:08
25

For the queue param, try this magic:

[NSOperationQueue mainQueue]

This works great if you are updating the UI on completion of the request since main queue is the main thread. It essentially gives you the previous behavior of NSURLConnection. If however you plan on writing to file or decompressing then you can complete on a background queue and then dispatch async back to the main queue for the UI updates.

malhal
  • 26,330
  • 7
  • 115
  • 133
  • 1
    @MaciejSwic no, as the documentation states: "queue: The operation queue to which the handler block is dispatched when the request completes or failed." So it's the completion handler that gets run on the main thread, which may or may not be a good thing depending on what you want to do in there. – Algorithm and Blues Jun 10 '15 at 14:44
  • Well if you are updating UI it's what you want. And there's no point in adding the complication of running the delegate on a background thread if it is not necessary. Using the main queue gives the same behavior as the old NSURLConnection which in most cases should be fine. – malhal Apr 10 '16 at 19:08
9

I have been working on a similar problem, I posted this question and got a clear answer here, I hope that helps with Part 2.

For part 1 what the others mentioned here are good but you need to add another check (I have modified an answer below). It is possible that your request will return say a 404 Error (page not found) in which case you will not get and error and data will be > 0. The 200 is a good response, you could also check the StatusCode for 404 or whatever.

     [NSURLConnection
     sendAsynchronousRequest:urlRequest
     queue:[[NSOperationQueue alloc] init]
     completionHandler:^(NSURLResponse *response,
                         NSData *data,
                         NSError *error) 
    {
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
     if ([data length] >0 && error == nil && [httpResponse statusCode] == 200)
     {

                    // DO YOUR WORK HERE

     }
Community
  • 1
  • 1
Recycled Steel
  • 2,272
  • 3
  • 30
  • 35
4

Since sendAsynchronousRequest:urlRequest queue:queue completionHandler: has been deprecated in iOS 9, and it will suggest to use NSURLSession's -dataTaskWithRequest:completionHandler: instead. It is available since iOS 7 and later.

Original:

 NSURL *URL = [NSURL URLWithString:@"http://example.com"];
 NSURLRequest *request = [NSURLRequest requestWithURL:URL];

 [NSURLConnection sendAsynchronousRequest:request
                                    queue:[NSOperationQueue mainQueue]
                        completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
     // ...
 }];

By NSURLSession:

 NSURL *URL = [NSURL URLWithString:@"http://example.com"];
 NSURLRequest *request = [NSURLRequest requestWithURL:URL];

 NSURLSession *session = [NSURLSession sharedSession];
 NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                         completionHandler:
     ^(NSData *data, NSURLResponse *response, NSError *error) {
         // ...
     }];

 [task resume];
AechoLiu
  • 17,522
  • 9
  • 100
  • 118
2

sendAsynchronousRequest has been deprecated in Swift. Move to dataTaskWithRequest, luckily it is used pretty much the same way.

if let url = NSURL(string:"http://your_url") {

    let request:NSURLRequest = NSURLRequest(URL: url)
    let config = NSURLSessionConfiguration.defaultSessionConfiguration()
    let session = NSURLSession(configuration: config)

    let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in

    });

    task.resume()
}
Aziz Shaikh
  • 16,245
  • 11
  • 62
  • 79