【问题标题】:Background Upload is not working in Swift后台上传在 Swift 中不起作用
【发布时间】:2019-01-13 12:10:30
【问题描述】:

我正在尝试实现一个代码,其中将在文件上传到 AWS 服务器后不久调用 API,但它必须处于后台模式。而 AWS sdk 在后台模式下管理将文件上传到其服务器,但以下代码不起作用。

ViewController.swift

func upload(_ mediaData:Data){

   //AWS method to upload a file
   AWSS3UploadImageData(mediaData!, strImageName: "person.jpg", strContentType: "img/*", { (isSuccess, result, strMessage) in
         if isSuccess {
              let arrPost = result as! [[String : String]]

              //Call custom webservice
              VaultUploadWebService.shared.callVaultUploadWebService(Params: arrPost)
         }
         else {
             print("Unsuccess")
         }
   })
}

VaultWebService.swift

class VaultUploadWebService: NSObject {

    static let shared = VaultUploadWebService()

    var savedCompletionHandler: (() -> Void)?

    func callVaultUploadWebService(Params: [[String : String]]) {

        startRequest(for: "www.example.com", param: Params)
    }


    func startRequest (for urlString: String, param: [[String : String]]) {

        let identifier = "com.com.background" + "\(NSDate().timeIntervalSince1970 * 1000)"
        let configuration = URLSessionConfiguration.background(withIdentifier:identifier)
        let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)

        let url = URL(string: urlString)!
        var request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 180)
        request.httpMethod = "post"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")      
        do {
           let paramsData = try JSONSerialization.data(withJSONObject:param, options:[])
           request.httpBody =  paramsData

           session.uploadTask(withStreamedRequest: request).resume()
       }catch {
        print("JSON serialization failed: ", error)
        return
       }


        //Also tried using the following but no luck
        /*guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
        let fileUrl = documentDirectoryUrl.appendingPathComponent("Persons.json")
        let jsonEncoder = JSONEncoder()
        do {
          let jsonData = try jsonEncoder.encode(param)
          try jsonData.write(to: fileUrl, options: [])
        }
        catch let error {
          print(error.localizedDescription)
        }
        session.uploadTask(with: request, fromFile: fileUrl).resume()*/

    }

}


extension VaultUploadWebService: URLSessionDelegate {
    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        DispatchQueue.main.async {
            self.savedCompletionHandler?()
            self.savedCompletionHandler = nil
        }
    }
}

extension VaultUploadWebService: URLSessionTaskDelegate{

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {

        if (error != nil){
            print(error?.localizedDescription ?? "error")
        }
        else{
            print(task.response)
        }
    }
}

最后.. Appdelegate.swift

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

   let id = identifier as NSString
   if id.contains("com.amazonaws") {
       AWSS3TransferUtility.interceptApplication(application, handleEventsForBackgroundURLSession: identifier, completionHandler: completionHandler)
   }else{
       VaultUploadWebService.shared.savedCompletionHandler = completionHandler
   }
}

但是这个委托方法永远不会被调用,而它被调用以进行 AWS 上传。我认为这是背景uploadTask 不适合我的主要原因。卡了2天。任何帮助将不胜感激。

【问题讨论】:

  • 可能是 UIBackgroundTaskIdentifier 可以帮助您,请阅读此内容。
  • 您是否在 Capabilities 中启用了后台模式?
  • @TarasChernyshenko:是的
  • 您是否检查了您尝试在后台点击的 URL 是否在方法 startRequest 中被点击,并且您得到任何响应?
  • 你检查过这个吗? stackoverflow.com/a/22703658/1057689

标签: ios swift urlsession background-fetch


【解决方案1】:

如果您通过uploadTask(withStreamedRequest:) 创建上传任务,则会忽略httpBody。它需要实现urlSession(_:task:needNewBodyStream:)委托回调。对于后台模式,它不适合。尝试改用uploadTask(with request: URLRequest, from bodyData: Data)。而且看起来VaultUploadWebService 没有对session 对象的任何引用。尝试将session 存储为VaultUploadWebService 的成员。

【讨论】:

  • 试过uploadTask(with request: URLRequest, from bodyData: Data)...同样的结果。
  • 您是否检查过至少一个请求已到达您的服务器? HTTP 代理工具,例如查尔斯可以帮助你。换句话说,您的请求甚至没有执行,或者您只是没有收到有关其执行状态(成功或失败)的回调?
  • 正如我在回答中指出的那样,session 对象可能在startRequest 方法的执行结束时被释放,因为VaultUploadWebService 没有对它的任何引用。检查这一点,因为它也可能导致这个问题。
  • 是的,因为正在调用func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
  • 我正在检查 session 是否在 startRequest 执行结束时被释放
【解决方案2】:

uploadTask(withStreamedRequest:...) 与后台 URL 会话不兼容。请改用uploadTask(with:request, fromFile:...)

【讨论】:

    【解决方案3】:

    后台下载事件不会在模拟器上触发。您只能在真实设备上进行测试。

    【讨论】:

    • 我试过了,没有运气。但有趣的是 AWS iOS SDK 的上传方法也适用于模拟器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-28
    • 1970-01-01
    • 2015-10-05
    • 1970-01-01
    • 2018-11-02
    相关资源
    最近更新 更多