Home > Blockchain >  How to check error if uploading is failed using session.uploadTask in swift?
How to check error if uploading is failed using session.uploadTask in swift?

Time:11-16

I am uploading video file in the background but I dnon't know how to check if the video file is failed to upload. I am using URLSession to upload the file. If anybody has some idea please help me out.

private func uploadMediaWith(mediaURL: URL, mediaMimeType: MediaMimeType, AWSPresignedURL: URL) {
    let finalMediaURL = mediaURL.updatedSandboxURL()
    Log.echo(key: "\(self.KEY)", text: "setup upload for \(mediaURL.lastPathComponent)")
    let url = AWSPresignedURL // https://dentiscope-development.s3.us-west-2.amazonaws.com/exams/42/videos/BottomLeftOutside-b121986d-bd5c-4b6f-bed2-f030978f03f0.mp4?x-amz-acl=private&x-amz-meta-exam_id=42&x-amz-meta-uuid=b121986d-bd5c-4b6f-bed2-f030978f03f0&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA5WF3XEUH4WAYWLEI/20211115/us-west-2/s3/aws4_request&X-Amz-Date=20211115T070402Z&X-Amz-SignedHeaders=host;x-amz-acl;x-amz-meta-exam_id;x-amz-meta-uuid&X-Amz-Expires=432000&X-Amz-Signature=a6ede8cd558cd1df5c5d6e62be1237f995b7ac97e235112acc263e3b0de1531f
    let boundary = UUID().uuidString
    var request = URLRequest(url: url)
    request.httpMethod = "PUT"
    
    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
    if let authToken = SignedUserInfo.sharedInstance?.authToken {
      request.setValue("Authorization", forHTTPHeaderField: "Bearer "   authToken)
    }
    
    let config = URLSessionConfiguration.background(withIdentifier: boundary)
    config.waitsForConnectivity = true
    config.shouldUseExtendedBackgroundIdleMode = true
//    config.httpMaximumConnectionsPerHost = 4
//    config.networkServiceType = .background
    
    let session = URLSession(configuration: config, delegate: self, delegateQueue: .main)
    
    var data = Data()
    if(mediaMimeType == MediaMimeType.video) {
      do {
        let videoData = try Data(contentsOf: finalMediaURL)
        data.append(videoData)
      } catch {
        Log.echo(key: "MediaMimeType.video", text: error.localizedDescription)
        fatalError("\(self.KEY) COULD NOT GET DATA OF THE VIDEO || \(finalMediaURL.lastPathComponent)")
      }
    }
    
    request.httpBody = data
    DispatchQueue.global().async {
      let task = session.uploadTask(withStreamedRequest: request)
      // how to check if failled to uload
      self.uploadingURL = finalMediaURL
      task.resume()
      Log.echo(key: "\(self.KEY)", text: "execute upload for \(mediaURL.lastPathComponent) || session id \(boundary)")
    }
  }

If I am using this code:

let task = session.dataTask(with: request) { (data, urlResp, error) in
        if let error = error {
          print(error)
        }
      } //uploadTask(withStreamedRequest: request)
      self.uploadingURL = finalMediaURL
      task.resume()

then getting this error: Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.' terminating with uncaught exception of type NSException

CodePudding user response:

Using a delegate and converting it to completion block you could use something like the following:

class UploadContainer: NSObject, URLSessionTaskDelegate {
    
    let session: URLSession
    let request: URLRequest
    
    private var completion: ((_ error: Error?) -> Void)?
    private var task: URLSessionUploadTask?
    
    private var selfRetain: Any? // This is to persist lifetime of this class for the duration of request execution
    
    init(request: URLRequest, session: URLSession = .shared, completion: @escaping ((_ error: Error?) -> Void)) {
        self.request = request
        self.session = session
        self.completion = completion
        super.init()
        self.upload()
    }
    
    private func upload() {
        let task = session.uploadTask(withStreamedRequest: self.request)
        self.task = task
        task.delegate = self
        self.selfRetain = self
        task.resume()
    }
    
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        self.selfRetain = nil
        completion?(error)
        completion = nil
    }
    
}

Then when you generate your request you can simply call

UploadContainer(request: request) { error in
    if let error = error {
        print("Error occurred uploading file \(error)")
    } else {
        print("File was successfully uploaded")
    }
}

But there are many ways to achieve this. Point being is that once you create a task you need to assign a delegate. In provided code you can see this line task.delegate = self. Whoever is assigned as delegate (in this example self as UploadContainer) needs to implement this delegate which is done by this : NSObject, URLSessionTaskDelegate. The class can now implement some methods documented in URLSessionTaskDelegate. But we are only interested in completing the upload (with or without an error) which seems to be a method called func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?). I hope this works for you.

  • Related