iOS:在应用程序处于后台时执行上传任务

20

在iOS应用程序处于后台时,是否真的没有办法运行上传任务?这太荒谬了。一直在查看各种东西,例如NSURLSessionUploadTaskdispatch_after甚至是NSTimer,但是在将应用程序放到后台后,这些方法都不能超过短暂的10秒。

其他具有上传功能的应用程序是如何工作的呢?比如,上传图片到Facebook并将应用程序置于后台,那么上传会被取消吗?

为什么iOS不能像Android和Windows Phone一样拥有后台服务或代理?

这是我应用程序的关键功能,在其他平台上它完美地工作。

非常感谢任何帮助 :(


如果您能够创建和调用Web服务来为您执行任务,似乎可以使用服务器端编程实现此操作。请查看以下链接:http://stackoverflow.com/questions/25725811/upload-image-video-to-facebook-asynchronously-using-php-sdk-4 。不确定这是否与您有关。 - Piyush Arora
2个回答

32

您可以使用“后台会话”继续后台上传。创建后台URLSessionConfiguration的基本过程是使用background(withIdentifier:),在下载文件文档中概述了该过程。该文档侧重于下载,但同样的基本过程也适用于上传任务。

注意:


顺便说一句,如果您不想使用后台NSURLSession,但希望在应用程序离开后台几秒钟后继续运行有限时间任务,则可以使用UIApplication方法beginBackgroundTask请求更多时间。即使用户离开了应用程序,这将为您提供一些时间(以前是3分钟,在iOS 13及更高版本中仅为30秒),以完成您正在处理的任何任务。

请参见延长应用程序的后台执行时间。他们的代码片段有点过时,但现代版本可能如下所示:

func initiateBackgroundRequest(with data: Data) {
    var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid

    // Request the task assertion and save the ID.
    backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "Finish Network Tasks") {
        // End the task if time expires.
        if backgroundTaskID != .invalid {
            UIApplication.shared.endBackgroundTask(backgroundTaskID)
            backgroundTaskID = .invalid
        }
    }

    // Send the data asynchronously.
    performNetworkRequest(with: data) { result in
        // End the task assertion.
        if backgroundTaskID != .invalid {
            UIApplication.shared.endBackgroundTask(backgroundTaskID)
            backgroundTaskID = .invalid
        }
    }
}

请不要深陷细节中,专注于基本的模式:

  • 开始后台任务;
  • 提供一个超时语句,以便在时间用尽时清理后台任务;
  • 即使用户离开应用程序,也要启动您需要继续的任何内容;和
  • 在网络请求的完成处理程序中结束后台任务。

你确定completionHandler没问题吗?我在uploadTaskWithRequest:fromFile:completionHandler:方法的文档中没有找到任何提到它不能用于后台上传任务的说明。 - tonymontana
请查看URLSession文档。其中指出:“由于您的应用程序可能在传输正在进行时退出并重新启动,因此不支持完成处理程序块。” - Rob
@AmberK - uploadTask仅按原样发送文件。因此,如果您想发送multipart/form-data,则必须手动构造文件内容以结构化为multipart/form-data(就像您必须手动构造dataTaskhttpBody一样); 并手动设置URLRequestContent-Type标头。 - Rob
1
@hannojg - 它曾经在他们的旧“URL编程指南”中概述过,但随后他们用新文档取代了它,这些新文档未能清晰地概述后台上传要求。而且描述此内容的原始2015年之前的视频似乎也已经消失了。但是,只需尝试使用带有后台会话的uploadTask(with:from:),您将收到一个明确的异常报告:“不支持从NSData上传任务的后台会话。” 这证实了这些对后台上传的限制仍然适用。 - Rob
1
就我而言,对于后台上传不支持“NSData”的限制是可以理解的。他们真的不想让后台守护程序在内存中保留巨大的Data/NSData对象。如果一个资源如此之大,以至于您需要后台上传(而不仅仅是在用户离开应用程序后请求额外的30秒来完成标准的URLSession),那么基于文件的上传无论如何都会更受欢迎(即使是非后台的URLSession)。 - Rob
显示剩余7条评论

0
class ViewController: UIViewController, URLSessionTaskDelegate {
override func viewDidLoad() {
    super.viewDidLoad()
    
    let url = URL(string: "http://0.0.0.0")!
    let data = "Secret Message".data(using: .utf8)!
    
    let tempDir = FileManager.default.temporaryDirectory
    let localURL = tempDir.appendingPathComponent("throwaway")
    try? data.write(to: localURL)
    
    let request = URLRequest(url: url)
    let config = URLSessionConfiguration.background(withIdentifier: "uniqueId")
    let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
    let task = session.uploadTask(with: request, fromFile: localURL)
    task.resume()
}

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    print("We're done here")
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接