I'm trying to download multiple files simultaneously using NSURLSession in Swift. I want to merge all the download progress status into one as to show 100% when all the files are downloaded. Currently I get 100% for each file download completion, but I need 100% only if all the files are downloaded. How can I achieve this in Swift ?
Here is my DownloadManager Class :
class DownloadManager : NSObject, URLSessionDelegate, URLSessionDownloadDelegate {
static var shared = DownloadManager()
var task1Progress = 0.00
var task2Progress = 0.00
typealias ProgressHandler = (Float) -> ()
var onProgress : ProgressHandler? {
didSet {
if onProgress != nil {
let _ = activate()
}
}
}
override private init() {
super.init()
}
func activate() -> URLSession {
let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).background")
// Warning: If an URLSession still exists from a previous download, it doesn't create a new URLSession object but returns the existing one with the old delegate object attached!
return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
}
func calculateProgress(session : URLSession, completionHandler : @escaping (Float) -> ()) {
session.getTasksWithCompletionHandler { (tasks, uploads, downloads) in
let progress = downloads.map({ (task) -> Float in
if task.countOfBytesExpectedToReceive > 0 {
return Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive)
} else {
return 0.0
}
})
completionHandler(progress.reduce(0.0, +))
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if totalBytesExpectedToWrite > 0 {
if let onProgress = onProgress {
calculateProgress(session: session, completionHandler: onProgress)
}
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
debugPrint("Download Progress \(downloadTask) \(progress)")
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
debugPrint("Download finished: \(location)")
try? FileManager.default.removeItem(at: location)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
debugPrint("Task completed: \(task), error: \(String(describing: error))")
}
private func calculateProgress(session : URLSession, completionHandler : @escaping (Float) -> ()) {
session.getTasksWithCompletionHandler { (tasks, uploads, downloads) in
let progress = downloads.map({ (task) -> Float in
if task.countOfBytesExpectedToReceive > 0 {
if (task.taskIdentifier == 1) {
self.task1Progress = Double(Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive))
} else if (task.taskIdentifier == 2){
self.task2Progress = Double(Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive))
}
print("pro1 = \(self.task1Progress) pro2 = \(self.task2Progress)")
if(self.task1Progress>0.0000 && self.task2Progress>0.000) {
return Float(min(self.task1Progress ,self.task2Progress))
}
return Float(max(self.task1Progress ,self.task2Progress))
} else {
return 0.0
}
})
completionHandler(progress.reduce(0.0, +))
}
}
}
You can use dispatchgroup tp achieve the desired behaviour.
To download file you will call shared method of
downloadManager. Dispatch the downloading operation on dispatchGroup and in notify you will get the callback when all files are downloaded.Please find example below :