Objective-c blocks as properties with AFNetworking

So you’ve decided on AFNetworking for your networking library. You are happily coding using blocks for success and failure handlers. You are however starting to get annoyed with having some duplicate code in the error handling block such as dismissing an existing HUD and/or displaying errors to the user. There is a solution for this code smell but lets first take a look at one of the AFNetworking samples:

NSURL *url = [NSURL URLWithString:@"https://alpha-api.app.net/stream/0/posts/stream/global"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
    success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        NSLog(@"App.net Global Stream: %@", JSON);
    } failure:^(NSURLRequest *, NSHTTPURLResponse *, NSError *, id ) {
        NSLog(@"fail whale!");
}];
[operation start];

Now lets see how we can handle this failure with a regular block first. Remember, blocks are C functions.

void (^failureBlock)(NSURLRequest *, NSHTTPURLResponse *, NSError *, id) = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
	NSLog(@"fail from a block");
	};
 
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
    success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        NSLog(@"App.net Global Stream: %@", JSON);
    } failure:failureBlock];

Or if we want to add additional functionality..

void (^failureBlock)(NSURLRequest *, NSHTTPURLResponse *, NSError *, id) = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
	NSLog(@"fail from a block");
	};
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
    success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        NSLog(@"App.net Global Stream: %@", JSON);
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON ) {
        NSLog(@"do other stuff");
        failureBlock( request, response, error, JSON);
    }];

Pretty awesome huh? Now lets get to making it a property. Be warned that when using blocks you can run into retain/release dance and can result in leaks so make sure you’re familiar with how to handle memory management with blocks or you’ll leak. At the time of this blog post, ARC doesn’t have a solution for this yet. With that said, see how the declaration uses copy because they need to outlive the scope they were created in. If you don’t use copy, you’ll get EXC_BAD_ACCESS.

@interface SomeBaseViewController : UIViewController
 
@property (copy) void (^jsonRequestOperationFailedBlock)(NSURLRequest *, NSHTTPURLResponse *, NSError *, id );
 
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    __weak MyCurrentClass *weakSelf = self;
    [self setJsonRequestOperationFailedBlock:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
        [weakSelf dismissHUD];
        [weakSelf presentErrorFromStatusCode:[response statusCode]];
    }];
}

That’s it! Now you’ll be able to use Objective-C blocks as properties for other things other than AFNetworking.