single ProgressView for multiple file(s) download task - ios

I am Not Using: ASIHTTPRequest, AFNetworking, RestKit.
I have multiple images/files to be downloaded via Asynchronous call to web service for downloading.
I am showing SVProgressHUD (Activity Indicator, NOT Bar) during the download process.
Till this point, everything works fine.
Now, my client wants me to show the single ProgressBar on the view for file(s) being downloaded, so as to make user aware about the approximate time it would take.
Problem: How can I get consolidated time/size for the files being downloaded Asynchronously.
I am open for using any API that serves the purpose. I hope I am making sense, here.
Hard Time. Any Hints?

Take a look at NSProgress, it's not a UIKit class but will allow you to crate multiple instances of NSProgress (one for each activity) and then get a combined progress value which you can hook up to your UI.
There a good write up about it here with examples:
http://oleb.net/blog/2014/03/nsprogress/
You basically use KVO to read the progress value and then update your UI element i.e. progress bar.

One option is to get length of all files with "head" request, then just calculate it yourself:
amount of data downloaded/total-data-length.
This post will help you to get total content length: Objective-c Check file size from URL without downloading
Other option - start downloading each file in separate thread and use shared variable to get consolidated time.
Hope this helps.

Related

Serial API Calls in Objective C

In my use case, I have a list and with every cell, there is a button to download a particular document. From the listing, they can click on any number of those buttons and the document has to be downloaded in the background but in a serial fashion. We will have to show some animation while downloading a particular cell document.
I've tried concurrent way and it's working fine but our main application is not able to handle such requests. So, we need to call those APIs sequential. Please let me know how we can do that.
Any help will be highly appreciated.

Alamofire Priority Queue

I am using Alamofire as my networking library for my Swift app. Is there a way to keep a "priority queue" of network requests with Alamofire? I believe I saw this feature in a library in the past but I can no longer find it or find other posts about this.
Let's say I open a page in my application and it starts to make a few requests. First it gets some JSON, which is fast and no problem.
From that JSON, it pulls out some information and then starts downloading images. These images have the potential to be quite large and take many seconds (~30 seconds or more sometimes). But the tricky part is that the user has the option to move on to the next page before the image(s) finish downloading.
If the user moves on to the next page before the image downloading is done, is it possible to move it on to a lower priority queue? So that when the images on the next page start loading they will go faster? I would even be open to pausing the old one entirely until the new requests are finished if that is even possible.
Keep in mind I am open to many suggestions. I have a lot of freedom with my implementation. So if this is a different library, or different mechanism in iOS that is fine. Even if I continue to use Alamofire for JSON and do all my image downloading and management with something else that would be alright too.
Also, probably irrelevant but I will add it here. I'm using https://github.com/rs/SDWebImage for caching my images once they're fully downloaded. Which is why I don't want to cancel the request completely. I need it to finish and then it won't happen again.
TL;DR I want a fast queue and a slow queue with the ability to move things from the fast queue to the slow queue before they are finished.
Have you considered managing a NSOperationQueue? This tutorial might be helpful. In his example, he pauses the downloads as they scroll off the page, but I believe you could adjust the queuePriority property of the NSOperation objects instead.

Using NSURLSession to download a lot of images

I have created a simple testing app to learn how to use NSURLSession. This App has to download images from a webservice and present them into a UITableView.
I've already written the first part of the App that reads a list of images urls from the web service, now, I want to display this list.
My doubt is:
given that the list of images could be a really long list, is it ok to create a NSURLSessionDownloadTask for each image?
I thought to create the session in the cellForRowAtIndexPath function and store the NSURLSessions in a NSDictionary using as key the IndexPath of the cell (and probably relying on NSURLCache to avoid to download the same images more than once).
Other solutions:
I can see three more solutions:
Using GCD with dispatch_async
Subclassing NSOperation and essentially store an NSOperation for any image I need to download.
Using a third party library like AFNetwork... but since it is a learning purpose app I prefer to go completely with my code
.
If the multiple NSURLSession isn't a good solution, I'd choose one of those options.
What do you think about this approach?
NSURLSessionTask is fine for a large number of downloads. One advantage of it over some of the other methods you mentioned is that downloads can be cancelled or paused. It also correctly implements concurrency for network operations, which is more difficult than many cats on the internet will lead you to believe (if you don't believe me, view the eskimo's 2010 WWDC session and sample code. NSOperation for network connections is not trivial).
NSURLSessionTask and friends are designed for exactly the kinds of problems you are trying to solve, and it's very well tested.
For a tableview, start the task in tableView:willDisplayCell:forRowAtIndexPath: and cancel (or pause) a task in tableView:didEndDisplayingCell:forRowAtIndexPath:. That will limit the active downloads to the currently visible cells.
Suggestion:
I also came across a similar situation were I need to download about 2000 Image files and 100 Video files. For that purpose I implemented a custom download manager using NSOperationQueue and blocks.
I have added this library to GitHub, please feel free to check the implementation.
IMO whilst it is ok to create an NSURLSessionTask for each image a standard first in first out implementation will cause problems when scrolling through your cells. The reason for this is that downloads will be queued on your NSURLSession and tasks will be executed in the order they've been added to the queue, in other words in a FIFO manner. Imagine a scenario where you've scrolled through a vast number of cells and you have to wait for all downloads to complete in order. You would not only have to wait a long time, you would be making unnecessary network requests for image assets that may no longer be relevant to your user.
Nick Lockwood created a great NSOperationQueue subclass called NSOperationStack that reverses the order of operations so that the the last operation is executed first (LIFO). IMO for a large number of downloads a LIFO implementation is a must.
NSOsperationStack is available here
If you combine this with an implementation that uses cellForRowAtIndexPath to initiate and NSURLCache to store downloads, you should end up with a very streamlined and efficient solution.
I would use (or at least take a look at) SDWebImage's SDWebImageManager.
Besides downloading you can set priority and continue in the background options which I think you'll want to have.

Core-Data + AFNetworking + UI Updating (Responsiveness)

Here's the scenario:
I'm writing a DownloadManager, that allows the user to download, pause, cancel, download all, and pause all. The DownloadManager is a singleton, and uses AFNetworking to download files. It has it's own private managed object context, so that user can freely use other parts of the application (by adding, editing, deleting) core-data objects. I have a core-data entity DownloadInfo that stores the download information i.e. fileURL, fileSize, bytesRead, etc. The DownloadManager updates the download progress in DownloadInfo (one for each file).
I have a DownloadManagerViewController which uses NSFetchedResultsController to show the download status to the user. This download view controller is using the main managed object context.
Now let's say that I have 20 files in the download queue. And let's say that only 3 concurrent downloads are allowed. The download manager should download the file, and show the download progress.
Problem:
The DownloadInfo objects are being updated by the DownloadManager at a very high rate. The DownloadManagerViewController (responsible for showing the download progress) is updating the list using NSFetchedResultsControllerDelegate methods. The result is that a lot is happening in the main queue and application has very poor responsiveness.
How can I fix this? How can I make the application responsive, while showing the download progress?
I don't know how else to communicate that the download status between DownloadManager and DownloadManagerViewController. Is there another/ a better way to do this?
I don't want to use main managed object context in my DownloadManager, for reasons mentioned above. Note, that the DownloadManager is using AFNetworking which is handling the requests asynchronously, but eventually the DownloadInfo objects are updated in the main thread (as a result of the callback methods). Maybe there's a way to handle the downloads and status update operations in a background thread? but how? How will I communicate between the main thread and the background thread i.e. how will I tell the background thread to queue another file for download?
Thanks.
Rather than observing every change to the managed object context, consider implementing one or more notifications for the events that you actually want to update the screen. If the notification are being posted from a background thread, be sure to switch back to the main thread before triggering any UI update.
Alternatively, when the FRC delegate methods are called you receive information about what actually changed. You can analyse this and filter out the most frequent and least meaningful changes and just prevent them from resulting in a UI update.
Have you tried to use Instruments to see what's really going on in your ViewController? Time Profiler is going to tell you where the CPU is spending more time and will help you to identify the root of the problem. Without knowing this, we can't know exactly what performance tweaks you should do.
After using Instruments, if the NSFetchedResultsController is forcing a lot of updates of your VC, you should consider not to read the progress from CoreData. I've seen that a lot of my tables or views spend more time reading from CoreData than performing draws. Guessing that your problem is with CoreData reads, I would try to read progress from an NSMutableDictionary and just update the UI when the downloads pass a certan threshold.

Can I use jQuery UI Progress Bar to indicate the progress of downloading a file?

I have a web page that has several files available for download. These files vary in size from 1MB to 40MB. To help the user see that something is actually happening during the file download process, I would like to include a progress bar that gives a visual indication of the download progress.
Would the jQuery UI Progress Bar work for this kind of task? If so, are there some examples of such coding that I could follow? (Yes, I have reviewed the jQuery UI site on this).
Thanks.
In short, no, you can't do this.
The page listing these files for download has nothing to do with the transaction after the download has begun. It's up to the browser to display a progress bar and indicate when the download has completed. Once the user is downloading a file, the dialog takes place between their browser and the webserver - your page is no longer involved.
If I'm not mistaken, all browsers have built in progress bar.
So you don't need to make one.
Well, the problem is a bit the other way around:
What you need is something to show that you php is reading the content of the file into something (most likely a header thing). When the header/string is done, the browser download more or less takes over.
That's at least my experience with PHP and file downloading.
I have a theory that you could with PHP find out how much data has been read, and how much this is compared with the total size of the file, then show that using some AJAX technique.
Again: this is just a theory. It is not tested. But I am having the same challenge my self at the moment. My task is that the file is first read from a FTP-server, then pushed to the browser.

Resources