An NSData object represents a buffer of bytes. For example, if you fetch some data from a URL, you get an instance of NSData. And you can ask an NSData to write itself to a file. Create a new Foundation Command Line Tool named ImageFetch that fetches an image from the Google website into an instance of NSData. Then ask the NSData to write its buffer of bytes to a file:
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { @autoreleasepool { NSURL *url = [NSURL URLWithString: @"http://www.google.com/images/logos/ps_logo2.png"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSError *error = nil; NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:&error]; if (!data) { NSLog(@"fetch failed: %@", [error localizedDescription]); return 1; } NSLog(@"The file is %lu bytes", [data length]); BOOL written = [data writeToFile:@"/tmp/google.png" options:0 error:&error]; if (!written) { NSLog(@"write failed: %@", [error localizedDescription]); return 1; } NSLog(@"Success!"); } return 0; }
Build and run the program. Open the resulting image file in Preview.
Note that the writeToFile:options:error: method takes a number that indicates options to be used in the saving process. The most common option is NSDataWritingAtomic. Let’s say that you’ve already fetched an image, and you’re just re-fetching and replacing it with a newer version. While the new image is being written, the power goes off. A half-written file is worthless. In cases where a half-written file is worse than no file at all, you can make the writing atomic. Add this option:
NSLog(@"The file is %lu bytes", [data length]); BOOL written = [data writeToFile:@"/tmp/google.png" options:NSDataWritingAtomic error:&error]; if (!written) { NSLog(@"write failed: %@", [error localizedDescription]); return 1; }
Now, the data will be written out to a temporary file, and, when the writing is done, the file is renamed the correct name. This way, you either get the whole file or nothing.