Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting Download progress with AFNetworking 2.0 + NSProgress + Custom ProgressView

i'm getting confused trying to get progress from a Download Task with AFNetworking 2.0. I created a singleton object where I centralized my download operations (my app will download podcast files).

So, I have a PodcastManager class with this method, where I use a Podcast object created by me too:

- (void)downloadPodcast:(Podcast *)podcast completion:(CompletionBlock)completionBlock{

podcast.downloadState = DOWNLOADING;

NSURL *url = [NSURL URLWithString:podcast.enclosure];
NSURLRequest *request = [NSURLRequest requestWithURL:url];

NSProgress *progress = [NSProgress progressWithTotalUnitCount:podcast.size];

NSURLSessionDownloadTask *downloadTask = [self.downloadManager downloadTaskWithRequest:request progress:&progress destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {

NSURL *documentsDirectoryPath = [NSURL fileURLWithPath:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]];
  //Return final path for podcast file

} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
if (!error) {
  //Handle success 
}
else{
  //Handle errors
}
}];


[progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil];

[downloadTask resume];

 if (completionBlock) {
    dispatch_async(dispatch_get_main_queue(), ^{
     completionBlock();
   });
 } 
}

Right now, for debugging purposes I have this also in PodcastManager:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
  if ([keyPath isEqualToString:@"fractionCompleted"] && [object isKindOfClass:[NSProgress class]]) {
    NSProgress *progress = (NSProgress *)object;
    NSLog(@"Download at %f", progress.fractionCompleted);
  }
}

I call the downloadPodcast:completion: method from a UITableViewController, where every cell represents a Podcast. What I'm trying to do is show a custom Progress View in the cell with the progress of the download of that podcast.

I know that AFNetworking 2.0 has a great UIProgressView that gets progress of a download task easily, but in my app I'm using this custom progress view https://github.com/danielamitay/DACircularProgress so I can't use this feature :(

Anyone can show me the way to do this right? Thanks

UPDATE: Well, I made some progress with this problem. Now I can access to the progress of the download from my custom cell using KVO. Using the method from AFURLSessionManager setDownloadTaskDidWriteDataBlock I get the current percentage of the download, and I save it in a property of the cell.

[[manager downloadManager] setDownloadTaskDidWriteDataBlock:^(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {

  self.downloadPercentage = (float)totalBytesWritten/(float)totalBytesExpectedToWrite;
}];

Then with KVO i look over my property

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
  if ([keyPath isEqualToString:@"downloadPercentage"] && [object isKindOfClass:[FDSEpisodeCell class]]) {
    int percentage = self.downloadPercentage * 100;

    NSLog(@"Percentage %d", percentage);

    [self.downloadLabel setText:[NSString stringWithFormat:@"%d",percentage]];

 }
}

My problem now is that I'm trying to show this percentage in a UILabel inside the cell, but although I change the label text, nothing changes. How can I refresh the label to show the current percentage?. I tried to use setNeedsDisplay but nothing happened. And if I use reloadData from the TableView, something weird happens and the view turns into blank.

Also, i tried with a NSTimer, but neither worked.

like image 619
WedgeSparda Avatar asked Dec 15 '22 02:12

WedgeSparda


2 Answers

Use AFURLConnectionOperation -setDownloadProgressBlock:, updating the progress with the ratio of totalBytesRead over totalBytesExpectedToRead each time the block is called.

To update your UI element, just call the update through the main thread:

dispatch_async(dispatch_get_main_queue(), ^(void){
    //Run UI Updates
});
like image 84
Alvaro Franco Avatar answered Jan 04 '23 07:01

Alvaro Franco


Simple solutions for Swift:

let progressView: UIProgressView?
let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
let sessionManager = AFURLSessionManager(sessionConfiguration: sessionConfiguration)
let request = NSURLRequest(URL: url)
let sessionDownloadTask = sessionManager.downloadTaskWithRequest(request, progress: nil, destination: { (url, response) -> NSURL in

            return destinationPath.URLByAppendingPathComponent(fileName) //this is destinationPath for downloaded file

            }, completionHandler: { response, url, error in

                //do sth when it finishes
        })

Using UIProgressView and setProgressWithDownloadProgressOfTask:

progressView.setProgressWithDownloadProgressOfTask(sessionDownloadTask, animated: true)

sessionDownloadTask.resume()
like image 28
Bartłomiej Semańczyk Avatar answered Jan 04 '23 07:01

Bartłomiej Semańczyk