views:

1168

answers:

1

I have a folder on my remote server that has a few .png files in it. I want to download these from within my app and store them in the apps 'Documents' folder. How can I do this?

+8  A: 

The easy way is to use NSData's convenience methods initWithContentOfURL: and writeToFile:atomically: to get the data and write it out, respectively. Keep in mind this is synchronous and will block whatever thread you execute it on until the fetch and write are complete.

For example:

// Create and escape the URL for the fetch
NSString *URLString = @"http://example.com/example.png";
NSURL *URL = [NSURL URLWithString:
              [URLString stringByAddingPercentEscapesUsingEncoding:
                          NSASCIIStringEncoding]];

// Do the fetch - blocks!
NSData *imageData = [NSData dataWithContentsOfURL:URL];
if(imageData == nil) {
    // Error - handle appropriately
}

// Do the write
NSString *filePath = [[self documentsDirectory] 
                      stringByAppendingPathComponent:@"image.png"];
[imageData writeToFile:filePath atomically:YES];

Where the documentsDirectory method is stolen shamelessly from this question:

- (NSString *)documentsDirectory {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask, YES);
    return [paths objectAtIndex:0];
}

However, unless you intend to thread it yourself this will stop UI activity while the file downloads. You may instead want to look into NSURLConnection and its delegate - it downloads in the background and notifies a delegate about data downloaded asynchronously, so you can build up an instance of NSMutableData then just write it out when the connection's done. Your delegate might contain methods like:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    // Append the data to some preexisting @property NSMutableData *dataAccumulator;
    [self.dataAccumulator appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // Do the write
    NSString *filePath = [[self documentsDirectory] 
                          stringByAppendingPathComponent:@"image.png"];
    [imageData writeToFile:filePath atomically:YES];
}

The little details, like declaring the dataAccumulator and handling errors, are left to the reader :)

The important documents:

Tim
Thanks! Ummm...Synchronous? Does this mean that I had better use a "progress wheel/bar" while the work is being done?
RexOnRoids
Synchronous means that all the activity in your program (well, the main thread) will completely stop until the download finishes. This means that the UI will appear frozen to the user, and spinners won't even start animating until after the download is complete (rendering them pretty useless). The second method, an asynchronous download, lets your program keep working in the foreground while it downloads in the background. Either way, yes, you should use some kind of progress indicator.
Tim