本文介绍了如何让alamofire下载进度在后台ios中运行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Alamofire 下载数据

I am using Alamofire to download data

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

How to make alamofire run download in background with swift?

谢谢

推荐答案

基本思路如下:

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

  1. The key problem is that with background downloads, your app may actually be terminated while downloads are in progress (e.g. jettisoned due to memory pressure). Fortunately, your app is fired up again when background downloads are done, but any task-level closures you originally supplied are long gone. To get around this, when using background sessions, one should rely upon session-level closures used by the delegate methods.

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)
    }
}

  • 然后我就可以开始下载了.请注意,我在启动下载时没有指定任何特定于任务的闭包,而是仅使用上述会话级闭包,这些闭包使用 URLSessionTask 的详细信息来确定要执行的操作:

  • Then I can just initiate those downloads. Note, I do not specify any task-specific closure when I initiate the download, but rather merely use the above session-level closures that use the details of the URLSessionTask to identify what to do:

    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)
            }
        }
    
    }
    

  • 如果您的应用在下载完成后没有运行,iOS 需要知道,在它重新启动您的应用后,当您全部完成后,它可以安全地暂停您的应用.因此,在 handleEventsForBackgroundURLSession 中,您捕获了该闭包:

  • If your app isn't running when the downloads finish, iOS needs to know that, after it restarted your app, when you're all done and that it can safely suspend your app. So, in handleEventsForBackgroundURLSession you capture that closure:

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

    sessionDidFinishEventsForBackgroundURLSession 在步骤 1 中使用.

    That is used by sessionDidFinishEventsForBackgroundURLSession, in step 1.

    两个观察:

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

    • This is only called if your app was not running when the downloads finish.

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

    If doing background sessions, though, you must capture this closure and call it when you're all done processing the background session delegate methods.

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

    So, to recap, the basic limitations of background sessions are:

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

    • You can only use download and upload tasks while the app is in background;

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

    You can only rely upon session-level delegates because the app may have been terminated since the requests were initiated; and

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

    In iOS, you must implement handleEventsForBackgroundURLSession, capture that completion handler, and call it when your background process is done.

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

    I must also point out that while Alamofire is a wonderful library, it's not actually adding a lot value (above and beyond what is provided by URLSession to this background download process). If you're doing simple uploads/downloads only, then you might consider just using URLSession directly. But if you are using Alamofire in your project already or if your requests consist of more complicated application/x-www-form-urlencoded requests (or whatever) which merit the advantages of Alamofire, then the above outlines the key moving parts involved in the process.

    这篇关于如何让alamofire下载进度在后台ios中运行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

  • 08-03 23:58