iPhone development – Downloading an image asynchronously

NOTE! I’ve updated the InternetImage class, read about it in this post: Downloading an image asynchronously. Revisited!, you will also find the revised code there.

In a recent post I showed you how to round of edges of an image. But how can you go about downloading images from the internet in your iPhone app? There are two possible ways of doing it: synchronously and asynchronously. Which way is best depends on what you are trying to do but it’s generally better to download asynchronously as the user probably doesn’t want to wait for an image to show up before being able to interact with the application. Downloading an image synchronously is well easy:

UIImage *myImage = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:strImageUrl]]];

Downloading an image asynchronously requires a little more work. I created a class to handle the download for me. Instead of posting the entire class I’ll explain it a little bit and provide the actual code files at the end.

Downloading an image, or anything else for that matter, asynchronously from the web requires the use of a NSURLRequest and NSURLConnection. Supply the NSURLRequest to the NSURLConnection and provide a delegate for the connection. The delegate is self, because i want my class to handle the events from the connection. We also need NSMutableData to hold the downloaded bytes.

NSURLRequest *imageRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:ImageUrl]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0];
 
NSURLConnection *imageConnection = [[NSURLConnection alloc] initWithRequest:imageRequest delegate:self];
 
if(imageConnection)
{
m_ImageRequestData = [[NSMutableData data] retain];
}

NSURLConnection has four interesting events that we want to handle:
didReceiveResponse: occurs when the connection has been established and we’re ready to receive data. I use this event to clear my NSMutableData variable

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response

didReceiveData: occurs for every chunk of downloaded data. If the file is bigger than a few bytes this even will trigger more than once. When this even occur i store the data chunk in my NSMutableData variable.

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

didFailWithError: Pretty self explanatory, this event triggers when there’s some kind of error.

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error

connectionDidFinishLoading: triggers when all data has been downloaded for the request. This is where i init my image variable and release everything that’s not need anymore.

- (void)connectionDidFinishLoading:(NSURLConnection *)connection

For my InternetImage class I wanted it to handle everything for me and tell me when it’s done. Therefor I created an init method where i can supply the URL in NSString format directly when i instanciate the object. To download the image I just call downloadImage and supply a delegate which is the object that will receive an event when the image is downloaded and ready for use. For this I implement my own delegate which is pretty simple. I declare the interface of the delegate:

@interface NSObject (InternetImageDelegate)
- (void)internetImageReady:(InternetImage*)internetImage;
@end

A class that wants to use the InternetImage class must implement this interface. As you can see the delegate method supplies the InternetImage instance, meaning the InternetImage class returns a pointer to itself. Read more about delegates at Apple Developer Connection

This is an example of how to download an image with my InternetImage class:

- (void) DownloadImageFromInternet:(NSString*) urlToImage
{
 
InternetImage *imgDownloader = [[InternetImage alloc] initWithUrl:urlToImage];
[imgDownloader downloadImage:self];
}
 
-(void)internetImageReady:(InternetImage*)internetImage
{
[mainImageView setImage:[ImageManipulator makeRoundCornerImage:internetImage.Image : 8 : 8]];
[internetImage release];
}

In the example above, the calling class would have to implement the InternetImageDelegate interface, as I explained earlier. When the InternetImage is done downloading I display the image in an UIImageView and I also round off the image corners which I wrote about earlier. Then I release the InternetImage object altogether.

Go here to find an updated post and the sample code: Downloading an image asynchronously. Revisited!

10 thoughts on “iPhone development – Downloading an image asynchronously

  1. Hi, thanks for the tutorial. How do I save this downloaded image to the memory of the phone? also, how would I save it if it was another file type? Thanks again.

  2. Hi Josh! The iPhone supports PNG, TIFF, JPEG, GIF, BMP, CUR and XBM image formats. The dev docs strongly recommend PNG over the other formate. But of course you can download other types than images. The concept is the same for any file you download, but store the data in the file system instead of inserting it into an image.
    I haven’t worked with storing files on the phone but you can find documentation on the file system on iPhone dev center here: http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/LowLevelFileMgmt/LowLevelFileMgmt.html

    Good luck!

  3. Hi,
    Thanks for the internetimage class. It is good for downloading a single image.
    How to use the internetimage class to download 3 images one by one (not need in sequence at all). It seems the delegate cannot identify which image it is downloading.

  4. Hi Eric!

    I’m glad you like the class. Be sure you’ve checked out the revised class here: http://blog.sallarp.com/2008/11/asynch-uiimage-revisited/

    The new intenetimage class is much more stable. You can solve your problem in a number of ways. The easiest is to just add a property on the InternetImage class that can work as an identifier. Another neat way to solve it would be to add a pointer (id) property on the InternetImage that you set to the UIImage (or whatever) you wish to set the image to once it’s been downloaded.

    Happy coding!
    // Björn Sållarp

  5. Hi Björn, nice article!

    Like Eric, I’m wondering about the way to use your class with many images?

    Your idea consisted in adding a property refering to the destination UIImage but how would you use it ?

    Thanks a lot for your answer.

    Cheers,
    –Franck

  6. >Downloading an image, or anything else for that matter, asynchronously from the web requires the use of a NSURLRequest and NSURLConnection.

    Why do you say this? Why can’t you just wrap [NSData dataWithContentsOfURL:] in an NSOperation and implement your own callback? Is there a reason why that is a bad idea?

    Thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>