Search code examples
iosafnetworking

AFNetworking. Check download progress for all operation queue


I have an iOS application made with AFNetworking. I have a singleton custom httpclient subclass and i use it to consume my api and download binary files from server.

I need to dowload about 100 files. Is it safe to iterate trough my url array and create one AFHTTPRequestionOperation for each url? My code is this:

NSMutableURLRequest* rq = [[MIHTTPClient sharedInstance] requestWithMethod:@"GET" path:@"...." parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:rq] ;
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];

[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    //completion

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    //manage error
}];


[[MIHTTPClient sharedInstance] enqueueHTTPRequestOperation:operation];

How can I receive a feedback from this queue? i don't see any "HTTPClientDelegate" protocol or something like this.


Solution

  • Regarding progress, see setDownloadProgressBlock (part of AFURLConnectionOperation, from which AFHTTPRequestOperation is subclassed), e.g.:

    [operation setDownloadProgressBlock:^(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) {
        NSLog(@"Sent %d of %d bytes, %@", totalBytesWritten, totalBytesExpectedToWrite, path);
    }];
    

    And, in your code sample, you're calling setCompletionBlockWithSuccess:failure:, so that provides the status regarding the completion of the individual operations. Personally, in my little test, I just maintain an array of my requested downloads, and have these three blocks (progress, success, and failure) update the status code of my downloads like so:

    for (NSInteger i = 0; i < 20; i++)
    {
        NSURLRequest *request = ... // set the request accordingly
    
        // create a download object to keep track of the status of my download
    
        DownloadObject *object = [[DownloadObject alloc] init];
        download.title = [NSString stringWithFormat:@"Download %d", i];
        download.status = kDownloadObjectStatusNotStarted;
        [self.downloadObjects addObject:download];
    
        AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    
        [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
            download.status = kDownloadObjectStatusDoneSucceeded;
    
            // update my UI, for example, I have table view with one row per download
            //
            // [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:i inSection:0]]
            //                       withRowAnimation:UITableViewRowAnimationNone];
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            download.status = kDownloadObjectStatusDoneFailed;
    
            // update UI
            //
            // [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:i inSection:0]]
            //                       withRowAnimation:UITableViewRowAnimationNone];
        }];
    
        [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
            download.totalBytesRead = totalBytesRead;
            download.status = kDownloadObjectStatusInProgress;
    
            // update UI
            //
            // [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:i inSection:0]]
            //                       withRowAnimation:UITableViewRowAnimationNone];
        }];
    
        [queue addOperation:operation];
    }
    

    You can keep track of the individual downloads this way. You can also just keep track of the pending operations (or query the HTTPClient object's operationQueue property, and look at the operationCount property of that).


    In terms of downloading 100 files, two considerations:

    • I would have thought that you'd want to call setMaxConcurrentOperationCount on the operationQueue of your HTTPClient subclass, setting it to some reasonable number (4 or 5), as glancing at the AFNetworking code, it doesn't seem to do that. I find that there are diminishing returns if you go much above that, and if all of the files are from a single server, there are constraints on how many concurrent operations can be done between a given client and given server.

    • I've read anecdotal claims that the Apple will reject apps that make extraordinary requests over cellular network. See https://stackoverflow.com/a/14922807/1271826.