Swift: UIViewController 实例未从 URLSession 委托方法接收更新
Swift: UIViewController instance not receiving updates from URLSession Delegate methods
我无法从 URLSession Delegate 方法更新 UIViewController。
我有一个包含不同用户的 TableView 和一个包含与该用户关联的多个图像的详细视图控制器。
现在我需要下载图像,我创建了一个背景 URL session,每个用户都有一个唯一标识符。我这样做是因为我必须为 TableView 中的每个用户使用相同的 Detail UIViewController。
>DetailViewController
// Download service for this controller. Shared instance because it tracks active downloads. /// [URL:Download]
let downloadService:DownloadService = MediaDownloadManager.shared.downloadService
// A Unique background download session for each user
lazy var downloadSession: URLSession = {
let identifer:String = "com.example.app" + "-user-" + userID /// userID passed from parent
let configuration = URLSessionConfiguration.background(withIdentifier: identifer)
return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}()
override func viewDidLoad() {
super.viewDidLoad()
downloadService.downloadsSession = downloadSession
/// I get this every time when I exit and re-enter the controller which is obvious.
/// [DEBUG CONSOLE] A background URLSession with identifier com.example.app-user-12 already exists!
}
并且我已将 URLSession Delegates 纳入我的 DetailViewController 以跟踪下载进度和更新视图。
extension DetailViewController: URLSessionDelegate, URLSessionDownloadDelegate {
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { /// }
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { /// }
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { /// }
}
现在的问题是,当我 运行 应用程序并为用户打开 DetailViewController 并开始下载我需要的图像时,它工作正常并且我从 urlsession 委托方法,然后更新我的 tableView。
但是当我滑回 TableViewController 然后为同一用户重新打开 DetailViewController 并开始下载图像时,DetailViewController 的 tableView 不会从那些 urlsession 委托方法接收更新。
I found what's exactly causing this problem. The URLSession Delegate
methods capture the instance of my view controller. Which means when I
update my DetailViewController's TableView, from the URLSession
Delegate methods, they actually try to update the tableView of the
first and former UIViewController instance.
下面是更新单元格进度的代码。
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
guard let downloadURL = downloadTask.originalRequest?.url else {
printAndLog(message: "### \(#function) Failed to retrieve original request download url", log: .network, logType: .error)
return
}
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
let downloadedSize = ByteCountFormatter.string(fromByteCount: totalBytesWritten, countStyle: .file)
let totalSize = ByteCountFormatter.string(fromByteCount: totalBytesExpectedToWrite, countStyle: .file)
print(self) /// Same instance everytime.
if let download = downloadService.activeDownloads[downloadURL] {
download.progress = progress
DispatchQueue.main.async {
if let index = self.images.firstIndex(of: download.image),
let cell = self.tableView.cellForRow(at: IndexPath(row: index, section: 0)) as? ImageCell {
cell.updateProgress(progress: download.progress, loadedSize: downloadedSize, totalSize: totalSize)
}
}
}
}
How can I solve this annoying problem? I know singletons are a good
answer but I want to avoid singletons as much possible.
我无法使用 downloadSession.finishTasksAndInvalidate()
,因为我认为当我退出控制器时它不会继续我的下载。
我的要求:
1-) 当我退出控制器时,下载应该会继续。
2-) DetailViewController 的每个实例都应该可以下载,即每个用户
3-) 后台下载当然是必须的。
如果您认为此问题的标题有误,请见谅。
您需要添加另一个抽象级别。视图不应直接订阅后台下载进程。其他一些 class 应该订阅它,并且应该从其他 class.
更新视图
这样您只有一次 class 订阅,即使多个视图需要数据也是如此。
我无法从 URLSession Delegate 方法更新 UIViewController。
我有一个包含不同用户的 TableView 和一个包含与该用户关联的多个图像的详细视图控制器。
现在我需要下载图像,我创建了一个背景 URL session,每个用户都有一个唯一标识符。我这样做是因为我必须为 TableView 中的每个用户使用相同的 Detail UIViewController。
>DetailViewController
// Download service for this controller. Shared instance because it tracks active downloads. /// [URL:Download]
let downloadService:DownloadService = MediaDownloadManager.shared.downloadService
// A Unique background download session for each user
lazy var downloadSession: URLSession = {
let identifer:String = "com.example.app" + "-user-" + userID /// userID passed from parent
let configuration = URLSessionConfiguration.background(withIdentifier: identifer)
return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}()
override func viewDidLoad() {
super.viewDidLoad()
downloadService.downloadsSession = downloadSession
/// I get this every time when I exit and re-enter the controller which is obvious.
/// [DEBUG CONSOLE] A background URLSession with identifier com.example.app-user-12 already exists!
}
并且我已将 URLSession Delegates 纳入我的 DetailViewController 以跟踪下载进度和更新视图。
extension DetailViewController: URLSessionDelegate, URLSessionDownloadDelegate {
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { /// }
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { /// }
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { /// }
}
现在的问题是,当我 运行 应用程序并为用户打开 DetailViewController 并开始下载我需要的图像时,它工作正常并且我从 urlsession 委托方法,然后更新我的 tableView。
但是当我滑回 TableViewController 然后为同一用户重新打开 DetailViewController 并开始下载图像时,DetailViewController 的 tableView 不会从那些 urlsession 委托方法接收更新。
I found what's exactly causing this problem. The URLSession Delegate methods capture the instance of my view controller. Which means when I update my DetailViewController's TableView, from the URLSession Delegate methods, they actually try to update the tableView of the first and former UIViewController instance.
下面是更新单元格进度的代码。
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
guard let downloadURL = downloadTask.originalRequest?.url else {
printAndLog(message: "### \(#function) Failed to retrieve original request download url", log: .network, logType: .error)
return
}
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
let downloadedSize = ByteCountFormatter.string(fromByteCount: totalBytesWritten, countStyle: .file)
let totalSize = ByteCountFormatter.string(fromByteCount: totalBytesExpectedToWrite, countStyle: .file)
print(self) /// Same instance everytime.
if let download = downloadService.activeDownloads[downloadURL] {
download.progress = progress
DispatchQueue.main.async {
if let index = self.images.firstIndex(of: download.image),
let cell = self.tableView.cellForRow(at: IndexPath(row: index, section: 0)) as? ImageCell {
cell.updateProgress(progress: download.progress, loadedSize: downloadedSize, totalSize: totalSize)
}
}
}
}
How can I solve this annoying problem? I know singletons are a good answer but I want to avoid singletons as much possible.
我无法使用 downloadSession.finishTasksAndInvalidate()
,因为我认为当我退出控制器时它不会继续我的下载。
我的要求:
1-) 当我退出控制器时,下载应该会继续。
2-) DetailViewController 的每个实例都应该可以下载,即每个用户
3-) 后台下载当然是必须的。
如果您认为此问题的标题有误,请见谅。
您需要添加另一个抽象级别。视图不应直接订阅后台下载进程。其他一些 class 应该订阅它,并且应该从其他 class.
更新视图这样您只有一次 class 订阅,即使多个视图需要数据也是如此。