@@ -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
1926public 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