Waiting for multiple uploads with AFNetworking

In working with networking and uploads and downloads via objective c I ended up using the AFNetworking framework. This is a great framework with excellent documentation but I struggled to understand how to wait for multiple uploads to finish before forwarding the customer onto a new view. Waiting for multiple uploads is synchronous and networking is asynchronous.

The basis of what I used to do multiple operations
AfNetworking FAQ on multiple operations

Networking is asynchronous
Afnetworking can enqueue multiple operations and then try and wait for them.  The problem is that the program will run through the method definition for networking and return back to the calling code before all operations have completed unless the operations are waited for by some mechanism.

Things that do Not work

Afnetworking and has the ability to wait for a single operation or wait for multiple operations to finish but I found it was only works when doing downloads.

There are ways to wait for downloads via the AFNetworking by

adding a wait on the operation queue

//Works to wait for multiple downloads to occur.  Trying with uploads I found it did a few then hung.
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:@"SERVER_URL"]];
[httpClient.operationQueue waitUntilAllOperationsAreFinished]; //wait for everyone to finish the tango
//...Add operations ...
//...Run operations ...

Trying with uploads I found it did the first few uploads then hangs until timeout.

Setting a wait for property on the operation itself.

//This will hang on the first upload and never get further until timeout.
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:@"SERVER_URL"]];
NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET"
      path:[NSString stringWithFormat: @"SOME_URL"] parameters:nil];
      AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]  initWithRequest: request];
[operation waitUntilFinished]; //wait for me
//this is run by enqueuing the operations directly upon the http client operation queue which starts the operation automatically.

This will hang on the first upload and never get further until timeout.

Thing that works

Use AFNetworking and enqueueBatchOfHTTPRequestOperations with NSNotification for knowing when all uploads have finished

The key is not to use the waits but to use a NSNotification that the calling controller is observing for a notification for and then the controller can do any final actions after the networking has finished.

Shared constant both networking code and controller or whatever you are using as a receiver share

//  constants.h
#define kUploadsFinished @"kUploadsFinished"

Networking code. This takes an NSArray of networking operations that you built up and runs through them.
We use the enqueueBatchOfHTTPRequestOperations method off AFNetworking HttpClient but we include a NSNotification that is fired when everything is finished.

// networking.m
#import "AFNetworking.h"
//Build up your operation queue into an NSArray manually.
//Then call runUploads passing in your NSArray.
-(void) runUploads(NSArray *operations)networkingOperations {
[httpClient enqueueBatchOfHTTPRequestOperations: networkingOperations
  progressBlock:^(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations) {
  //  This gets fired after each networking operations has finished.
  }
  completionBlock:^(NSArray *operations) {
    //Run on completion now iterate through the operations looking for failure and success operations.
    for (AFHTTPRequestOperation *ro in operations) {
      if (ro.error) {
        //handle your error case
        //Notify of failure?  Up to what you need to do.
      }else {
        //handle your success case
      }
    }
    //notify that uploads have finished.  Can have separate notifications for success or failure if you keep track of if there was a failure.
    [[NSNotificationCenter defaultCenter] postNotification:
      [NSNotification notificationWithName: kUploadsFinished object:nil]]; //Send things back to observer with the object or just return nil as this example shows.
  }
     ];
     

Controller code to call the networking and deal with the notification that everything has finished.

//   yourController.m
//First some add and remove observer methods that you will call
- (void)removeObserver{
    [[NSNotificationCenter defaultCenter] removeObserver:self name: kUploadsFinished object:nil];
    }
- (void)addObserver {
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(uploadsFinished:)
                                                 name: kUploadsFinished object:nil];
                                                 }
//Be a good citizen and kill your observers on memory warning and on dealloc
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    [self removeObserver];
    }
-(void) dealloc {
    [self removeObserver];
    }
//add the observer on viewWillAppear or viewDidLoad
- (void) viewWillAppear:(BOOL)animated {
  [self addObserver];
  }
//handle the uploads finished.  This is triggered by the networking code when the notification is fired.
-(void) uploadsFinished {
//Forward onto next view or whatever needs to be done
}
// Call the networking code and setup your operations
-(void) doUploads {
//Setup your call to your networking class.
//Create all your upload operations.
//Call the runUploads method
}
Advertisements

One thought on “Waiting for multiple uploads with AFNetworking

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s