Quantcast
Channel: iDevBlogADay » Matt Martel
Viewing all articles
Browse latest Browse all 10

Simple HTTP Request Handling [Matt Martel]

$
0
0

It’s common for iPhone apps to need to make HTTP requests and receive results in either JSON or XML format. There are several excellent full-featured tools ( ASIHTTPRequest, RestKit, etc.) to help you with this task, but sometimes all you need is NSURLConnection. NSURLConnection is a simple class that provides easy to use high-level asynchronous request/response handling.  I’ll describe a simple wrapper I’ve been using to make it easy to handle multiple requests easily.

When using NSURLConnection there are only a couple of things to deal with. Unless you are using the synchronous version (not recommended) you will typically set up an object, such as your application delegate or a view controller, as a delegate to receive the NSURLConnectionDelegate callbacks. The main delegate message you need to handle are connection:didFailWithError:, connection:didReceiveData:, and connectionDidFinishLoading:.

One potential issue you face here is using a single object as delegate for multiple connections. Then your delegate message handlers will become messy as they attempt to determine which response goes with which request, etc. Now instead consider creating a utility class with just two public methods, like this:

@interface myConnectionController : NSObject {
    NSMutableData* receivedData;
}
@property (nonatomic, retain) id connectionDelegate;
@property (nonatomic) SEL succeededAction;
@property (nonatomic) SEL failedAction;
- (id)initWithDelegate:(id)delegate selSucceeded:(SEL)succeeded selFailed:(SEL)failed;
- (BOOL)startRequestForURL:(NSURL*)url;
@end

In this sample class receivedData is where the response will be stored, the connectionDelegate refers to what will be the target of the succeededAction and failedAction messages. After all, that’s all I really care about when making this request: success or failure notification. Now your connection controller can be instantiated like this:

id delegate = self;
myConnectionController* connectionController = [[[myConnectionController alloc] initWithDelegate:delegate
                                                                                   selSucceeded:@selector(connectionSucceeded:)
                                                                                      selFailed:@selector(connectionFailed:)] autorelease];
[myConnectionController startRequestForURL:[NSURL URLWithString:@"http://request.org/somequery.php"]];

You also need to implement the callbacks. These will take the response data and parse it, typically, or handle any error conditions. Although the request is handled asynchronously by the URL Loading System, your callbacks are signalled on the main thread, and you may not want to tie it up parsing large chunks of XML or JSON. Next time I’ll write about using NSOperationQueue to do the parsing in a background thread so the UI remains responsive.

- (void)connectionSucceeded:(NSMutableData*)data {
    // data contains response, e.g. JSON to be parsed
}
- (void)connectionFailed:(NSError*)error {
    // error contains reason for failure
}

This is a totally trivial example, but you can see how easy it will be to a) define multiple callback handlers for any given class, and thus allowing for multiple NSURLConnections, and b) subclass myConnectionController for specialized request handling. This is what the default implementation looks like:

@implementation myConnectionController
@synthesize connectionDelegate;
@synthesize succeededAction;
@synthesize failedAction;
- (id)initWithDelegate:(id)delegate selSucceeded:(SEL)succeeded selFailed:(SEL)failed {
    if ((self = [super init])) {
        self.connectionDelegate = delegate;
        self.succeededAction = succeeded;
        self.failedAction = failed;
    }
    return self;
}
-(void)dealloc {
    [connectionDelegate release];
    [super dealloc];
}
- (BOOL)startRequestForURL:(NSURL*)url {
    NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:url];
    // cache & policy stuff here
    [[NSURLCache sharedURLCache] removeAllCachedResponses];
    [urlRequest setHTTPMethod:@"POST"];
    [urlRequest setHTTPShouldHandleCookies:YES];
    NSURLConnection* connectionResponse = [[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self] autorelease];
    if (!connectionResponse)
    {
        // handle error
    return NO;
    } else {
        receivedData = [[NSMutableData data] retain];
    }
    return YES;
}

And finally, the standard delegate methods for NSURLConnection are hidden inside the myConnectionController implementation. These should be familiar if you’ve ever used NSURLConnection directly:

- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response {
    [receivedData setLength:0];
}
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {
    [receivedData appendData:data];
}
- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error {
    [connectionDelegate performSelector:failedAction withObject:error];
    [receivedData release];
}
- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
    [connectionDelegate performSelector:succeededAction withObject:receivedData];
    [receivedData release];
}
@end

That should be enough to get you started. Hopefully you won’t find it too hard to extend this to add the ability to cancel a request, etc.


This post is part of iDevBlogADay, a group of indie iOS development blogs featuring two new posts per day. You can keep up with iDevBlogADay through the web site,RSS feed, or Twitter.


Viewing all articles
Browse latest Browse all 10

Latest Images

Trending Articles





Latest Images