Question

In my view I attempt to display some weather related info. I use

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    Make NSURL and other related things
    Send NSURLRequest
    Set 2 UILabels to data
    NSLog(Show data retrieved)
});

For some reason, I see the NSLog line roughly 15-45 seconds before the uilabel changes to the new text. I am fairly new to Obj-C and a lot of my code comes from using tutorial so I dont have the greatest understanding of the dispatch_async method. Any thoughts or suggestions would be appreciated.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSURLResponse *response = nil;
        NSError *error = nil;
        NSData *respData = nil;
        NSURLRequest *forecastRequest = [NSURLRequest requestWithURL:currentForecastUrl];
        respData = [NSURLConnection sendSynchronousRequest:forecastRequest returningResponse:&response error:&error];
        [_responseData appendData:respData];
        NSLog(@"Response: %@", response);
        NSLog(@"Error: %@", error);
        id JSON = [_responseData yajl_JSON];
        currentForecast = [[NSArray alloc]initWithObjects:[JSON objectForKey:@"temperature"],[JSON objectForKey:@"feelsLike"],nil];

        [_temperatureLabel setText:[NSString stringWithFormat:@"%@",[JSON objectForKey:@"temperature"]]];
        [_tempDescriptionLabel setText:[NSString stringWithFormat:@"%@",[JSON objectForKey:@"desc"]]];
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:2.0];
        [_tempDescriptionLabel setAlpha:1];
        [_temperatureLabel setAlpha:1];
        [UIView commitAnimations];
        NSLog(@"Done");

        NSLog(@"JSON: %@", [JSON yajl_JSONStringWithOptions:YAJLGenOptionsBeautify indentString:@"  "]);
    });
Was it helpful?

Solution

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

is used for create a background thread, but the user interface should be updated in the Main Thread

like this:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

     //do stuff in background

     dispatch_async(dispatch_get_main_queue(), ^{

         // Update your UI
     });

});

OTHER TIPS

All the "use the main queue" answers are good. But please notice that NSURLConnection does all this for you.

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

    // this happens on the main thread because we passed mainQueue
}];

EDIT: Here's your code, except smaller and with modern syntax. It should do exactly the same job. For fun, cut and paste this in.

// it assumes the following variables in scope

NSURL *currentForecastUrl;
__block NSArray *currentForecast;
UILabel *_temperatureLabel, *_tempDescriptionLabel;

// start counting lines here
NSURLRequest *forecastRequest = [NSURLRequest requestWithURL:currentForecastUrl];
[NSURLConnection sendAsynchronousRequest:forecastRequest queue:[NSOperationQueue mainQueue]
    completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

    // I cheated a little on lines, because we really ought to check errors
    // in here.
    NSError *parseError;  // use native JSON parser
    id parse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&parseError];
    currentForecast = @[parse[@"temperature"], parse[@"feelsLike"]];  // note the array and dictionary literal syntax

    _temperatureLabel.text = parse[@"temperature"];   // if these are ivars, I reccomend using self.temperatureLabel, not _temp...
    _tempDescriptionLabel.text = parse[@"desc"];
    [UIView animateWithDuration:2.0 animations:^{     // use UIView animation block
        _temperatureLabel.alpha = 1.0;
        _tempDescriptionLabel.alpha = 1.0;
    }];
}];

Deleting code for me is the most pleasurable part of programming. The line of code that's easiest to read, executes the quickest and requires no testing is the line that isn't there.

Your problem is because you're trying to update the UILabels off the main thread. I've seen that exact symptom before, where updating UI elements off the main thread still updates them, but with a long delay. So any time you have a UI element that's behaving in this way, check to make sure it's executing on the main thread.

I am surprised this code works at all because you are updating the label on a background thread. You want to do something along these lines:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{
    //Make NSURL and other related things
    //Send NSURLRequest
    dispatch_async(dispatch_get_main_queue(), ^{
        // Set your UILabels
    });
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top