Jelajahi Sumber

Use task based on download delegate

And also add tests
onevcat 4 tahun lalu
induk
melakukan
50db653e10

+ 1 - 4
Sources/Networking/ImageDownloader.swift

@@ -194,10 +194,7 @@ open class ImageDownloader {
             }
         }
         sessionDelegate.onDidDownloadData.delegate(on: self) { (self, task) in
-            guard task.originalURL != nil, let response = task.task.response else {
-                return task.mutableData
-            }
-            return (self.delegate ?? self).imageDownloader(self, didDownload: task.mutableData, with: response)
+            return (self.delegate ?? self).imageDownloader(self, didDownload: task.mutableData, with: task)
         }
     }
 

+ 25 - 7
Sources/Networking/ImageDownloaderDelegate.swift

@@ -53,6 +53,23 @@ public protocol ImageDownloaderDelegate: AnyObject {
         with response: URLResponse?,
         error: Error?)
 
+    /// Called when the `ImageDownloader` object successfully downloaded image data from specified URL. This is
+    /// your last chance to verify or modify the downloaded data before Kingfisher tries to perform addition
+    /// processing on the image data.
+    ///
+    /// - Parameters:
+    ///   - downloader: The `ImageDownloader` object which is used for the downloading operation.
+    ///   - data: The original downloaded data.
+    ///   - dataTask: The data task contains request and response information of the download.
+    /// - Note:
+    ///   This can be used to pre-process raw image data before creation of `Image` instance (i.e.
+    ///   decrypting or verification). If `nil` returned, the processing is interrupted and a `KingfisherError` with
+    ///   `ResponseErrorReason.dataModifyingFailed` will be raised. You could use this fact to stop the image
+    ///   processing flow if you find the data is corrupted or malformed.
+    ///
+    ///  If this method is implemented, `imageDownloader(_:didDownload:for:)` will not be called anymore.
+    func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, with dataTask: SessionDataTask) -> Data?
+  
     /// Called when the `ImageDownloader` object successfully downloaded image data from specified URL. This is
     /// your last chance to verify or modify the downloaded data before Kingfisher tries to perform addition
     /// processing on the image data.
@@ -69,9 +86,8 @@ public protocol ImageDownloaderDelegate: AnyObject {
     ///   decrypting or verification). If `nil` returned, the processing is interrupted and a `KingfisherError` with
     ///   `ResponseErrorReason.dataModifyingFailed` will be raised. You could use this fact to stop the image
     ///   processing flow if you find the data is corrupted or malformed.
-    func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, with response: URLResponse) -> Data?
-  
-    @available(*, deprecated, message: "use `imageDownloader(_:didDownload:with:)` instead")
+    ///
+    ///   If `imageDownloader(_:didDownload:with:)` is implemented, this method will not be called anymore.
     func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data?
 
     /// Called when the `ImageDownloader` object successfully downloads and processes an image from specified URL.
@@ -125,12 +141,14 @@ extension ImageDownloaderDelegate {
         return (200..<400).contains(code)
     }
   
-    public func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, with response: URLResponse) -> Data? {
-      guard let url = response.url else { return data }
-      return imageDownloader(downloader, didDownload: data, for: url)
+    public func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, with task: SessionDataTask) -> Data? {
+        guard let url = task.originalURL else {
+            return data
+        }
+        return imageDownloader(downloader, didDownload: data, for: url)
     }
   
     public func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data? {
-      return data
+        return data
     }
 }

+ 1 - 1
Sources/Networking/SessionDataTask.swift

@@ -43,7 +43,7 @@ public class SessionDataTask {
 
     // This is a copy of `task.originalRequest?.url`. It is for getting a race-safe behavior for a pitfall on iOS 13.
     // Ref: https://github.com/onevcat/Kingfisher/issues/1511
-    let originalURL: URL?
+    public let originalURL: URL?
 
     /// The underlying download task. It is only for debugging purpose when you encountered an error. You should not
     /// modify the content of this task or start it yourself.

+ 36 - 2
Tests/KingfisherTests/ImageDownloaderTests.swift

@@ -496,8 +496,10 @@ class ImageDownloaderTests: XCTestCase {
         
         let url = testURLs[0]
         stub(url, data: testImageData)
+        
+        let modifier = URLNilDataModifier()
 
-        downloader.delegate = self
+        downloader.delegate = modifier
         downloader.downloadImage(with: url) { result in
             XCTAssertNil(result.value)
             XCTAssertNotNil(result.error)
@@ -506,11 +508,37 @@ class ImageDownloaderTests: XCTestCase {
                 XCTFail()
             }
             self.downloader.delegate = nil
+            // hold delegate
+            _ = modifier
             exp.fulfill()
         }
         waitForExpectations(timeout: 3, handler: nil)
     }
 
+    func testDownloadedDataCouldBeModifiedWithTask() {
+        let exp = expectation(description: #function)
+        
+        let url = testURLs[0]
+        stub(url, data: testImageData)
+        
+        let modifier = TaskNilDataModifier()
+
+        downloader.delegate = modifier
+        downloader.downloadImage(with: url) { result in
+            XCTAssertNil(result.value)
+            XCTAssertNotNil(result.error)
+            if case .responseError(reason: .dataModifyingFailed) = result.error! {
+            } else {
+                XCTFail()
+            }
+            self.downloader.delegate = nil
+            // hold delegate
+            _ = modifier
+            exp.fulfill()
+        }
+        waitForExpectations(timeout: 3, handler: nil)
+    }
+    
 #if os(iOS) || os(tvOS) || os(watchOS)
     func testModifierShouldOnlyApplyForFinalResultWhenDownload() {
         let exp = expectation(description: #function)
@@ -571,12 +599,18 @@ class ImageDownloaderTests: XCTestCase {
     }
 }
 
-extension ImageDownloaderTests: ImageDownloaderDelegate {
+class URLNilDataModifier: ImageDownloaderDelegate {
     func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, for url: URL) -> Data? {
         return nil
     }
 }
 
+class TaskNilDataModifier: ImageDownloaderDelegate {
+    func imageDownloader(_ downloader: ImageDownloader, didDownload data: Data, with dataTask: SessionDataTask) -> Data? {
+        return nil
+    }
+}
+
 class URLModifier: ImageDownloadRequestModifier {
     var url: URL? = nil
     func modified(for request: URLRequest) -> URLRequest? {