24

AFNetworking HTTP Cache

When developing iOS application that requires network communication, I frequently use AFNetworking library. It’s good to know that iOS along with AFNetworking supports HTTP Cache out of the box.

Unfortunately, when server returns code 304 “Not Modified”, iOS loads cached response in background and returns it to the user, as it would be returned from server. Because of that, you can’t implement different logic depending on data modification state. Investigating how AFNetworking works, gets me nice solution to that issue.

I found a way to determine if response was returned from cache or not, using AFNetworking 2.0. Each time a new response is returned from the server (status 200, not 304) the cacheResponseBlock (which is a property of AFHTTPRequestOperation) is called. The block should return NSCachedURLResponse object if response should be cached or nil if it shouldn’t. That’s way you can filter responses and cache only some of them. In this case, I am caching all responses that comes from the server.

The trick is, that when server sends 304 and response is loaded from cache, this block won’t be called. So, this is the code I am using:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

BOOL __block responseFromCache = YES; // yes by default

void (^requestSuccessBlock)(AFHTTPRequestOperation *operation, id responseObject) = 
    ^(AFHTTPRequestOperation *operation, id responseObject) {
        if (responseFromCache) {
            // response was returned from cache
            NSLog(@"RESPONSE FROM CACHE: %@", responseObject);
        }
        else {
            // response was returned from the server, not from cache
            NSLog(@"RESPONSE: %@", responseObject);
        }
    };

void (^requestFailureBlock)(AFHTTPRequestOperation *operation, NSError *error) = 
    ^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"ERROR: %@", error);
    };

AFHTTPRequestOperation *operation = [manager GET:@"http://example.com/"
                                      parameters:nil
                                         success:requestSuccessBlock
                                         failure:requestFailureBlock];

[operation setCacheResponseBlock:
    ^NSCachedURLResponse *(NSURLConnection *connection, NSCachedURLResponse *cachedResponse) {
        // this will be called whenever server returns status code 200, not 304
        responseFromCache = NO;
        return cachedResponse;
    }];

Perhaps your app is doing some heavy processing of returned data, for example parsing JSON and updating CoreData content. You can assume that if response is returned from cache the whole processing can be skipped, saving user’s time. That way you will take full advantage of HTTP Cache implementation on the client side.