Store URLSessionDownloadTask in Core Data

884 Views Asked by At

I want to save URLSessionDownloadTask in core data when app is gone in closed state or my download state is changed e.g from waiting state to downloading state or to completed state.

All other attributes of my custom class are stored perfectly but app crashes when it stores download task.

reason to crash is

[__NSCFLocalDownloadTask encodeWithCoder:]: unrecognized selector sent to instance 0x7ff189f181c0 -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it. Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFLocalDownloadTask encodeWithCoder:]: unrecognized selector sent to instance 0x7ff189f181c0'

this is my class

class VideoDownloadModel : NSManagedObject {

    @NSManaged var videoID : NSNumber?
    @NSManaged var vid : Video?
    @NSManaged var downloadTask : URLSessionDownloadTask?
    @NSManaged var downloadStatus : String?
}

storing it like this

    let request = NSFetchRequest<NSFetchRequestResult>(entityName: (COREDATA_ENTITY_Description?.name)!)
    request.returnsObjectsAsFaults = false
    request.predicate = NSPredicate(format: "videoID == %@", videoModel.videoID!)

        do {
            let result = try COREDATA_CONTEXT.fetch(request)
            print(result)
            var vidArr = result as! [VideoDownloadModel]

            if vidArr.count != 0 {
                vidArr[0] = videoModel

                COREDATA_MANAGER.saveContext()
            }

        } catch {
            let fetchError = error as NSError
            print(fetchError)
        }

when URLSessionDownlaodTask is nil it works fine but when any download is started it crashes on saving.

scenerio :

  • I initialized my custom class object with all attributes but set task to nil.

  • I store that object in core data it saved perfectly.

  • I initialize the task of that object the download work perfectly.

  • Then i update the object in core data while updating the app got crash because URLSssionTask is not inheriting from NSCoding. so it don't have encoding and decoding methods.

I want some solution to solve this issue

Any help will be appreciated. Thanks.

2

There are 2 best solutions below

2
On

Saving URLSessionDownloadTask doesn't make sense. You would actually want to save the data obtained from downloadTask.cancel(byProducingResumeData: ) while pausing.

Once you need to resume the download create a new downloadtask with the saved data by using downloadTaskWithResumeData(:) and resume.

When app is terminated - dosen't include the case when the app is forcefully terminated by the user.

The apple documentation on URLSession clearly explains what to do when app is terminated.
From the docs - In both iOS and OS X, when the user relaunches your app, your app should immediately create background configuration objects with the same identifiers as any sessions that had outstanding tasks when your app was last running, then create a session for each of those configuration objects. These new sessions are similarly automatically reassociated with ongoing background activity.

When user forcefully terminate the app

In this case, URLSession delegate urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) will be fired, in which the userInfo dict in error object will have the resume data corresponding to the key NSURLSessionDownloadTaskResumeData, which could be used to resume the task using downloadTask.cancel(byProducingResumeData: ). Also please note that you will have to typecast error to NSError to retrieve the userInfo dict.

It would be good to read through the docs here before using NSURLSession

1
On

You can't save the URLSessionDownloadTask in Core Data because-- as you mentioned-- it doesn't conform to NSCoding. In many cases the answer would be to write your own code to convert to/from Data but that doesn't work in this case. A URLSessionDownloadTask can only be created by a URLSession, so you can't serialize and deserialize the task object.

That doesn't really matter though because saving and restoring them doesn't make sense. A URLSessionDownloadTask represents something that is in progress while the app is running. If your app is closed, that activity ends. Restoring a URLSessionDownloadTask after the app closes and relaunches doesn't make sense because there's no activity for it to represent. Basically, even if you could store the task object and restore it, it would be useless after restoring. There's no reason to bother.

If your interest is that you want to resume incomplete downloads, you'll have to start over from the beginning. Create your URLSession and then use it to create a new URLSessionDownloadTask. If your interest is in getting information about a background download, you can use the session object with getTasksWithCompletionHandler(_:) to find out whether they completed.