Swift: How to catch disk full error on background URLSession.downloadTask?

不羁岁月 提交于 2021-01-28 13:38:16

问题


I'm struggling to understand what I thought would be easy.

I have a URLSession.downloadTask. I have set my downloading object as the URLSession delegate and the following delegate methods do receive calls, so I know my delegate is set correctly.

func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?)
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)

The case I can't trap is when the downloadTask fills up the disk space on the iPad. None of those delegate methods get called.

How should I catch this error?

Here's my download object:

import Foundation
import Zip
import SwiftyUserDefaults

extension DownloadArchiveTask: URLSessionDownloadDelegate {

    // Updates progress info
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
                    didWriteData bytesWritten: Int64, totalBytesWritten: Int64,
                    totalBytesExpectedToWrite: Int64) {


        let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
        self.delegate?.updateProgress(param: progress)
    }

    // Stores downloaded file
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {

        print("DownloadArchiveTask: In didFinishDownloadingTo")

    }
}

extension DownloadArchiveTask: URLSessionTaskDelegate {

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        print("DownloadArchiveTask: In didCompleteWithError")
        if error != nil {
            print("DownloadArchiveTask: has error")
            self.delegate?.hasDiskSpaceIssue()
        }
    }
}

extension DownloadArchiveTask: URLSessionDelegate {
    // Background task handling
    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        print("DownloadArchiveTask: In handler for downloading archive")
        DispatchQueue.main.async {
            let sessionIdentifier = session.configuration.identifier
            if let sessionId = sessionIdentifier, let app = UIApplication.shared.delegate as? AppDelegate, let handler = app.completionHandlers.removeValue(forKey: sessionId) {
                handler()
            }
        }
    }
    func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
        print("DownloadArchiveTask: didBecomeInvalidWithError")
        if error != nil {
            print("DownloadArchiveTask: has error")
            self.delegate?.hasDiskSpaceIssue()
        }
    }
}

class DownloadArchiveTask: NSObject {
    var delegate: UIProgressDelegate?
    var archiveUrl:String = "http://someurl.com/archive.zip"

    var task: URLSessionDownloadTask?

    static var shared = DownloadArchiveTask()

    // Create downloadsSession here, to set self as delegate
    lazy var session: URLSession = {
        let configuration = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).background_archive")
        return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
    }()

    func initialDownload() {
        // Create URL to the source file you want to download
        let fileURL = URL(string: archiveUrl)

        let request = URLRequest(url:fileURL!)

        self.task = self.session.downloadTask(with: request)
        task?.resume()

    }
}

Anyone done this before? I can't believe it's this hard - I must be approaching the problem in the wrong way...


回答1:


I had to solve this problem for my company not that long ago. Now my solution is in Objective C so you'll have to convert it over to Swift, which shouldn't be that hard. I created a method that got the available storage space left on the device and then checked that against the size of the file we're downloading. My solution assumes you know the size of the file you download, in your case you can use the totalBytesExpectedToWrite parameter in the didWriteData method.

Here's what I did:

+ (unsigned long long)availableStorage
{
     unsigned long long totalFreeSpace = 0;
     NSError* error = nil;

     NSArray* paths = NSSearchPathForDirectoriesInDomain(NSDocumentDirectory, NSUserDomainMask, YES);
     NSDictionary* dictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error:&error];
     if (dictionary)
     {
          NSNumber* freeFileSystemSizeInBytes = [dictionary objectForKey:NSFileSystemFreeSize];
          totalFreeSpace = [freeFileSystemSizeInBytes unsignedLongLongValue];
     }
     return totalFreeSpace;
}

Make sure you leave some room for error with these numbers, since iTunes, the settings app on the device, and this number never match up. The number we get here is the smallest of the three and is in MB. I hope this helps and let me know if you need help converting it to Swift.



来源:https://stackoverflow.com/questions/55304446/swift-how-to-catch-disk-full-error-on-background-urlsession-downloadtask

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!