Ios 后台上传在Swift中不起作用

Ios 后台上传在Swift中不起作用,ios,swift,urlsession,background-fetch,Ios,Swift,Urlsession,Background Fetch,我正在尝试实现一个代码,其中一个API将在文件上传到AWS服务器后不久调用,但它必须处于后台模式。虽然AWS sdk在后台模式下管理将文件上载到其服务器,但以下代码不起作用 ViewController.swift func upload(_ mediaData:Data){ //AWS method to upload a file AWSS3UploadImageData(mediaData!, strImageName: "person.jpg", strContentTyp

我正在尝试实现一个代码,其中一个API将在文件上传到AWS服务器后不久调用,但它必须处于后台模式。虽然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")
         }
   })
}
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)
        }
    }
}
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
   }
}
VaultWebService.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")
         }
   })
}
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)
        }
    }
}
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
   }
}
最后Appdelegate.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")
         }
   })
}
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)
        }
    }
}
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上传而被调用的。我认为这是后台
上传任务
不适合我的主要原因。卡了两天。任何帮助都将不胜感激。

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

httpBody
如果您通过
uploadTask(with streamedrequest:)
创建上载任务,则忽略该操作。它需要实现
urlSession(uquot:task:needNewBodyStream:)
委托回调。对于背景模式,它不适合。尝试使用
uploadTask(带request:URLRequest,来自bodyData:Data)
。另外,看起来
VaultUploadWebService
没有对
会话
对象的任何引用。尝试将
会话
存储为
Vault UploadWebService
的成员

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

可能是UIBackgroundTaskIdentifier可以帮助您,请阅读相关内容。您是否在功能中启用了后台模式?@TarasChernyshenko:Yes是否检查您试图在后台命中的URL是否在方法startRequest中被命中,并且您是否得到任何响应?是否检查了此项?我试过了,运气不好。但有趣的是AWS iOS SDK的上传方法也可以在模拟器中运行。尝试了
上传任务(使用request:URLRequest,from bodyData:Data)
…结果相同。您是否检查了至少一个请求已到达服务器?HTTP代理工具(例如Charles)可以帮助您实现这一点。换句话说,您的请求甚至没有被执行,或者您只是没有收到关于其执行状态的回调(成功或失败)?正如我在回答中指出的,
会话
对象可能在执行
startRequest
方法结束时被释放,因为
VaultUploadWebService
没有对它的任何引用。检查这一点,因为它也可能导致此问题。是的,因为调用了
func-urlSession(u-session:urlSession,task:URLSessionTask,didCompleteWither错误:error?)
。我正在检查
session
是否在startRequest执行结束时解除分配