@@ -30,22 +30,42 @@ public enum DownloaderError: Error {
3030/// The `Downloader` protocol abstract away the download of a file with a progress report.
3131public protocol Downloader {
3232
33+ /// The progress closure type. The first arguments contains the number of bytes downloaded, and the second argument
34+ /// contains the total number of bytes to download, if known.
35+ typealias Progress = ( Int64 , Int64 ? ) -> Void
36+
37+ /// The completion closure type. The only argument contains the result type containing the
38+ /// `DownloaderError` encountered on failure.
39+ typealias Completion = ( Result < Void , DownloaderError > ) -> Void
40+
3341 /// Downloads a file and keeps the caller updated on the progress and completion.
3442 ///
3543 /// - Parameters:
3644 /// - url: The `URL` to the file to download.
3745 /// - destination: The `AbsolutePath` to download the file to.
38- /// - progress: A closure to receive the download's progress as a fractional value between `0.0` and `1.0`.
39- /// - completion: A closure to be notifed of the completion of the download as a `Result` type containing the
40- /// `DownloaderError` encountered on failure.
46+ /// - progress: A closure to receive the download's progress as number of bytes.
47+ /// - completion: A closure to be notifed of the completion of the download.
4148 func downloadFile(
4249 at url: Foundation . URL ,
4350 to destination: AbsolutePath ,
44- progress: @escaping ( Double ) -> Void ,
45- completion: @escaping ( Result < Void , DownloaderError > ) -> Void
51+ progress: @escaping Progress ,
52+ completion: @escaping Completion
4653 )
4754}
4855
56+ extension DownloaderError : LocalizedError {
57+ public var errorDescription : String ? {
58+ switch self {
59+ case . clientError( let error) :
60+ return ( error as? LocalizedError ) ? . errorDescription ?? error. localizedDescription
61+ case . serverError( let statusCode) :
62+ return " invalid status code \( statusCode) "
63+ case . fileSystemError( let error) :
64+ return ( error as? LocalizedError ) ? . errorDescription ?? error. localizedDescription
65+ }
66+ }
67+ }
68+
4969/// A `Downloader` conformance that uses Foundation's `URLSession`.
5070public final class FoundationDownloader : NSObject , Downloader {
5171
@@ -56,8 +76,8 @@ public final class FoundationDownloader: NSObject, Downloader {
5676 fileprivate struct Download {
5777 let task : URLSessionDownloadTask
5878 let destination : AbsolutePath
59- let progress : ( Double ) -> Void
60- let completion : ( Result < Void , DownloaderError > ) -> Void
79+ let progress : Downloader . Progress
80+ let completion : Downloader . Completion
6181 }
6282
6383 /// The `URLSession` used for all downloads.
@@ -89,8 +109,8 @@ public final class FoundationDownloader: NSObject, Downloader {
89109 public func downloadFile(
90110 at url: Foundation . URL ,
91111 to destination: AbsolutePath ,
92- progress: @escaping ( Double ) -> Void ,
93- completion: @escaping ( Result < Void , DownloaderError > ) -> Void
112+ progress: @escaping Downloader . Progress ,
113+ completion: @escaping Downloader . Completion
94114 ) {
95115 queue. addOperation {
96116 let task = self . session. downloadTask ( with: url)
@@ -114,8 +134,9 @@ extension FoundationDownloader: URLSessionDownloadDelegate {
114134 totalBytesExpectedToWrite: Int64
115135 ) {
116136 let download = self . download ( for: downloadTask)
117- let progress = Double ( totalBytesWritten) / Double( totalBytesExpectedToWrite)
118- download. notifyProgress ( progress)
137+ let totalBytesToDownload = totalBytesExpectedToWrite != NSURLSessionTransferSizeUnknown ?
138+ totalBytesExpectedToWrite : nil
139+ download. notifyProgress ( bytesDownloaded: totalBytesWritten, totalBytesToDownload: totalBytesToDownload)
119140 }
120141
121142 public func urlSession(
@@ -162,9 +183,9 @@ extension FoundationDownloader {
162183}
163184
164185extension FoundationDownloader . Download {
165- func notifyProgress( _ progress : Double ) {
186+ func notifyProgress( bytesDownloaded : Int64 , totalBytesToDownload : Int64 ? ) {
166187 DispatchQueue . global ( ) . async {
167- self . progress ( progress )
188+ self . progress ( bytesDownloaded , totalBytesToDownload )
168189 }
169190 }
170191
0 commit comments