Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to resume a download when the app force-Quit?

My app have to download a pretty big file ( 390Mb), I'm using TCBlopDownloadSwift for the download ( I converted it for swift 2.0 and it works fine) and I made the config for a background download . I want , when the app force quit to be able to resume the download . I found that when the app quit I can still found the downloaded data in the cache (in "com.apple.nsurlsessiond/Downloads/" + bundleIdentifier ) as a tmp file . But When I try to get the data of the download using :

func dataInCacheForName (name : String) -> NSData? {
    let PathToCache = (NSSearchPathForDirectoriesInDomains( NSSearchPathDirectory.CachesDirectory , .UserDomainMask, true)[0] as NSString).stringByAppendingPathComponent("com.apple.nsurlsessiond/Downloads/" + bundleIdentifier)
    let path = (PathToCache as NSString).stringByAppendingPathComponent(name)
    let data = NSData(contentsOfFile: path)
    print("data : \(data?.length)")
    return data
}

it returns nil , but the file isn't nil . I can move the file , so I tried to move the file in the Documents . But then if I try to resume the download with the data , I get errors :

-[NSKeyedUnarchiver initForReadingWithData:]: data is NULL 

-[NSKeyedUnarchiver initForReadingWithData:]: data is NULL 

-[NSKeyedUnarchiver initForReadingWithData:]: data is NULL 

Invalid resume data for background download. Background downloads must use http or https and must download to an accessible file.

and in the URLSession

(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError sessionError: NSError?)

error userInfo : Optional([:])

: Optional(Error Domain=NSURLErrorDomain Code=-3003 "(null)")

error code -3003 means impossible to write to file

I've read many post and yet can't find an answer

the most promising was https://forums.developer.apple.com/thread/24770

like image 332
kholl Avatar asked Dec 07 '15 11:12

kholl


1 Answers

Ok so , the problem come from the library ,I explain :

TCBlobDownloadSwift has a custom delegate , which is called at the end of the urlSessionDelegate method (example the custom delegate give a progress value instead of the totalByteWritten and totalBytesExpectedToWrite ) . :

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
    guard let download = self.downloads[downloadTask.taskIdentifier] else{
        return
    }
    let progress = totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown ? -1 : Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
    print("progress : \(progress)")
    // the delegate is in fact called download and has more parameter .
    customDelegate(download , progress : progress)
}

and It works fine . But when it come to resume a download when the app restart the download isn't registered and the downloadTask.taskIdentifier return nil so the custom delegate isn't called !!

In order to resume a download after a force-quit you have to use this code (the method is called when the object that follow the NSURLSessionDelegate Protocol has been created) :

public func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError sessionError: NSError?) {
    if let error = sessionError {
    print("error : \(error)")
    let directory = NSURL(fileURLWithPath: fileManage.Path)
    if let resumedData = error.userInfo[NSURLSessionDownloadTaskResumeData] as? NSData {
        let url = error.userInfo[NSURLErrorFailingURLStringErrorKey] as? String
        // get name from url just read a dictionnary of name and url
        let name = getNameFromURL(url!) 
        // start the download
        self.manager.downloadFileWithResumeData(resumedData, toDirectory: directory , withName: name, andDelegate: self)
        }
    }
}

I had to destroy the library (it's structure doesn't allow to resume download if the app force-Quit )

TLDR

If you use a library with a custom delegate (one different than NSURLSessionDelegate) the problem might come from the custom delegate who don't call the URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError sessionError: NSError?) method

PS: Thanks for the answer I understand How misleading is my Post .

I will try (if I have time) to work on a framework that can Allow to resume a download after the app force-Quit (it looks simple in fact you just have to add a delegate method for that particular case but if it is more complicated I don't have the time yet maybe later )

like image 86
kholl Avatar answered Oct 25 '22 14:10

kholl