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!

Related posts