2009/11/21

Meet IZURLConnection, the NSURLConnection with Reachability and easy Network Activity monitoring

This is my 2nd blog post on the topic of iPhone development and I am happy to share with you yet an another open source project of mine: IZURLConnection

Basically it mimics NSURLConnection, has the same delegate callbacks, and heck it uses NSURLConnection as it's back-end, BUT it has 2 nifty features:

1. Robust (uses Reachability v2.0)

Network connections on a mobile device are terrible. Or worse. Connection is spotty and if you happen to issue an NSURLConnection when the device is/goes dark (e.g. enters into a tunnel or a stargate wormhole) then you'll end up with a delegate call with an error like (all NSURLErrorDomain domain error codes): NSURLErrorNotConnectedToInternet, NSURLErrorCannotFindHost, NSURLErrorCannotConnectToHost, NSURLErrorTimedOut

In my real life experiments NSURLErrorNotConnectedToInternet is an error that is permanent and lasts longer while the others are transient. They are usually present when there's a glitch e.g. switching between network connection types (3G, EDGE, WiFi). IZURLConnection does not notify the delegate with NSURLErrorNotConnectedToInternet. It uses the Reachability class provided by Apple and waits until the host provided in the request will be reachable again and automatically retries. In the other (transient) error cases it retries by default 2 times before notifying the delegate of the error.

This takes a little bit of error handling off of your shoulders. But wait, there is more. Continue reading.

2. Simple and easy way to notify the user of network activity

When it starts or finishes loading it posts a NSNotification instance. If there are network connections out there then you are safe to assume that your network indicator on the device should be spinning and you should switch it on. Take a look at the following code snippet in the appdelegate:

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    
    networkConnections = 0;
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(IZURLConnectionDidStart:) 
                                                 name:IZURLConnectionConnectionDidStartNotification 
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(IZURLConnectionDidFinish:) 
                                                 name:IZURLConnectionConnectionDidFinishNotification 
                                               object:nil];     
    
    // Override point for customization after app launch    
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];
}

- (void)updateNetworkActivityIndicator
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = (networkConnections != 0);
}

- (void)IZURLConnectionDidStart:(NSNotification *)notification
{
    // Only increment networkConnections if the request is a network protocol
    // Assuming that NSURLConnection handles the following protocols: ftp, http, https, file
    // Assuming that file is the only protocol that is not a network protocol
    IZURLConnection *connection = (IZURLConnection *)[notification object];
    if ([connection.request.URL.scheme isEqualToString:@"file"] == NO)
    {
        networkConnections++;
        [self updateNetworkActivityIndicator];
    }
}

- (void)IZURLConnectionDidFinish:(NSNotification *)notification
{
    // Only decrement networkConnections if the request is a network protocol
    // Assuming that NSURLConnection handles the following protocols: ftp, http, https, file
    // Assuming that file is the only protocol that is not a network protocol
    IZURLConnection *connection = (IZURLConnection *)[notification object];
    if ([connection.request.URL.scheme isEqualToString:@"file"] == NO)
    {
        networkConnections--;
        [self updateNetworkActivityIndicator];
    }
}

3. All your current NSURLConnection code will work if you refactor to IZURLConnection

Well, this isn't entirely true. IZURLConnection currently doesn't implement NSURLConnection's Runloop Scheduling methods:

- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode
- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode

If you wish to extend it though, go ahead. At the current state it gets the job done. I've never used runloop scheduling with NSURLConnection anyway.
Important note: Be prepared to receive multiple delegate messages as IZURLConnection may retry multiple times.

That's it. Take it away.

0 comments:

Post a Comment