Ios 无法从URLSessionTaskDeleteGate获取数据

Ios 无法从URLSessionTaskDeleteGate获取数据,ios,swift,swift3,nsurlsession,nsurlsessiondownloadtask,Ios,Swift,Swift3,Nsurlsession,Nsurlsessiondownloadtask,我正在尝试将后台提取功能添加到我的应用程序中。当前,在网络调用完成后,将调用委托函数urlSession(\usession:urlSession,downloadTask:URLSessionDownloadTask,didFinishDownloadingTo location:URL),但缓存目录中的URL不存在: class DownloadManager: NSObject, URLSessionTaskDelegate, URLSessionDownloadDelegate { s

我正在尝试将后台提取功能添加到我的应用程序中。当前,在网络调用完成后,将调用委托函数
urlSession(\usession:urlSession,downloadTask:URLSessionDownloadTask,didFinishDownloadingTo location:URL)
,但缓存目录中的URL不存在:

class DownloadManager: NSObject, URLSessionTaskDelegate, URLSessionDownloadDelegate {

static var shared = DownloadManager()

var session : URLSession {
    get {
        let config = URLSessionConfiguration.background(withIdentifier: "my_Identifier")
        return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
    }
}

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
    print(location.absoluteString)
    do {
        let myData = try Data(contentsOf: location)
    } catch let error {
        print(error.localizedDescription)
      // The The file “file_id.tmp” couldn’t be opened because there is no such file.
    }
}

public func fetch() {
    guard let url = URL(string: "#{myURL}") else {
        return
    }

    let task = session.downloadTask(with: url)
    task.resume()
  }
}
在我的应用程序代理中:

func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    print("Executing background fetch")
    DownloadManager.shared.fetch()
    completionHandler(.newData)
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
        debugPrint("handleEventsForBackgroundURLSession: \(identifier)")
        completionHandler()
    }
我缺少什么?

尝试使用以下方法:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var progressView: UIProgressView!

    override func viewDidLoad() {
        let _ = DownloadManager.shared.activate()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        DownloadManager.shared.onProgress = { (progress) in
            OperationQueue.main.addOperation {
                self.progressView.progress = progress //assign progress value
            }
        }
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        DownloadManager.shared.onProgress = nil
    }

    @IBAction func startDownload(_ sender: Any) {
        let url = URL(string: "YourFileURL")!
        DownloadManager.shared.download(url)
    }

}

更换您的
下载管理器

    import Foundation

class DownloadManager : NSObject, URLSessionDelegate, URLSessionDownloadDelegate {

    static var shared = DownloadManager()
    var url : URL?
    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())
    }

    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 {
                    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("Progress \(downloadTask) \(progress)")

        }
    }

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        debugPrint("Download finished: \(location)")
//        try? FileManager.default.removeItem(at: location)
        //copy downloaded data to your documents directory with same names as source file
        let documentsUrl =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
        let destinationUrl = documentsUrl!.appendingPathComponent(url!.lastPathComponent)
        let dataFromURL = try? Data(contentsOf: location)
        try? dataFromURL?.write(to: destinationUrl, options: [.atomic])
        print(destinationUrl)
        //now it is time to do what is needed to be done after the download
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        debugPrint("Task completed: \(task), error: \(String(describing: error))")
    }


    func download(_ url: URL)
    {
        self.url = url

        //download identifier can be customized. I used the "ulr.absoluteString"
        let task = DownloadManager.shared.activate().downloadTask(with: url)
        task.resume()

    }
}

在我的应用程序代理中:

func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    print("Executing background fetch")
    DownloadManager.shared.fetch()
    completionHandler(.newData)
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
        debugPrint("handleEventsForBackgroundURLSession: \(identifier)")
        completionHandler()
    }

参考资料:ralfebert

当我尝试时,你的代码似乎能工作。您的URL是否可能以
http
而不是
https
开头?这可能会使它与应用程序传输安全相冲突。它们都是
https
。你是如何模拟这种后台获取的?我刚刚将你的类复制到CodeRunner中,将URL替换为我网站上的一个文件,并在底部添加了一行,以调用
DownloadManager.shared.fetch()
。它似乎下载正确。@AndrewWalz你能发布一个小项目来演示这个问题吗?@AndrewWalz你检查过我的答案了吗?它对你有用吗?