Code, Code, Revolution!
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!
With this blog I try to provide useful tips and solutions for programming .NET, Objective-C and more. My name is Björn Sållarp, and I love writing code.
Josh
October 13th, 2008 at 3:38 am
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.
Björn Sållarp
October 13th, 2008 at 6:41 am
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!
Josh
October 13th, 2008 at 9:31 am
OK, thank you.
iPhone - Downloading an image asynchronously. Revisited!
November 2nd, 2008 at 1:28 am
[...] – Downloading an image asynchronously. Revisited! Nov.02, 2008 in Uncategorized, iPhone In a previous post I wrote about downloading an image asynchronously. As my work has progressed with my app (which is [...]
Eric
November 30th, 2008 at 3:37 am
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.
Björn Sållarp
December 1st, 2008 at 10:35 pm
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
blogg
February 12th, 2009 at 1:26 am
Very nice information. Thanks for this.
Franck
February 23rd, 2009 at 10:15 pm
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
Teerasej
October 8th, 2009 at 6:35 am
Thank you for this article. Your writing is very good and easy understand. I think this is the best one.
Greg Maletic
December 10th, 2009 at 2:43 am
>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.