6

I have a syncing functionality, where a series of downloads happens in the background.

For that, I create a URLSession instance with background configuration as below:

let config = URLSessionConfiguration.background(withIdentifier: "BackgroundSessionDL")
config.httpMaximumConnectionsPerHost = 20

self.session = URLSession(configuration: config, delegate: self, delegateQueue: nil)

Each of the download tasks (which may be in a number of few hundreds) is added as follows:

var request = URLRequest(url: url)
request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")

let downloadTask = self.session.downloadTask(with: request)
downloadTask.resume()

This code works perfectly on iOS 12 or earlier - the download continues even in the background for the tasks already added. But, for iOS 13, causes issue as follows:

  1. As soon as app goes to background, downloading stops.
  2. If any download task is in progress and app is put to background, no callaback related logs are observed even after app is again brought to foreground.

I tried to search whether any new classes/framework introduced by Apple for background task download on iOS 13, but did not find anything on this. Also, class used in earlier versions, for representation of a background download task, is still NOT deprecated in iOS 13 and is the only result when searched for background download on iOS 13. Similarly, background configuration used for the HTTP session, is still NOT deprecated. So, I believe the current background download implementation is the one recommended by Apple. (As per my understanding, "BGTaskScheduler" is not intended for this, please correct me if wrong).

So, is this due to any newly introduced bug in iOS 13? OR any change in this implementation, specific to iOS 13, is expected by Apple?

Edit:

I have already added the handleEventsForBackgroundURLSession callback. Found that the respective logs are not printed and app size does not increase in Settings on iOS 13. So, it appears that download does not continue in the background and it is not related to download related callbacks not getting invoked.

Harish J
  • 525
  • 1
  • 4
  • 16
  • Did you figure out how to overcome this behavior? I have the same problem and already spent days trying to figure it out... Everything works perfectly on iOS13- – Exception Feb 23 '20 at 00:33

1 Answers1

3

I guess that if you run the app in the foreground then everything works as expected however as soon as the app is put into the background no callback are received anymore

BackgroundDownloader only works when the app is in the foreground but there is a specific support when the app enters a suspended/backgrounded state.

When an app is in a suspended/terminated state and a download is completed, iOS will wake the app up (in the background) by calling:

class AppDelegate: UIResponder, UIApplicationDelegate { 

    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
        //TODO: Handle processing downloads
    }
}

The behavior change between ios12 and ios13 is probably not related to some API changes but just with a more aggressive power saving policy for app while they are not in foreground

Modify the URLSession background configuration identifier Making the isDiscretionary as false in order to avoid OS scheduling the background request transfers due to battery or performance The most important one shouldUseExtendedBackgroundIdleMode as true. This is the ultimate line that makes the TCP sockets open even when the app is locked or suspended.

cristallo
  • 1,951
  • 2
  • 25
  • 42
  • thanks for your reply. Your question made me aware that the question was a bit ambiguous, so have re-worded it. I have already added the "handleEventsForBackgroundURLSession" callback, and found that the respective logs are not printed. – Harish J Jan 27 '20 at 09:57
  • Can you verify that all of your background downloads are finishing? If one of them hangs or fails, it will prevent the completion of the background session to be triggered. The delegate method is only called when everything is done, that means that your app will not be activated. Have you checked the device's console for some diagnostic error messages? – cristallo Jan 27 '20 at 10:29
  • Hi cristallo, the background downloads do not complete (There are around a few hundred files, of which at least 2-3 are started parallely). Rather I do not see any update for a download ongoing when app goes to background. This is evident from the app size in the settings. I want to figure out the same... why the download does not continue when app is put to background. – Harish J Jan 27 '20 at 10:49
  • Did you properly configure urlsession with shouldUseExtendedBackgroundIdleMode and discretionary mode? – cristallo Jan 27 '20 at 11:20
  • Yes, I tried with shouldUseExtendedBackgroundIdleMode. Also, tried with both values true and false for isDiscretionary. Yet, it is not working. I cannot see the log for the method - func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) which is invoked after completion of individual task. – Harish J Jan 28 '20 at 13:30
  • Have you checked the device's console for diagnostic error messages? – cristallo Jan 28 '20 at 14:20
  • Yes, I ca n see the logs like this related to nsurlsessiond: logTaskError isDownload = Y bytesReceived = 111134 bytesSent = 0 isRecoverable = N – Harish J Jan 28 '20 at 14:45
  • I am pretty confused ... I've try to run then following example on ios13 and it is working fine. https://www.raywenderlich.com/3244963-urlsession-tutorial-getting-started Can you try to do the same? just to see if it works or not. It looks pretty similar to your use case – cristallo Jan 28 '20 at 15:12