4

I was trying implement a code where a API will be called shortly after a file has been uploaded into AWS server but it must be in background mode. Whereas the AWS sdk manages uploading file into their server in background mode but the following code is not working.

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

And last.. 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
   }
}

But this delegate method never gets called whereas its being called for AWS upload. I think that's the main reason why background uploadTask not working for me. Stuck for 2 days. Any help will be appreciated.

Poles
  • 3,585
  • 9
  • 43
  • 91

3 Answers3

2

uploadTask(withStreamedRequest:...) is not compatible with background URL sessions. Use uploadTask(with:request, fromFile:...) instead.

Luke Melia
  • 8,389
  • 33
  • 41
1

httpBody is ignored if you create an upload task by uploadTask(withStreamedRequest:). It requires to implement urlSession(_:task:needNewBodyStream:) delegate callback. For the case of background mode it doesn't suite. Try to use uploadTask(with request: URLRequest, from bodyData: Data) instead. Also it looks like VaultUploadWebService does not have any references to the session object. Try to store session as a member of VaultUploadWebService.

cocavo
  • 163
  • 8
  • Tried `uploadTask(with request: URLRequest, from bodyData: Data)`...same result. – Poles Aug 13 '18 at 11:24
  • Have you checked that at least one of your requests has reached your server? HTTP proxy tools, e.g. Charles can help you with this. In other words the issue that your requests are not even executed or you just don't receive callbacks about their execution state (successful or failed)? – cocavo Aug 13 '18 at 15:16
  • And as I pointed out in my answer, `session` object is probably deallocated just upon end of execution of `startRequest ` method, because `VaultUploadWebService ` doesn't have any references to it. Check this point, because it also may cause this issue. – cocavo Aug 13 '18 at 15:24
  • Yes, because `func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)` is being called. – Poles Aug 14 '18 at 10:06
  • I'm checking if `session` is being deallocated upon end of execution of startRequest – Poles Aug 14 '18 at 10:07
  • What print log output did you get in `func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)`? – cocavo Aug 14 '18 at 15:13
  • Unfortunately when I tried to log in background it doesn't print. It only print when I came to foreground. – Poles Aug 16 '18 at 06:05
0

Background download events don't fire on a simulator. You can only test this on a real device.

ielyamani
  • 17,807
  • 10
  • 55
  • 90
  • I tried, no luck. But the interesting thing is AWS iOS SDK's upload method works in simulator also. – Poles Aug 09 '18 at 06:49