Preliminary Study of Alamofire (Background Download)

Keywords: Session iOS Swift

Alamofire Background Download

Background download

Since iOS 7, Apple has launched NSURLSession, enabling background downloads.

Let's take a look at Apple's native background download first.

  • Setting Background Download Request
let urlString:String = "http://dldir1.qq.com/qqfile/QQforMac/QQ_V6.5.5.dmg"
let config = URLSessionConfiguration.background(withIdentifier: createID())
let session = URLSession.init(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
let downloadTask: URLSessionDownloadTask = session.downloadTask(with: URL(string: urlString)!)
downloadTask .resume()
  • AppDelegate proxy method (callback when the application is in the background and the background download task is completed)
//Completion Handler for saving background Downloads
    var backgroundSessionCompletionHandler: (() -> Void)?
    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
        
        print("handleEventsForBackgroundURLSession - \(identifier)")
        self.backgroundSessionCompletionHandler = completionHandler
    }
  • URLSession Download Delegate Agent Method
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        print("Download complete - \(location)")
        let locationPath = location.path
        //Copy to the user directory (file names are named after timestamps)
        let documnets = NSHomeDirectory() + "/Documents/" + lgCurrentDataTurnString() + ".dmg"
        //Create a File Manager
        let fileManager = FileManager.default
        
        try! fileManager.moveItem(atPath: locationPath, toPath: documnets)
    }
    
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        print(" bytesWritten \(bytesWritten)\n totalBytesWritten \(totalBytesWritten)\n totalBytesExpectedToWrite \(totalBytesExpectedToWrite)")
        print("Download progress: \(Double(totalBytesWritten)/Double(totalBytesExpectedToWrite))\n")
    }
    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        print("Background Tasks Download Back,Current thread-\(Thread.current)")

        DispatchQueue.main.async {
        
            guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
                return
            }
            guard let backHandler = appDelegate.backgroundSessionCompletionHandler else {
                return
            }
            backHandler()
        }
    }
  • Note: App Transport Security Settings - > Allow Arbitrary Loads - > YES
  • Note: In the urlSession (session: URLSession, download Task: URLSession download Task, didFinish download to location: URL) method, location is only a temporary URL of the file on disk, it is only a temporary file, you need to use FileManager to write the file to the directory of the application (generally speaking, this can be done). Content retrieved repeatedly should be placed in the cache directory, because when you return from this delegation method, the file will be deleted from temporary storage.
  • Note: It's better to save the completionHandler in handleEvents ForBackgroundURLSession, in which completionHandler() is called after downloading data and UI refresh are completed.
  • Use of Swift try?, try!, as?, as?.
    • try? Tell the system that it may be wrong, or it may be right. If an error occurs, return nil. If no error occurs, the data will be packaged as an optional type of value and returned to us.
    • try! It must be right to tell the system that if something goes wrong, the program will crash. It is not recommended to use it.
    • as! Used when downcasting, because it is mandatory type conversion, if the conversion fails, runtime runtime runs incorrectly.
    • The conversion rules for as? And as! Operators are the same, except that as? Returns nil objects after the conversion fails and an optional type after the conversion succeeds, requiring us to unpack and use. Because as? Conversion failures will not report errors, so for those who can be 100% sure to use as! Can be converted successfully, use as!, otherwise use as?

Alamofire Background Download

  • Create a singleton, initialize the request, and facilitate callback
struct LGBackgroundManger {
    static let shared = LGBackgroundManger()
    let manager: SessionManager = {
        let configuration = URLSessionConfiguration.background(withIdentifier: "com.lgcooci.AlamofireTest.demo")
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
        return SessionManager(configuration: configuration)
    }()
}
  • Alamofire Download
LGBackgroundManger.shared.manager
            .download(urlString, method: .get, parameters: nil) {
                (url, response) -> (destinationURL: URL, options: DownloadRequest.DownloadOptions) in
                let documentUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
                let fileUrl = documentUrl?.appendingPathComponent(response.suggestedFilename ?? "123.dmg")
                return (fileUrl!, [.createIntermediateDirectories,.removePreviousFile])
            }.response { (downloadResponse) in
                print("Download callback information:\(downloadResponse)")
            }.downloadProgress { (progress) in
                print("Download progress:\(progress)")
        }
  • AppDelegate proxy callback
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
        
        print("handleEventsForBackgroundURLSession - \(identifier)")
        LGBackgroundManger.shared.manager.backgroundCompletionHandler = completionHandler
    }
  • Note: Manager needs to be retained during the session else we get HTTP Error Code-999.
    After defining manager as global it was retained. Could have defined it as Stored property as well for the class.

Posted by el_kab0ng on Tue, 24 Sep 2019 02:38:09 -0700