How to track progress of multiple simultaneous downloads with AFNetworking?
afnetworking-2, batch-processing, block, ios, mbprogresshud
Solution
If your block is inlined, you can access the `operation` directly but the compiler might warn you of the circular referencing. You can work around by declaring a weak reference and use it inside the block:
__weak AFHTTPRequestOperation weakOp = operation;
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
NSURL* url = weakOp.request.URL; // sender operation's URL
}];
Actually, you can access anything inside the block, but you need to understand block to go for that. In general, any variable referred in the block is copied at the time the block created i.e. the time that the line got executed. It means my `weakOp` in the block will refer to the value of the `weakOp` variable when the `setDownloadProgressBlock` line got executed. You can think it like what would each variable you refer in the block would be if your block got executed immediately.
Problem
I am using AFNetworking to download files that my app uses for a sync solution. At certain times, the app downloads a series of files as a batch unit. Following this example, I run the batch like this: ``` NSURL *baseURL = <NSURL with the base of my server>; AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL]; // as per: https://stackoverflow.com/a/19883392/353137 dispatch_group_t group = dispatch_group_create(); for (NSDictionary *changeSet in changeSets) { dispatch_group_enter(group); AFHTTPRequestOperation *operation = [manager POST:@"download" parameters: <my download parameters> success:^(AFHTTPRequestOperation *operation, id responseObject) { // handle download success... // ... dispatch_group_leave(group); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { // handle failure... // ... dispatch_group_leave(group); }]; [operation start]; } // Here we wait for all the requests to finish dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // run code when all files are downloaded }); ``` This works well for the batch downloads. However, I want to display to the user an MBProgressHUD which shows them how the downloads are coming along. AFNetworking provides a callback method ``` [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) { }]; ``` ... which lets you update a progress meter pretty easily, simply by setting the progress to `totalBytesRead / totalBytesExpectedToRead`. But when you have multiple downloads going simultaneously that is hard to keep track of on a total basis. I have considered having an `NSMutableDictionary` with a key for each HTTP operation, with this general format: ``` NSMutableArray *downloadProgress = [NSMutableArray arrayWithArray:@{ @"DownloadID1" : @{ @"totalBytesRead" : @0, @"totalBytesExpected" : @100000}, @"DownloadID2" : @{ @"totalBytesRead" : @0, @"totalBytesExpected" : @200000} }]; ``` As each operation's download progresses, I can update the `totalBytesRead` for that specific operation in the central `NSMutableDictionary` -- and then total up all the `totalBytesRead` and `totalBytesExpected' to come up with the total for the whole batched operation. However, AFNetworking's progress callback method`downloadProgressBlock`, defined as`^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead){}`does not include the specific operation as a callback block variable (as opposed to the`success`and`failure` callbacks, which do contain the specific operation as a variable, making it accessible). Which makes it impossible, as far as I can tell, to determine which operation specifically is making the callback. Any suggestions on how to track the progress of multipole simultaneous downloads using AFNetworking?