4

I have background downloading zip file:

if let url = NSURL(string: urlstring) 
         {            
            let config = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier((NSUUID().UUIDString))
            let session = NSURLSession(configuration: config, delegate: self, delegateQueue: nil)
            let task = session.downloadTaskWithURL(url)
            session.sessionDescription = filepath
            if let sessionId = session.configuration.identifier
            {
                print("start zip session: " + sessionId)
            }

            task.resume()               
            }
        }

it works cool if you have internet connection but if you lose it during downloading app just wait and URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) will not be called never. How it can be handle?
Something like time for response from server

3 Answers 3

5

The question is old, but still unanswered, so here is one workaround.

Some clarifications first

In general there is a property of URLSessionConfiguration called waitsForConnectivity which can be set to false where URLSessionTask will then directly fail on connection lost. However if true then URLSessionTaskDelegate will receive a callback on the urlSession(_:taskIsWaitingForConnectivity:) method.

However

Background tasks such as DownloadTask always wait for connectivity and ignore waitsForConnectivity property of URLSessionConfiguration. They also DON'T trigger urlSession(_:taskIsWaitingForConnectivity:) callback, so there is no official way to listen for connectivity drop-out on download task.

Workaround

If you listening for the download progress you'll notice that a call to the method is done few times in a second. Therefore we can conclude that if the progress callback is not called for more than 5 seconds, then there could be a connectivity drop issue. So the workaround is to make additional property to the URLSessionDownloadDelegate delegate and store the last update of the progress. Then have interval function to periodically check whether this property were not updated soon.

Something like:

    class Downloader: NSObject, URLSessionTaskDelegate, URLSessionDownloadDelegate {

        var lastUpdate: Date;
        var downloadTask: URLSessionDownloadTask?;

        public var session : URLSession {
            get {
                let config = URLSessionConfiguration.background(
                withIdentifier: "\(Bundle.main.bundleIdentifier!).downloader");
                config.isDiscretionary = true;

                return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue());
            }
        }

        override init() {
            self.lastUpdate = Date();
            super.init();
        }

        func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
            // Handle download completition
            // ...
        }

        func urlSession(
            _ session: URLSession,
            downloadTask: URLSessionDownloadTask,
            didWriteData bytesWritten: Int64,
            totalBytesWritten writ: Int64,
            totalBytesExpectedToWrite exp: Int64)
        {
            let progress = 100 * writ / exp;
            // Do something with the progress
            // ...

            self.lastUpdate = Date();
        }
    }

    var downloader = Downloader();
    let url = "http://...";
    var request = URLRequest(url: URL(string: url)!);
    currentWorker.downloadTask = downloader.session.downloadTask(with: request);
    currentWorker.downloadTask!.resume();

    // Schedule timer for every 5 secs
    var timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "CheckInternetDrop", userInfo: nil, repeats: true);

func CheckInternetDrop(){
    let interval = Date().timeIntervalSinceDate(downloader.lastUpdate);
    if (interval > 5) {
        print("connection dropped");
    }
}

Very simple example...

Sign up to request clarification or add additional context in comments.

1 Comment

Background sessions don't need isDiscretionary, because they always wait for network connectivity.
1

According to apple docs on pausing and resuming downloads :

Also, downloads that use a background configuration will handle resumption automatically, so manual resuming is only needed for non-background downloads.

Besides, the reason waitsForConnectivity is being ignored is stated in the downloading files in the background :

If your app is in the background, the system may suspend your app while the download is performed in another process.

This is why even if you were to build a timer in a similar fashion to Dimitar Atanasov it will not work once the app goes in the background since it will be suspended.

I've tested the background downloading myself with network failures and the system resumes without fail, hence no extra code is required.

1 Comment

Author of the post is asking different question! He knows that the background download will continue when the connection is available. He's asking if there is a way of receiving notification when that happens. No matter if the cable is unplugged or the system is suspending the background process, the data transfer will pause. If you're building a download manager where you display the speed and remaining time you will most probably want to show the users that the download is paused. The provided solution is not the cleanest one, but there is no official way - that's why it's called workaround.
0

You can set timeouts for your requests:

config.timeoutIntervalForRequest = <desired value>
config.timeoutIntervalForResource = <desired value>

Documentation:

3 Comments

but if I will download something big it will fail. time of downloading depending on the file size and the speed of the Internet
Hm, I wonder why you are not getting a NSURLErrorNetworkConnectionLost when you lose the connection?
I add func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) and func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) but. when I lost connection they are not called

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.