Question

I have an URL, which when copied into a browser, displays an image. My function is supposed to download the image asynchronously.

- (UIImage *)downloadImage:(NSString *)imageURL
{
    NSError *error = nil;
    NSURL *urlString = [NSURL URLWithString:imageURL];
    NSData *data = [NSData dataWithContentsOfURL:urlString options:NSDataReadingUncached error:&error];
    __block UIImage *image;

    if (!error) {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(queue, ^{
            image = [UIImage imageWithData:data];
        });
        return image;
    } else {
        NSLog(@"%@", [error localizedDescription]);
    }
    return nil;
}

When I try to display the image in an UIImageView I get no errors, no nothing. I have NSLogged out both data and the imageURL passed in, and none of those are empty.

Any suggestions?

Was it helpful?

Solution

By calling dispatch_async, you're scheduling that work to happen later. Your function exits with nil before that work is done. You'll want to add a callback block to your function or make it block until you receive and process the image data.

Here is an example of a function with a block callback and how to use it.

- (void)downloadImageAtURL:(NSString *)imageURL withHandler:(void(^)(UIImage *image))handler
{
    NSURL *urlString = [NSURL URLWithString:imageURL];

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSError *error = nil;
        NSData *data = [NSData dataWithContentsOfURL:urlString options:NSDataReadingUncached error:&error];
        if (!error) {
            UIImage *downloadedImage = [UIImage imageWithData:data];
            handler(downloadedImage); // pass back the image in a block
        } else {
            NSLog(@"%@", [error localizedDescription]);
            handler(nil); // pass back nil in the block
        }
    });
}



- (void)keyboardDidShow:(NSNotification *)aNotification {
    [self downloadImageAtURL:@"" withHandler:^(UIImage *image) {
        if (image) {
            // display
        } else {
            // handle probelm
        }
    }];
}

OTHER TIPS

The call to dataWithContentsOfURL:options:error: needs to be within the dispatch_queue block for it to be asynchronous. Any changes to the UI need to be in the mainThread. It should look something like this:

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
    dispatch_async(queue, ^(void) {

        NSData *imageData = [NSData dataWithContentsOfURL:imageURL];

        UIImage* image = [[UIImage alloc] initWithData:imageData];
        if (image) {
             dispatch_async(dispatch_get_main_queue(), ^{
                    //load image into UIImageView
                 }
             });
         }
    });

You are not downloading the image asynchronously. Moreover, a method that is supposed to return a value in an async way cannot return that value through the method return value, but it should return it using a block.

You can try to do something like this:

- (void)downloadImage:(NSString *)imageURL onComplete:(void (^)(UIImage *, NSError * error))onComplete
{
    NSURL *urlString = [NSURL URLWithString:imageURL];

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSError *error = nil;
        NSData *data = [NSData dataWithContentsOfURL:urlString options:NSDataReadingUncached error:&error];
        image = [UIImage imageWithData:data];
        if (onComplete) {
          // Keep in mind that onComplete block will be called on a background thread.
          // If you need to use it on UIImageView, you must set it on main thread.
          onComplete(image, error); 
        }            
    });
}

Then, when you need to set the UIImageView image:

__weak typeof(self)selfB = self; // Better to use a weak reference inside blocks to avoid retain cycles
[self downloadImage:myURLString onComplete:^(UIImage * image, NSError * error) {
    dispatch_async(dispatch_get_main_queue(), ^{ // As you can see, we use main thread for UI updates
        selfB.imageView.image = image;
    });
}];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top