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.