简体   繁体   English

Swift:UIViewController 实例未从 URLSession 委托方法接收更新

[英]Swift: UIViewController instance not receiving updates from URLSession Delegate methods

I'm unable to update UIViewController from URLSession Delegate methods.我无法从 URLSession Delegate 方法更新 UIViewController。

I have a TableView containing different users and a detail view controller which contains several images associated with that user.我有一个包含不同用户的 TableView 和一个包含与该用户关联的多个图像的详细视图 controller。

Now that I need to download images, I create a background URL session with a unique identifier for each user.现在我需要下载图像,我创建了一个背景 URL session,每个用户都有一个唯一标识符。 I'm doing this because I have to use the same Detail UIViewController for each user from TableView.我这样做是因为我必须为 TableView 中的每个用户使用相同的详细信息 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!
}

And I've conformed URLSession Delegates to my DetailViewController in order to track download progress and update views.为了跟踪下载进度和更新视图,我已将 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) { /// }
}

Now the problem is, when I run the app and open the DetailViewController for a user and start the download process for images I need, It works fine and I get updates from urlsession delegate methods which then updates my tableView.现在的问题是,当我运行应用程序并为用户打开 DetailViewController 并开始下载我需要的图像时,它工作正常,我从 urlsession 委托方法获取更新,然后更新我的 tableView。

But when I swipe back to TableViewController and then reopen the DetailViewController for that same user, and start downloading images, the DetailViewController's tableView does not receive update from those urlsession delegate methods.但是当我滑回 TableViewController 然后为同一用户重新打开 DetailViewController 并开始下载图像时,DetailViewController 的 tableView 不会从这些 urlsession 委托方法接收更新。

I found what's exactly causing this problem.我找到了导致这个问题的确切原因。 The URLSession Delegate methods capture the instance of my view controller. URLSession Delegate 方法捕获我的视图 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.这意味着当我从 URLSession Delegate 方法更新我的 DetailViewController 的 TableView 时,他们实际上尝试更新第一个和以前的 UIViewController 实例的 tableView。

Below is the code for updating cell progress.下面是更新单元格进度的代码。

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.我知道单身人士是一个很好的答案,但我想尽可能避免单身人士。

I can't use downloadSession.finishTasksAndInvalidate() as I think it will not continue my downloads when I exit the controller.我不能使用downloadSession.finishTasksAndInvalidate()因为我认为当我退出 controller 时它不会继续我的下载。

My Requirements:我的要求:

1-) Download should continue when I exit the controller. 1-) 当我退出 controller 时,应该会继续下载。

2-) Downloading should be possible for each instance of DetailViewController, ie, for each user 2-) DetailViewController 的每个实例(即每个用户)都应该可以下载

3-) Background downloads are a must of course. 3-) 后台下载当然是必须的。

Pardon if you think the title of this question is wrong.如果您认为这个问题的标题是错误的,请原谅。

You need to add another level of abstraction.您需要添加另一个抽象级别。 The views should not directly subscribe to the background download process.视图不应直接订阅后台下载过程。 Some other class should subscribe to that and the views should be updated from that other class.其他一些 class 应该订阅它,并且应该从其他 class 更新视图。

That way you only have that once class subscribing, even if multiple views need the data.这样,即使多个视图需要数据,您也只有一次 class 订阅。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM