【问题标题】:how to make alamofire download progress run in background ios?如何让 alamofire 下载进度在 iOS 后台运行?
【发布时间】:2014-12-19 20:45:22
【问题描述】:

我正在使用Alamofire下载数据

如何让 alamofire 在后台快速运行下载?

谢谢

【问题讨论】:

  • 此问题已回答here

标签: ios swift download alamofire


【解决方案1】:

基本思路如下:

  1. 关键问题是,在后台下载时,您的应用实际上可能在下载过程中被终止(例如,由于内存压力而被丢弃)。幸运的是,当后台下载完成时,您的应用程序会再次启动,但您最初提供的任何任务级闭包早已不复存在。为了解决这个问题,在使用后台会话时,应该依赖委托方法使用的会话级闭包。

    import UIKit
    import Alamofire
    import UserNotifications
    
    fileprivate let backgroundIdentifier = ...
    fileprivate let notificationIdentifier = ...
    
    final class BackgroundSession {
    
        /// Shared singleton instance of BackgroundSession
    
        static let shared = BackgroundSession()
    
        /// AlamoFire `SessionManager`
        ///
        /// This is `private` to keep this app loosely coupled with Alamofire.
    
        private let manager: SessionManager
    
        /// Save background completion handler, supplied by app delegate
    
        func saveBackgroundCompletionHandler(_ backgroundCompletionHandler: @escaping () -> Void) {
            manager.backgroundCompletionHandler = backgroundCompletionHandler
        }
    
        /// Initialize background session
        ///
        /// This is `private` to avoid accidentally instantiating separate instance of this singleton object.
    
        private init() {
            let configuration = URLSessionConfiguration.background(withIdentifier: backgroundIdentifier)
            manager = SessionManager(configuration: configuration)
    
            // specify what to do when download is done
    
            manager.delegate.downloadTaskDidFinishDownloadingToURL = { _, task, location in
                do {
                    let destination = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
                        .appendingPathComponent(task.originalRequest!.url!.lastPathComponent)
                    try FileManager.default.moveItem(at: location, to: destination)
                } catch {
                    print("\(error)")
                }
            }
    
            // specify what to do when background session finishes; i.e. make sure to call saved completion handler
            // if you don't implement this, it will call the saved `backgroundCompletionHandler` for you
    
            manager.delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] _ in
                self?.manager.backgroundCompletionHandler?()
                self?.manager.backgroundCompletionHandler = nil
    
                // if you want, tell the user that the downloads are done
    
                let content = UNMutableNotificationContent()
                content.title = "All downloads done"
                content.body = "Whoo, hoo!"
                let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
                let notification = UNNotificationRequest(identifier: notificationIdentifier, content: content, trigger: trigger)
                UNUserNotificationCenter.current().add(notification)
            }
    
            // specify what to do upon error
    
            manager.delegate.taskDidComplete = { _, task, error in
                let filename = task.originalRequest!.url!.lastPathComponent
                if let error = error {
                    print("\(filename) error: \(error)")
                } else {
                    print("\(filename) done!")
                }
    
                // I might want to post some event to `NotificationCenter`
                // so app UI can be updated, if it's in foreground
            }
        }
    
        func download(_ url: URL) {
            manager.download(url)
        }
    }
    
  2. 然后我可以启动这些下载。请注意,当我开始下载时,我没有指定任何特定于任务的闭包,而只是使用上述会话级闭包,这些闭包使用URLSessionTask 的详细信息来确定要做什么:

    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // request permission to post notification if download finishes while this is running in background
    
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
                if let error = error, !granted {
                    print("\(error)")
                }
            }
        }
    
        @IBAction func didTapButton(_ sender: Any) {
            let urlStrings = [
                "http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/s72-55482.jpg",
                "http://spaceflight.nasa.gov/gallery/images/apollo/apollo10/hires/as10-34-5162.jpg",
                "http://spaceflight.nasa.gov/gallery/images/apollo-soyuz/apollo-soyuz/hires/s75-33375.jpg",
                "http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/as17-134-20380.jpg",
                "http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/as17-140-21497.jpg",
                "http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/as17-148-22727.jpg"
            ]
            let urls = urlStrings.flatMap { URL(string: $0) }
    
            for url in urls {
                BackgroundSession.shared.download(url)
            }
        }
    
    }
    
  3. 如果您的应用在下载完成时没有运行,iOS 需要知道,在它重新启动您的应用后,您已完成所有操作,并且它可以安全地暂停您的应用。因此,在 handleEventsForBackgroundURLSession 中,您捕获了该闭包:

    class AppDelegate: UIResponder, UIApplicationDelegate {
    
        ...
    
        func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
            BackgroundSession.shared.saveBackgroundCompletionHandler(completionHandler)
        }
    
    }
    

    sessionDidFinishEventsForBackgroundURLSession 在步骤 1 中使用。

    两个观察结果:

    • 仅当下载完成时您的应用未运行时才会调用此方法。

    • 但是,如果执行后台会话,则必须捕获此闭包并在处理完后台会话委托方法后调用它。

所以,回顾一下,后台会话的基本限制是:

  • 您只能在应用处于后台时使用下载和上传任务;

  • 您只能依赖会话级别的委托,因为应用程序可能在请求发起后已终止;和

  • 在 iOS 中,您必须实现 handleEventsForBackgroundURLSession,捕获该完成处理程序,并在后台进程完成时调用它。

我还必须指出,虽然 Alamofire 是一个很棒的库,但它实际上并没有增加很多价值(超出URLSession 为这个后台下载过程提供的内容)。如果您只进行简单的上传/下载,那么您可以考虑直接使用URLSession。但是,如果您已经在您的项目中使用 Alamofire,或者您的请求包含更复杂的 application/x-www-form-urlencoded 请求(或其他),而这些请求具有 Alamofire 的优势,那么上面概述了该过程中涉及的关键移动部分。

【讨论】:

    猜你喜欢
    • 2014-02-11
    • 1970-01-01
    • 2018-05-28
    • 1970-01-01
    • 2017-03-03
    • 1970-01-01
    • 2015-07-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多