Skip to content

Commit f133a75

Browse files
committed
Refactors downloader to use a handle.
Changes the downloader to return a `DownloadHandle` containing the progress stream, a Task representing the finished download, and a cancellation closure. This provides a more structured and flexible way to manage downloads, allowing for better control and observation of the download process.
1 parent 6f2f25b commit f133a75

File tree

1 file changed

+44
-19
lines changed

1 file changed

+44
-19
lines changed

Sources/Service/Downloader.swift

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ public extension Network.Service {
88
private var downloadTask: URLSessionDownloadTask?
99
private var progressContinuation: AsyncStream<Float>.Continuation?
1010

11+
// MARK: - Public
12+
public struct DownloadHandle {
13+
let progress: AsyncStream<Float>
14+
let finished: Task<URL, Error>
15+
let cancel: @Sendable () -> Void
16+
}
17+
1118
// MARK: - Init
1219
init(url: URL) {
1320
self.url = url
@@ -17,29 +24,46 @@ public extension Network.Service {
1724

1825
// MARK: - Public functions
1926
public extension Network.Service.Downloader {
20-
/// Start downloading the file and track progress
21-
/// - Returns: A tuple containing the downloaded file URL and an AsyncStream of progress updates
22-
func download() async throws -> (URL, AsyncStream<Float>) {
23-
let (stream, continuation) = AsyncStream<Float>.makeStream()
24-
progressContinuation = continuation
25-
continuation.yield(0.0)
27+
/// Starts the download for the URL configured in the downloader and returns
28+
/// a handle for observing progress, awaiting completion, or cancelling.
29+
///
30+
/// - Returns: A `DownloadHandle` containing:
31+
/// - `progress`: An `AsyncStream<Float>` emitting values in `0.0...1.0`.
32+
/// - `finished`: A `Task<URL, Error>` that resolves with the downloaded file's
33+
/// temporary location.
34+
/// - `cancel`: A closure that cancels the download and completes the stream.
35+
func start() -> DownloadHandle {
36+
let (progressStream, progressCont) = AsyncStream<Float>.makeStream(
37+
bufferingPolicy: .bufferingNewest(1)
38+
)
39+
progressContinuation = progressCont
40+
progressCont.yield(0.0)
2641

27-
let downloadedURL = try await withCheckedThrowingContinuation { continuation in
28-
let delegate = DownloadDelegate { [weak self] result in
29-
Task { [weak self] in
30-
await self?.handleCompletion(result, continuation: continuation)
31-
}
32-
} progressHandler: { [weak self] progress in
33-
Task { [weak self] in
34-
await self?.handleProgress(progress)
42+
// Kick off the download, expose the completion via a Task
43+
let finished = Task<URL, Error> {
44+
try await withCheckedThrowingContinuation { (cc: CheckedContinuation<URL, Error>) in
45+
let delegate = DownloadDelegate { [weak self] result in
46+
Task { [weak self] in
47+
await self?.handleCompletion(result, continuation: cc)
48+
}
49+
} progressHandler: { [weak self] progress in
50+
Task { [weak self] in
51+
await self?.handleProgress(progress)
52+
}
3553
}
54+
55+
let session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
56+
let task = session.downloadTask(with: url)
57+
downloadTask = task
58+
task.resume()
3659
}
37-
let session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
38-
let task = session.downloadTask(with: url)
39-
downloadTask = task
40-
task.resume()
4160
}
42-
return (downloadedURL, stream)
61+
62+
return DownloadHandle(
63+
progress: progressStream,
64+
finished: finished,
65+
cancel: { [weak self] in Task { await self?.cancel() } }
66+
)
4367
}
4468

4569
/// Cancel the ongoing download
@@ -90,3 +114,4 @@ private final class DownloadDelegate: NSObject, URLSessionDownloadDelegate {
90114
}
91115
}
92116
}
117+

0 commit comments

Comments
 (0)