I start a download in an action extension (`ActionRequestHandler`) like this:
private lazy var urlSession: URLSession = {
let config = URLSessionConfiguration.background(withIdentifier: "de.stefantrauth.Downloader")
config.sharedContainerIdentifier = "group.de.stefantrauth.Downloader"
return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
}()
private func initiateDownloadOfFileFrom(url: URL) {
urlSession.downloadTask(with: url).resume()
completeRequest() // this tells the system the action extension is done with its work
}Then the download is processed by iOS in the background.
I now want to handle the finished download in my main application `AppDelegate`, because that is what iOS calls when the download has finished.
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
print("handleEventsForBackgroundURLSession")
urlSessionBackgroundCompletionHandler = completionHandler
}This method gets called in background after some time as expected.
My `AppDelegate` also implements `URLSessionDelegate` and `URLSessionDownloadDelegate` to process updates for the download.
Especially interesting are
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
print("urlSessionDidFinishEvents")
self.urlSessionBackgroundCompletionHandler?()
self.urlSessionBackgroundCompletionHandler = nil
}
}and
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("download finished to \(location.absoluteString)")
do {
let documentsURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let savedURL = documentsURL.appendingPathComponent(location.lastPathComponent)
try FileManager.default.moveItem(at: location, to: savedURL)
print("moved file to: \(savedURL.absoluteString)")
} catch {
print ("file error: \(error)")
}
}Both `urlSessionDidFinishEvents` and `didFinishDownloadingTo` are not being called after `handleEventsForBackgroundURLSession` got called in background. Only after relaunching the app into foreground the delegate methods get called.
Why are they not getting called and what can I do to fix that?
I tried creating the `URLSession` in `handleEventsForBackgroundURLSession`like this:
private func initUrlSessionWith(identifier: String) {
let config = URLSessionConfiguration.background(withIdentifier: identifier)
config.sharedContainerIdentifier = "group.de.stefantrauth.Downloader"
urlSession = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
print("handleEventsForBackgroundURLSession")
initUrlSessionWith(identifier: identifier)
urlSessionBackgroundCompletionHandler = completionHandler
}However this did not fix the problem.
Before you ask: Yes I am testing this on a real device because the simulator has problems with background task handling.
Dealing with background sessions in a short-lived app extension is quite tricky. You can find some general guidelines in my Networking in a Short-Lived Extension.
It’s hard to say what’s going on here but there’s a couple of things I suggest:
Implement
in both the app and the app extension so you can learn about invalidation.-URLSession:didBecomeInvalidWithError:Debug via logging, rather than with the debugging, for the reason explained in the above-mentioned post.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"