我正在尝试为我的应用程序实现后台下载管理器。一切顺利。但是我面临的问题是,当我返回到下载器视图控制器时,每次下载大小都会增加。因此,可能是每当新的下载任务排队时。我尝试过以下处理方式。
if let task = Downloader.shared.task {
task.resume()
} else {
Downloader.shared.startDownload(url: url)
}
我的下载管理器课程
//
// Downloader.swift
// Quran Touch
//
// Created by Ahsan Aasim on 28/4/19.
// Copyright © 2019 Ahsan Aasim. All rights reserved.
//
import Foundation
import Zip
import UserNotifications
public struct DownloadProgress {
public let name: String
public let progress: Float
public let completedUnitCount: Float
public let totalUnitCount: Float
}
class Downloader : NSObject, URLSessionDelegate, URLSessionDownloadDelegate {
static var shared = Downloader()
let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).audioDownloader")
var session: URLSession!
var task: URLSessionDownloadTask?
typealias ProgressHandler = (String, Float, Float, Float) -> ()
//
// var onProgress : ProgressHandler? {
// didSet {
// if onProgress != nil {
// let _ = activate()
// }
// }
// }
override private init() {
super.init()
session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
}
func activate() -> URLSession {
// 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 session
}
func startDownload(url: URL) {
task = Downloader.shared.activate().downloadTask(with: url)
task?.resume()
}
private func calculateProgress(session : URLSession, completionHandler : @escaping ProgressHandler) {
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
}
})
let countOfBytesReceived = downloads.map({ (task) -> Float in
return Float(task.countOfBytesReceived)
})
let countOfBytesExpectedToReceive = downloads.map({ (task) -> Float in
return Float(task.countOfBytesExpectedToReceive)
})
if let name = UserDefaults.standard.string(forKey: UserDefaultKeys.downloadingAudio) {
if name.isEmpty {
return self.session.invalidateAndCancel()
}
completionHandler(name, progress.reduce(0.0, +), countOfBytesReceived.reduce(0.0, +), countOfBytesExpectedToReceive.reduce(0.0, +))
}
}
}
func postUnzipProgress(progress: Double) {
NotificationCenter.default.post(name: .UnzipProgress, object: progress)
}
func postNotification() {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization.
}
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey: "Download Completed", arguments: nil)
content.body = NSString.localizedUserNotificationString(forKey: "Your reciter is ready to play offline", arguments: nil)
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "com.qurantouch.qurantouch.audioDownloader"
// Deliver the notification in 60 seconds.
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 2.0, repeats: false)
let request = UNNotificationRequest.init(identifier: "audioDownloadCompleted", content: content, trigger: trigger)
// Schedule the notification.
center.add(request)
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if totalBytesExpectedToWrite > 0 {
calculateProgress(session: session, completionHandler: { (name, progress, completedUnitCount, totalUnitCount) in
let progressInfo = DownloadProgress(name: name, progress: progress, completedUnitCount: completedUnitCount, totalUnitCount: totalUnitCount)
NotificationCenter.default.post(name: .AudioDownloadProgress, object: progressInfo)
})
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
if let name = UserDefaults.standard.string(forKey: UserDefaultKeys.downloadingAudio) {
if name.isEmpty {
return self.session.invalidateAndCancel()
}
let folder = URL.createFolder(folderName: "\(Config.audioFolder)\(name)")
let fileURL = folder!.appendingPathComponent("\(name).zip")
if let url = URL.getFolderUrl(folderName: "\(Config.audioFolder)\(name)") {
do {
try FileManager.default.moveItem(at: location, to: fileURL)
try Zip.unzipFile((fileURL), destination: url, overwrite: true, password: nil, progress: { (progress) -> () in
self.postUnzipProgress(progress: progress)
if progress == 1 {
DispatchQueue.main.async {
Reciter().downloadCompleteReciter(success: true).done{_ in}.catch{_ in}
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let backgroundCompletionHandler =
appDelegate.backgroundCompletionHandler else {
return
}
backgroundCompletionHandler()
self.postNotification()
}
URL.removeFile(file: fileURL)
}
}, fileOutputHandler: {(outputUrl) -> () in
})
} catch {
print(error)
}
}
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
debugPrint("Task completed: \(task), error: \(error)")
DispatchQueue.main.async {
Reciter().downloadCompleteReciter(success: false).done{_ in}.catch{_ in}
}
}
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
DispatchQueue.main.async {
Reciter().downloadCompleteReciter(success: false).done{_ in}.catch{_ in}
}
}
}