NSData
has always had a very convenient method called +dataWithContentsOfURL:options:error:
. While convenient, it also blocks execution of the current thread, which meant it was basically useless for production code (Ignoring NSOperation
). I used this method so infrequently, I completely forgot that it existed. Until recently.
The way I've been grabbing data from the tubes is the standard NSURLConnectionDelegate
approach: Write a download class that handles the various NSURLConnectionDelegate
methods, gradually build up some data, handle errors, etc. I'll usually make this generic enough to be reused for as many requests as possible.
Say my typical downloader class runs somewhere in the ballpark of 100 lines. That's 100 lines to do asynchronously what NSData
can do synchronously in one line. For more complexity, that downloader class needs a delegate protocol of its own to communicate completion and errors to its owner, and the owner needs to implement that protocol in some fashion.
Now, enter Grand Central Dispatch, and I can do something as fantastically simple as:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSData* data = [NSData dataWithContentsOfURL:someURL];
// Process data, also async...
dispatch_async(dispatch_get_main_queue(), ^(void) {
// Back to the main thread for UI updates, etc.
});
});
And I can throw that sucker in anywhere I want, right in-line. No need for a download class, no need to handle connection delegate methods: Easy async data in just a few lines. The disparity between this approach and my pre-GCD approach is of a magnitude great enough to trigger the Too Good to be True Alarm.
Thus, my question: Are there any caveats to using NSData
+ GCD for simple data download tasks instead of NSURLConnection
(Assuming I don't care about things like download progress)?
You are losing a lot of functionality here:
and there's probably more I guess.
The right approach for that is to create a class than manages the download.
See my own OHURLLoader
class for example, which is simple and I made the API to be easy to use with blocks:
NSURL* url = ...
NSURLRequest* req = [NSURLRequest requestWithURL:url];
OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
[loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
NSLog(@"Download of %@ done (statusCode:%d)",url,httpStatusCode);
if (httpStatusCode == 200) {
NSLog(%@"Received string: %@", loader.receivedString); // receivedString is a commodity getter that interpret receivedData using the TextEncoding specified in the HTTP response
} else {
NSLog(@"HTTP Status code: %d",httpStatusCode); // Log unexpected status code
}
} errorHandler:^(NSError *error) {
NSLog(@"Error while downloading %@: %@",url,error);
}];
See the README file and sample project on github for more info.
This way: