6

Alamofire is being used to download multiple larger files at one request. I am able to make progress be seen for each file separately using

Alamofire.request(.GET, imageURL).progress

But I would like to track the progress of all open sessions at once and do not know how to do that. (lets say I have 15 files downloading simultaneously) I read a lot of tutorials that address single file progress but for whole session none.

Is it possible to track progress that way with Alamofire and if it is, how?

Jernej Mihalic
  • 151
  • 2
  • 6

1 Answers1

5

In iOS9, you can create your own NSProgress object, and observe, for example, fractionCompleted. Then you can addChild:

private var observerContext = 0

class ViewController: UIViewController {

    private var progress: NSProgress!

    override func viewDidLoad() {
        super.viewDidLoad()

        progress = NSProgress()
        progress.addObserver(self, forKeyPath: "fractionCompleted", options: .New, context: &observerContext)

        downloadFiles()
    }

    deinit {
        progress?.removeObserver(self, forKeyPath: "fractionCompleted")
    }

    private func downloadFiles() {
        let filenames = ["as17-134-20380.jpg", "as17-140-21497.jpg", "as17-148-22727.jpg"]

        let baseURL = NSURL(string: "http://example.com/path")!

        progress.totalUnitCount = Int64(filenames.count)
        progress.completedUnitCount = 0

        for filename in filenames {
            let url = baseURL.URLByAppendingPathComponent(filename)
            let childProgress = Alamofire.request(.GET, url.absoluteString)
                .response() { request, response, data, error in
                    // process response
                }
                .progress
            progress.addChild(childProgress, withPendingUnitCount: 1)
        }
    }

    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        if context == &observerContext {
            if keyPath == "fractionCompleted" {
                let percent = change![NSKeyValueChangeNewKey] as! Double
                print("\(percent)")
            }
        } else {
            super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
        }
    }

}

If you need support for iOS 7/8, too, you can call becomeCurrentWithPendingUnitCount and resignCurrent:

for filename in filenames {
    let url = baseURL.URLByAppendingPathComponent(filename)
    progress.becomeCurrentWithPendingUnitCount(1)
    Alamofire.request(.GET, url.absoluteString)
        .response() { request, response, data, error in
            // process response
    }
    progress.resignCurrent()
}

If you are using AFNetworking, it's the same process (i.e. the same viewDidLoad, observeValueForKeyPath, and deinit methods as above), but rather than retrieving the Alamofire progress property, you instead use the AFHTTPSessionManager method downloadProgressForTask to get the NSProgress associated with the NSURLSessionTask. For example:

private func getFiles() {
    let filenames = ["as17-134-20380.jpg", "as17-140-21497.jpg", "as17-148-22727.jpg"]

    let manager = AFHTTPSessionManager()
    manager.responseSerializer = AFHTTPResponseSerializer()

    let baseURL = NSURL(string: "http://example.com/path")!

    progress.totalUnitCount = Int64(filenames.count)
    progress.completedUnitCount = 0

    for filename in filenames {
        let url = baseURL.URLByAppendingPathComponent(filename)
        let task = manager.GET(url.absoluteString, parameters: nil, progress: nil, success: { task, responseObject in
            // do something with responseObject
            print(url.lastPathComponent! + " succeeded")
        }, failure: { task, error in
            // do something with error
            print(error)
        })

        if let downloadTask = task, let childProgress = manager.downloadProgressForTask(downloadTask) {
            progress.addChild(childProgress, withPendingUnitCount: 1)
        }
    }
}

Or, if using download tasks:

private func downloadFiles() {
    let filenames = ["as17-134-20380.jpg", "as17-140-21497.jpg", "as17-148-22727.jpg"]

    let manager = AFHTTPSessionManager()

    let baseURL = NSURL(string: "http://example.com/path")!

    progress.totalUnitCount = Int64(filenames.count)
    progress.completedUnitCount = 0

    let documents = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)

    for filename in filenames {
        let url = baseURL.URLByAppendingPathComponent(filename)
        let task = manager.downloadTaskWithRequest(NSURLRequest(URL: url), progress: nil, destination: { (temporaryURL, response) -> NSURL in
            return documents.URLByAppendingPathComponent(url.lastPathComponent!)
        }, completionHandler: { response, url, error in
            guard error == nil else {
                print(error)
                return
            }

            if let name = url?.lastPathComponent {
                print("\(name) succeeded")
            }
        })

        if let childProgress = manager.downloadProgressForTask(task) {
            progress.addChild(childProgress, withPendingUnitCount: 1)
        }

        task.resume()
    }
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Hi Rob, can you give an example with AFNetworking 3.0 for multiple file download progress ? – Nuibb Feb 03 '16 at 11:52
  • 1
    @Nuibb - That really should be a separate question, but I've updated my answer illustrating the AFNetworking examples... – Rob Feb 03 '16 at 18:11
  • @Rob, i tried in your way but getting NSInternalInconsistencyException. [here](http://stackoverflow.com/questions/35249845/adding-child-progress-for-multiple-file-download) you can see my code. thanks. – Nuibb Feb 07 '16 at 04:50
  • @Nuibb - Once you implemented `observeValueForKeyPath`, like above, was your problem resolved? – Rob Feb 07 '16 at 05:24
  • Yes, that was the missing link. It's resolved now. Thanks. – Nuibb Feb 07 '16 at 05:27
  • @Rob Possible can I ask one question related to progress bar? Actually I am struggling since last week couldn't get proper solution for it. – Newbie Feb 04 '20 at 14:58