iOS Recipes - Matt Drance [65]
Explore the SimpleDownloadViewController class implementation provided with the SimpleDownloads test project, and note how few lines of code are needed to handle the actual download: nearly all the work is limited to manipulation of the user interface. This abstraction of NSURLConnection allows us to keep our controller code clean and focused on its important, higher-level tasks. We can use it to acquire RSS feeds, get JSON responses from web services, and even download media.
Note that for large downloads, you’ll want to wire this up to an NSInputStream and write the data to disk as it comes down in order to avoid memory pressure. Downloading a 500MB video directly to an NSData object in memory, for example, will inevitably crash your app.
Recipe 33 Format a Simple HTTP POST
Problem
New web service APIs pop up every day. Sooner or later one of them will require an HTTP POST instead of a plain old GET. How do you format such a request? How do you make it easy for every project that needs it?
Solution
If you’ve used (or coded for) the Web, you’re no stranger to POST methods. When filling out a form on a web page with basic drop-downs and text fields, followed by some kind of “submit” action, that form probably produces a “form” or “URL encoded” POST. On the Web, however, the browser does the dirty work. What’s a Cocoa programmer to do?
The good news is we can do a POST with the same NSURLConnection API we’ve used for other more basic web requests (usually GET methods). Submitting a POST involves a few simple additions:
Setting the request method to POST
Identifying the type of POST we’re submitting
Adding form data to the request body
The connection itself is unchanged; it’s the supporting NSURLRequest that needs modification. To do this, we’ll write a subclass of NSMutableURLRequest, PRPFormEncodedPOSTRequest, which supports a dictionary of parameters to be used in the POST method. We subclass NSMutableURLRequest so we can add the form data to the HTTP body.
BasicHTTPPost/PRPFormEncodedPOSTRequest.h
@interface PRPFormEncodedPOSTRequest : NSMutableURLRequest {}
+ (id)requestWithURL:(NSURL *)url formParameters:(NSDictionary *)params;
- (id)initWithURL:(NSURL *)url formParameters:(NSDictionary *)params;
- (void)setFormParameters:(NSDictionary *)params;
@end
The first two steps outlined earlier are simple: set the HTTP method to POST, and set the content type to application/x-www-form-urlencoded. We can do this work at initialization time.
BasicHTTPPost/PRPFormEncodedPOSTRequest.m
- (id)initWithURL:(NSURL *)url formParameters:(NSDictionary *)params {
if ((self = [super initWithURL:url])) {
[self setHTTPMethod:@"POST"];
[self setValue:@"application/x-www-form-urlencoded"
forHTTPHeaderField:@"Content-Type"];
[self setFormParameters:params];
}
return self;
}
That ‑setFormParameters: method is the remaining piece of the puzzle. In the case of our ‑initWithURL:formParameters: method, the parameters are passed at creation time, but we could also set them after creating the object, so it’s broken out into a separate method.
Form parameters look a lot like a URL query string, but instead of being appended to the URL, they’re placed in the HTTP body. So, if we are submitting a form that includes your name and age, the composed body string might look like this:
name=Lucas+Drance&age=1.5
Each parameter’s name and value are connected with an equal sign (=), and the pairs are connected with an ampersand (&). In this recipe, we use %20 to escape spaces, rather than the plus (+) characters specified by RFC 2616. In practice, many servers accept either, but unfortunately many popular web services don’t support the plus sign. Always test your project with whitespace content to make sure the server you’re talking to is behaving as expected.
So, given a set of name-value pairs, our Cocoa code needs to tie them together,