Добавлена ​​задача загрузки - PullRequest
0 голосов
/ 28 апреля 2019

Я пытаюсь реализовать менеджер фоновых загрузок для моего приложения. Все работает плавно Но проблема, с которой я сталкиваюсь, заключается в том, что, когда я возвращаюсь к контроллеру представления загрузчика, размер загрузки увеличивается каждый раз. Так может быть каждый раз, когда новая задача загрузки становится в очередь. Я попробовал следующий способ справиться с этим.

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}
        }
    }
}
...