无法从 URLSessionTaskDelegate 获取数据
Posted
技术标签:
【中文标题】无法从 URLSessionTaskDelegate 获取数据【英文标题】:Cannot get data from URLSessionTaskDelegate 【发布时间】:2017-09-24 20:05:44 【问题描述】:我正在尝试向我的应用程序添加后台获取功能。目前网络调用完成后会调用委托函数urlSession(_ session: 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()
在我的 App Delegate 中:
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
print("Executing background fetch")
DownloadManager.shared.fetch()
completionHandler(.newData)
我错过了什么?
【问题讨论】:
您的代码在我尝试时似乎可以正常工作。您的 URL 是否可能以http
而不是 https
开头?这可能会使其与 App Transit Security 发生冲突。
他们都是https
。你是如何模拟这个后台抓取的?
我刚刚将您的课程复制到 CodeRunner 中,将 URL 替换为我网站上的文件,并在底部添加了一行以调用 DownloadManager.shared.fetch()
。它似乎下载正确。
@AndrewWalz 你能发一个小项目来证明这个问题吗?
@AndrewWalz 你检查我的答案了吗?它对你有用吗?
【参考方案1】:
试试这个:
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)
替换你的DownloadManager
:
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()
在我的 App Delegate 中:
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void)
debugPrint("handleEventsForBackgroundURLSession: \(identifier)")
completionHandler()
参考:Tutorial by ralfebert
【讨论】:
以上是关于无法从 URLSessionTaskDelegate 获取数据的主要内容,如果未能解决你的问题,请参考以下文章
DateTimeParseException:无法解析文本:无法从 TemporalAccessor 获取 LocalDateTime