2017-09-24 6 views
1

J'essaye d'ajouter la capacité d'extraction d'arrière-plan à mon application. À l'heure actuelle, la fonction de délégué urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) est appelée après l'appel du réseau est terminée, mais l'URL dans le répertoire du cache n'existe pas:Impossible d'obtenir des données à partir de URLSessionTaskDelegate

class DownloadManager: NSObject, URLSessionTaskDelegate, URLSessionDownloadDelegate { 

static var shared = DownloadManager() 

var session : URLSession { 
    get { 
     let config = URLSessionConfiguration.background(withIdentifier: "my_Identifier") 
     return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue()) 
    } 
} 

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { 
    print(location.absoluteString) 
    do { 
     let myData = try Data(contentsOf: location) 
    } catch let error { 
     print(error.localizedDescription) 
     // The The file “file_id.tmp” couldn’t be opened because there is no such file. 
    } 
} 

public func fetch() { 
    guard let url = URL(string: "#{myURL}") else { 
     return 
    } 

    let task = session.downloadTask(with: url) 
    task.resume() 
    } 
} 

Et dans mon délégué App:

func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { 
    print("Executing background fetch") 
    DownloadManager.shared.fetch() 
    completionHandler(.newData) 
} 

Qu'est-ce que je manque ?

+0

Votre code semble fonctionner quand je l'essaie. Est-il possible que votre URL commence par 'http' au lieu de' https'? Cela pourrait le faire aller à l'encontre de App Transit Security. –

+0

Ils sont tous 'https'. Comment simulez-vous cette recherche de fond? –

+0

Je viens de copier votre classe dans CodeRunner, j'ai remplacé l'URL par un fichier sur mon site web, et j'ai ajouté une ligne en bas pour appeler 'DownloadManager.shared.fetch()'. Il a semblé télécharger correctement. –

Répondre

1

Essayez d'utiliser ceci:

import UIKit 

class ViewController: UIViewController { 

    @IBOutlet weak var progressView: UIProgressView! 

    override func viewDidLoad() { 
     let _ = DownloadManager.shared.activate() 
    } 

    override func viewWillAppear(_ animated: Bool) { 
     super.viewWillAppear(animated) 
     DownloadManager.shared.onProgress = { (progress) in 
      OperationQueue.main.addOperation { 
       self.progressView.progress = progress //assign progress value 
      } 
     } 
    } 

    override func viewWillDisappear(_ animated: Bool) { 
     super.viewWillDisappear(animated) 
     DownloadManager.shared.onProgress = nil 
    } 

    @IBAction func startDownload(_ sender: Any) { 
     let url = URL(string: "YourFileURL")! 
     DownloadManager.shared.download(url) 
    } 

} 


Remplacez votre DownloadManager:

import Foundation 

class DownloadManager : NSObject, URLSessionDelegate, URLSessionDownloadDelegate { 

    static var shared = DownloadManager() 
    var url : URL? 
    typealias ProgressHandler = (Float) ->() 

    var onProgress : ProgressHandler? { 
     didSet { 
      if onProgress != nil { 
       let _ = activate() 
      } 
     } 
    } 

    override private init() { 
     super.init() 
    } 

    func activate() -> URLSession { 
     let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).background") 

     // Warning: If an URLSession still exists from a previous download, it doesn't create a new URLSession object but returns the existing one with the old delegate object attached! 
     return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue()) 
    } 

    private func calculateProgress(session : URLSession, completionHandler : @escaping (Float) ->()) { 
     session.getTasksWithCompletionHandler { (tasks, uploads, downloads) in 
      let progress = downloads.map({ (task) -> Float in 
       if task.countOfBytesExpectedToReceive > 0 { 
        return Float(task.countOfBytesReceived)/Float(task.countOfBytesExpectedToReceive) 
       } else { 
        return 0.0 
       } 
      }) 
      completionHandler(progress.reduce(0.0, +)) 
     } 
    } 

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { 

     if totalBytesExpectedToWrite > 0 { 
      if let onProgress = onProgress { 
       calculateProgress(session: session, completionHandler: onProgress) 
      } 
      let progress = Float(totalBytesWritten)/Float(totalBytesExpectedToWrite) 
      debugPrint("Progress \(downloadTask) \(progress)") 

     } 
    } 

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { 
     debugPrint("Download finished: \(location)") 
//  try? FileManager.default.removeItem(at: location) 
     //copy downloaded data to your documents directory with same names as source file 
     let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first 
     let destinationUrl = documentsUrl!.appendingPathComponent(url!.lastPathComponent) 
     let dataFromURL = try? Data(contentsOf: location) 
     try? dataFromURL?.write(to: destinationUrl, options: [.atomic]) 
     print(destinationUrl) 
     //now it is time to do what is needed to be done after the download 
    } 

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 
     debugPrint("Task completed: \(task), error: \(String(describing: error))") 
    } 


    func download(_ url: URL) 
    { 
     self.url = url 

     //download identifier can be customized. I used the "ulr.absoluteString" 
     let task = DownloadManager.shared.activate().downloadTask(with: url) 
     task.resume() 

    } 
} 


Et dans mon délégué App:

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping() -> Void) { 
     debugPrint("handleEventsForBackgroundURLSession: \(identifier)") 
     completionHandler() 
    } 

Référence: Tutorial par ralfebert