Episode #91

AFNetworking 2.0

19 minutes
Published on October 17, 2013

This video is only available to subscribers. Get access to this video and 572 others.

In this episode we take a look at the brand new design of AFNetworking 2.0. We'll convert our TuneStore application to leverage AFNetworking and clean up our View Controller by introducing a new class to handle the network plumbing. We'll also take a look at a handy UIKit category for easily managing an activity indicator's state.

Episode Links

Creating a client to search iTunes

#import "AFHTTPSessionManager.h"
@interface ITunesClient : AFHTTPSessionManager
+ (ITunesClient *)sharedClient;
- (NSURLSessionDataTask *)searchForTerm:(NSString *)term completion:( void (^)(NSArray *results, NSError *error) )completion;
@end
#import "ITunesClient.h"

@implementation ITunesClient


+ (ITunesClient *)sharedClient {
    static ITunesClient *_sharedClient = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSURL *baseURL = [NSURL URLWithString:@"https://itunes.apple.com/"];

        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        [config setHTTPAdditionalHeaders:@{ @"User-Agent" : @"TuneStore iOS 1.0"}];

        NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:10 * 1024 * 1024
                                                          diskCapacity:50 * 1024 * 1024
                                                              diskPath:nil];

        [config setURLCache:cache];

        _sharedClient = [[ITunesClient alloc] initWithBaseURL:baseURL
                                         sessionConfiguration:config];
        _sharedClient.responseSerializer = [AFJSONResponseSerializer serializer];
    });

    return _sharedClient;
}

- (NSURLSessionDataTask *)searchForTerm:(NSString *)term completion:( void (^)(NSArray *results, NSError *error) )completion {
    NSURLSessionDataTask *task = [self GET:@"/search"
                                parameters:@{ @"country" : @"US",
                                              @"term" : term }
                                   success:^(NSURLSessionDataTask *task, id responseObject) {
                                       NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
                                       if (httpResponse.statusCode == 200) {
                                           dispatch_async(dispatch_get_main_queue(), ^{
                                               completion(responseObject[@"results"], nil);
                                           });
                                       } else {
                                           dispatch_async(dispatch_get_main_queue(), ^{
                                               completion(nil, nil);
                                           });
                                           NSLog(@"Received: %@", responseObject);
                                           NSLog(@"Received HTTP %d", httpResponse.statusCode);
                                       }

                                   } failure:^(NSURLSessionDataTask *task, NSError *error) {
                                       dispatch_async(dispatch_get_main_queue(), ^{
                                           completion(nil, error);
                                       });
                                   }];
    return task;
}
@end

Here we return the task so that it can be canceled or so the state property might be observed.

Using the client in a view controller

    NSURLSessionDataTask *task = [[ITunesClient sharedClient] searchForTerm:term
                                                                 completion:^(NSArray *results, NSError *error) {
                                                                     if (results) {
                                                                         self.results = results;
                                                                         [self.tableView reloadData];
                                                                     } else {
                                                                         NSLog(@"ERROR: %@", error);
                                                                     }
                                                                 }];

Loading images using AFNetworking

For each cell we can now use the AFNetworking category method for UIImageView. Just #import "UIImageView+AFNetworking.h" at the top of the file, then in the tableView:cellForRowAtIndexPath: method:

    cell.artworkImageView.image = nil;
    [cell.artworkImageView cancelImageRequestOperation];

    NSURL *imageURL = [NSURL URLWithString:record[@"artworkUrl100"]];
    if (imageURL) {
        [cell.artworkImageView setImageWithURL:imageURL];
    }

Easily showing an activity indicator when a task is running

Using another handy category method, add #import "UIActivityIndicatorView+AFNetworking.h" at the top of the file, then after making the request, you can use the resulting task...

[self.activityIndicator setAnimatingWithStateOfTask:task];

This will show the indicator spinning when the request is running, but when it is paused or canceled the animation is stopped.