9

I am trying to pull some data from my local node server. The server is getting the get request and logging it, but for some reason my iOS app will not execute any of the code that I have in the completion handler. Here is the code:

- (IBAction) buttonPressed{
NSURL *url = [NSURL URLWithString:@"http://127.0.0.1:3000/"];
NSURLSessionDataTask *dataTask =
[self.session dataTaskWithURL:url
          completionHandler:^(NSData *data,
                              NSURLResponse *response,
                              NSError *error){
              nameLabel.text = @"yay!";
              /*
              if (!error){
                  nameLabel.text = @"noerr";
                  NSHTTPURLResponse *httpResp = (NSHTTPURLResponse *)response;
                  if (httpResp.statusCode == 200){
                      NSError *jsonErr;

                      NSDictionary *usersJSON =
                      [NSJSONSerialization JSONObjectWithData:data
                                                      options:NSJSONReadingAllowFragments
                                                        error:&jsonErr];

                      if (!jsonErr){
                         // nameLabel.text = usersJSON[@"username"];
                          nameLabel.text = @"nojerr";

                      }
                      else{
                          nameLabel.text = @"jsonErr";
                      }
                  }
              }
              else{
                  nameLabel.text = @"Err";
              }
               */
          }];
[dataTask resume];

}

When the program is run, the nameLabel is not changed to "yay". However if I try to change the nameLabel before the NSURLSessionDataTask line, it changes.

Frankie Nwafili
  • 165
  • 1
  • 7
  • Please select answer below if it helped you. Feel free to comment if you still have questions related to the answer. – wigging Oct 21 '13 at 22:12

2 Answers2

14

NSURLSessionDataTask runs in a background thread. To update anything in the user interface such as labels, buttons, table views, etc, you must do so on the main thread. If you want to update the label text from the completionHandler block then you need to update the label in the main thread like so:

dispatch_sync(dispatch_get_main_queue(), ^{
  nameLabel.text = @"yay!";
});
duan
  • 8,515
  • 3
  • 48
  • 70
wigging
  • 8,492
  • 12
  • 75
  • 117
13

try this magic:

static NSURLSession* sharedSessionMainQueue = nil;
if(!sharedSessionMainQueue){
    sharedSessionMainQueue = [NSURLSession sessionWithConfiguration:nil delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
}

NSURLSessionDataTask *dataTask =
[sharedSessionMainQueue dataTaskWithURL:url completionHandler:^(NSData *data,
                              NSURLResponse *response,
                              NSError *error){
    //now will be on main thread
}];
[dataTask resume];

This gives you the original behavior of NSURLConnection with the completing handler on the main thread so you are safe to update the UI. However, say you would like to parse the download or do some heavy processing, in that case you might benefit from the completion handler on the operation queue's background thread and then using dispatch_sync to the main thread as a final step.

malhal
  • 26,330
  • 7
  • 115
  • 133
  • 2
    +1 - This is a much better way, as you can then *optionally* choose to run additional tasks on background threads, and assume everything is run on the main thread. – Jonathan King Sep 27 '14 at 21:30
  • Hmm, Jonathon if you use this method EVERYTHING in the completion block, will be (can only be) on the main thread. – Fattie Sep 29 '14 at 08:03
  • Joe he means create background threads from the main thread completion block if required. – malhal Oct 05 '14 at 15:09