Search code examples
iosobjective-cafnetworking-2

How to get download progress in AFNetworking 2.0?


I am using AFURLSessionManager to create a new download task:

AFURLSessionManager* manager = ...

NSProgress* p = nil;
NSURLSessionDownloadTask* downloadTask =
        [manager downloadTaskWithRequest:request
                                 progress:&p
                              destination:^NSURL*(NSURL* targetPath, NSURLResponse* response) {...}
                        completionHandler:^(NSURLResponse* response, NSURL* filePath, NSError* error) {...}
        ];
[downloadTask resume];

The file gets downloaded fine, however, how do I get progress notifications?

p is always set to nil. I've filed an issue for that.

I've also tried to call setDownloadTaskDidWriteDataBlock on the manager, and I do get progress notifications there but I receive them all grouped together after the file has been downloaded.

Seems like this area is still a bit buggy in AFNetworking 2.0

Any ideas?


Solution

  • You should observe the fractionCompleted property of your NSProgress object using KVO:

    NSURL *url = [NSURL URLWithString:@"http://www.hfrmovies.com/TheHobbitDesolationOfSmaug48fps.mp4"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
    NSProgress *progress;
    NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request progress:&progress destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
        // …
    } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
        [progress removeObserver:self forKeyPath:@"fractionCompleted" context:NULL];
        // …
    }];
    
    [downloadTask resume];
    [progress addObserver:self
                forKeyPath:@"fractionCompleted"
                   options:NSKeyValueObservingOptionNew
                   context:NULL];
    

    Then add the observer method:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
        if ([keyPath isEqualToString:@"fractionCompleted"]) {
            NSProgress *progress = (NSProgress *)object;
            NSLog(@"Progress… %f", progress.fractionCompleted);
        } else {
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        }
    }
    

    Of course, you should check keyPath and/or object parameters to decide if that's the object/property you want to observe.

    You can also use the setDownloadTaskDidWriteDataBlock: method from AFURLSessionManager (from which AFHTTPSessionManager inherits) to set a block for receiving download progress updates.

    [session setDownloadTaskDidWriteDataBlock:^(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
        NSLog(@"Progress… %lld", totalBytesWritten);
    }];
    

    This AFNetworking method maps the URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: method from NSURLSessionDownloadDelegate protocol to a more convenient block mechanism.

    BTW, Apple's KVO implementation is severely broken. I recommend using a better implementation like the one proposed by Mike Ash with MAKVONotificationCenter. If you are interested in reading why Apple's KVO is broken, read Key-Value Observing Done Right by Mike Ash.