Je commence un téléchargement dans une extension d'action (ActionRequestHandler
) comme ceci:URLSessionDownloadDelegate didFinishDownloadingTo pas appelé en arrière-plan
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
}
Ensuite, le téléchargement est traité par iOS en arrière-plan.
Je veux maintenant gérer le téléchargement fini dans mon application principale AppDelegate
, parce que c'est ce que iOS appelle lorsque le téléchargement est terminé.
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping() -> Void) {
print("handleEventsForBackgroundURLSession")
urlSessionBackgroundCompletionHandler = completionHandler
}
Cette méthode est appelée en arrière-plan après un certain temps comme prévu.
Mes AppDelegate
implémente également URLSessionDelegate
et URLSessionDownloadDelegate
pour traiter les mises à jour pour le téléchargement.
Particulièrement intéressants sont
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
print("urlSessionDidFinishEvents")
self.urlSessionBackgroundCompletionHandler?()
self.urlSessionBackgroundCompletionHandler = nil
}
}
et
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)")
}
}
Les deux urlSessionDidFinishEvents
et didFinishDownloadingTo
ne sont pas appelés après handleEventsForBackgroundURLSession
été appelé en arrière-plan. Ce n'est qu'après avoir relancé l'application au premier plan que les méthodes déléguées sont appelées. Pourquoi ne sont-ils pas appelés et qu'est-ce que je peux faire pour résoudre ce problème?
J'ai essayé de créer le URLSession
dans handleEventsForBackgroundURLSession
comme ceci:
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
}
Cependant, cela n'a pas résolu le problème.
Avant de demander: Oui, je le teste sur un périphérique réel car le simulateur a des problèmes avec la gestion des tâches d'arrière-plan.
J'ai l'impression que ce problème est spécifique aux extensions, parce que le code me convient; c'est probablement une question de Quinn. Demandez sur les forums des développeurs. En termes de conseils généraux, il n'y a aucune raison d'exécuter les rappels de session sur le thread principal; vous envoyez déjà le callback critique sur le thread principal. Il suffit de passer rien. Mais je suis assez sûr que ce n'est pas la cause de votre problème. – dgatwood