Lessons From iOS: NSURLConnection

Edit:  NSURLConnection is dead. long live  NSURLSession

I am working on an app for my University, LeTourneau University. The app pulls data from a bunch of web services.  Sometimes these services have nice XML/JSON/RSS interfaces. Sometimes you have to result to good old fashion HTML scrapping. The trick will be how to download the content of a HTML document on iOS.

Usually when you work with HTML you are displaying it in a web view of some flavor. But when you are scrapping HTML you want the content of the HTML doc itself. Browsing around the iOS docs will lead you to some potentially useful classes:

  • NSURL
  • NSURLRequest
  • NSURLConnection
  • NSURLResponse

The NSURL class is straightforward enough. It stores a manipulates URLs. The NSURLRequest is representation of request to load a URL. The NSURLConnection is a way to execute a URL request. The NSURLResponse is the results of the executed request. 

The real key is the NSURLConnection and the various NSURLConnection delegates. NSURLConnectionDelegate, NSURLConnectionDownloadDelegate, and NSURLConnectionDataDelegate are complementary delegates designed to configure a specific NSURLConnection. These delegates have been extensively adjusted, modified, and overhauled. Most of the methods in these delegates have become obsolete since iOS 5 (more on this later).  The first step is to fetching data is to to set up the NSURLConnection. NSURLConnections can be performed synchronously or asynchronously. To prevent blocking the main thread I recommend sending requests asynchronously.

-(void)performRequest
{

    NSURL * url =[[NSURL alloc] initWithString:@"http://myurl.com"];
    NSURLRequest * request = [[NSURLRequest alloc] initWithURL:url];
    NSURLConnection * connection =[[NSURLConnection alloc]initWithRequest:request 
																 delegate:self];
    
}

Once the request  has been performed all the rest of the processing occurs in the delegate methods. If your URL is secure then you will need to implement the following two methods to ensure access.

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    if ([challenge previousFailureCount] == 0)
    {
        NSURLCredential * cred = [NSURLCredential credentialWithUser: @"username"
                                                            password: @"password"
                                                         persistence: NSURLCredentialPersistenceForSession];
        [[challenge sender] useCredential:cred forAuthenticationChallenge:challenge];
    }
    else
    {
        //You don't have authentication
    }
}
-(BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
{
    return YES;
}

The first method is how to provide authentication for the URL. The second method is to use authentication credentials already stored either in the keychain or earlier in your applications code. Once you started the connection and provided any authentication required you should check for connection failures.

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    //handle errors
}

The final step is to receive the data and process it.

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSString * html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"%@",html);
}

The didReceiveData method is part of an informal protocol called NSURLConnectionDataDelegate. All of the methods in the NSURLConnection delegate related to receiving data are deprecated. You have three options that aren't deprecated.The NSURLConnectionDataDelegate method shown above. The NSURConnection class provides

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler;

But this method does not have a delegate option for handling authentication. This option only works for unsecured URL's. The last option is to use the NSURLConnectionDownload delegate. This downloads the URL's contents to a temporary file that you can then copy. I tried to get this to work but had a headache trying to debug the errors. Edit: but in the mean time Apple has added the fabulous new NSURLSession class and a whole host of new API's I highly encourage you to check them out.