Skip to content

Instantly share code, notes, and snippets.

@donnywals
Last active April 16, 2021 02:47
Show Gist options
  • Save donnywals/4e946402619af5b20dca541cf89fda4f to your computer and use it in GitHub Desktop.
Save donnywals/4e946402619af5b20dca541cf89fda4f to your computer and use it in GitHub Desktop.
class UploadTaskPublisher {
typealias UploadProgress = (bytesSent: Int64, expectedTotalBytes: Int64)
typealias ProgressSubject = CurrentValueSubject<UploadProgress, Never>
typealias CompletionSubject = PassthroughSubject<URLSession.DataTaskPublisher.Output, URLSession.DataTaskPublisher.Failure>
var progress = ProgressSubject((0, 0))
var completion = CompletionSubject()
}
class UploadSession: NSObject, URLSessionTaskDelegate {
static var shared = UploadSession()
private var publishers = [URLSessionTask: UploadTaskPublisher]()
lazy private var session = URLSession(configuration: .default,
delegate: self,
delegateQueue: nil)
/// Starts an upload task and prepares an UploadTaskPublisher that provides progress and completion subjects
/// - Parameter request: The URLRequest to run
/// - Returns: an UploadTaskPublisher that provides progress and completion subjects
func performTask(with request: URLRequest) -> UploadTaskPublisher {
let publisher = UploadTaskPublisher()
let task = session.uploadTask(with: request, from: nil) { data, response, error in
if let data = data, let response = response {
publisher.completion.send((data, response))
publisher.completion.send(completion: .finished)
} else if let error = error as? URLError {
publisher.completion.send(completion: .failure(error))
} else {
fatalError("Task completed without a valid output")
}
}
publishers[task] = publisher
task.resume()
return publisher
}
func urlSession(_ session: URLSession, task: URLSessionTask,
didSendBodyData bytesSent: Int64, totalBytesSent: Int64,
totalBytesExpectedToSend: Int64) {
guard let publisher = publishers[task] else {
return
}
publisher.progress.value = (totalBytesSent, totalBytesExpectedToSend)
}
}
// simple usage example. I'm assuming you have a request prepared
let publisher = UploadSession.shared.performTask(with: aRequest)
publisher.progress.sink { progress in
// calculate progress based on progress.bytesSent / progress.expectedTotalBytes
}
publisher.completion.sink { result in
// handle the server response if needed
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment