-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Download a file to destination with progress #18
Comments
Ideally would be great to be able to combine the async download method with public struct GetFileDownload: Sendable {
// ... (other parts of the struct)
public static func live(
auth: Auth,
keychain: Keychain,
httpClient: HTTPClient,
onProgress: @escaping (Double) -> Void // Progress closure
) -> GetFileDownload {
GetFileDownload { params in
// ... (authorization and request setup)
let delegate = ProgressDelegate(onProgress: onProgress)
let (responseData, response) = try await httpClient.download(for: request, delegate: delegate)
// ... (status code validation and return data)
}
}
}
class ProgressDelegate: DownloadProgressDelegate {
private var totalBytesReceived: Int64 = 0
private var totalBytesExpected: Int64 = -1
private let progressClosure: (Double) -> Void
init(onProgress: @escaping (Double) -> Void) {
self.progressClosure = onProgress
}
func didReceiveData(_ bytesReceived: Int64, totalBytesExpected: Int64) {
self.totalBytesExpected = totalBytesExpected
totalBytesReceived += bytesReceived
let progress = totalBytesExpected > 0 ? Double(totalBytesReceived) / Double(totalBytesExpected) : 0
DispatchQueue.main.async {
self.progressClosure(progress)
}
}
} |
Confirmed - does appear the only |
Thanks for the feedback @markst, it's a nice feature to have. I would start by updating the public struct HTTPClient: Sendable {
public typealias DataForRequest = @Sendable (URLRequest) async throws -> (Data, URLResponse)
public typealias DownloadForRequest = @Sendable (URLRequest, @escaping OnProgress) async throws -> (URL, URLResponse)
public typealias OnProgress = @Sendable (Double) -> Void
public init(
dataForRequest: @escaping DataForRequest,
downloadForRequest: @escaping DownloadForRequest
) {
self.dataForRequest = dataForRequest
self.downloadForRequest = downloadForRequest
}
public var dataForRequest: DataForRequest
public var downloadForRequest: DownloadForRequest
public func data(for urlRequest: URLRequest) async throws -> (Data, URLResponse) {
try await dataForRequest(urlRequest)
}
public func download(for urlRequest: URLRequest, onProgress: @escaping OnProgress) async throws -> (URL, URLResponse) {
try await downloadForRequest(urlRequest, onProgress)
}
}
extension HTTPClient {
public static func urlSession(_ urlSession: URLSession = .shared) -> HTTPClient {
HTTPClient(
dataForRequest: { request in
try await urlSession.data(for: request)
},
downloadForRequest: { request, onProgress in
var progressObservation: NSKeyValueObservation?
defer { _ = progressObservation }
return try await withCheckedThrowingContinuation { continuation in
let task = urlSession.downloadTask(with: request) { url, response, error in
if let error {
continuation.resume(throwing: error)
} else if let url, let response {
continuation.resume(returning: (url, response))
} else {
continuation.resume(throwing: URLError(.unknown))
}
}
progressObservation = task.progress.observe(\.fractionCompleted) { progress, _ in
onProgress(progress.fractionCompleted)
}
task.resume()
}
}
)
}
} This should report progress during download, and return URL once completed (or throw an error on failure). It's just a draft (I didn't test it), but it should give you a starting point. We can add a new I would appreciate it if you open a new pull request, and I will try my best to help! |
I had started to look into this and thought about opening up a PR, but figured I'd open an issue to begin with in case you had some input.
Would be great to add support in order to download files from Google Drive to a destination as downloading using
GetFileData
means entire data is in memory.We can download using:
Which returns:
https://developer.apple.com/documentation/foundation/urlsession#2934757
However I'm unsure how to get the download progress.
It seems we may be able to take the approach using
AsyncBytes
, but this still means entire download living in memory.https://khanlou.com/2021/10/download-progress-with-awaited-network-tasks/
The text was updated successfully, but these errors were encountered: