Sfoglia il codice sorgente

Download task update callback & skip cancel error

onevcat 6 anni fa
parent
commit
136745f73b

+ 18 - 3
Sources/General/KingfisherManager.swift

@@ -49,6 +49,8 @@ public struct RetrieveImageResult {
     public let originalSource: Source
 }
 
+public typealias DownloadTaskUpdatedBlock = ((_ newTask: DownloadTask?) -> Void)
+
 /// Main manager class of Kingfisher. It connects Kingfisher downloader and cache,
 /// to provide a set of convenience methods to use Kingfisher for tasks.
 /// You can use this class to retrieve an image via a specified URL from web or cache.
@@ -125,11 +127,16 @@ public class KingfisherManager {
         with resource: Resource,
         options: KingfisherOptionsInfo? = nil,
         progressBlock: DownloadProgressBlock? = nil,
+        downloadTaskUpdated: DownloadTaskUpdatedBlock? = nil,
         completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)?) -> DownloadTask?
     {
         let source = Source.network(resource)
         return retrieveImage(
-            with: source, options: options, progressBlock: progressBlock, completionHandler: completionHandler
+            with: source,
+            options: options,
+            progressBlock: progressBlock,
+            downloadTaskUpdated: downloadTaskUpdated,
+            completionHandler: completionHandler
         )
     }
 
@@ -155,6 +162,7 @@ public class KingfisherManager {
         with source: Source,
         options: KingfisherOptionsInfo? = nil,
         progressBlock: DownloadProgressBlock? = nil,
+        downloadTaskUpdated: DownloadTaskUpdatedBlock? = nil,
         completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)?) -> DownloadTask?
     {
         let options = currentDefaultOptions + (options ?? .empty)
@@ -165,12 +173,14 @@ public class KingfisherManager {
         return retrieveImage(
             with: source,
             options: info,
+            downloadTaskUpdated: downloadTaskUpdated,
             completionHandler: completionHandler)
     }
 
     func retrieveImage(
         with source: Source,
         options: KingfisherParsedOptionsInfo,
+        downloadTaskUpdated: DownloadTaskUpdatedBlock? = nil,
         completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)?) -> DownloadTask?
     {
         var context = RetrievingContext(options: options, originalSource: source)
@@ -180,12 +190,17 @@ public class KingfisherManager {
             case .success:
                 completionHandler?(result)
             case .failure(let error):
+                // Skip alternative sources if the user cancelled it.
+                guard !error.isTaskCancelled else {
+                    completionHandler?(.failure(error))
+                    return
+                }
                 if let nextSource = context.popAlternativeSource() {
                     context.appendError(error, to: currentSource)
-                    _ = self.retrieveImage(with: nextSource, context: context) { result in
+                    let newTask = self.retrieveImage(with: nextSource, context: context) { result in
                         handler(currentSource: nextSource, result: result)
                     }
-
+                    downloadTaskUpdated?(newTask)
                 } else {
                     // No other alternative source. Finish with error.
                     if context.propagationErrors.isEmpty {

+ 52 - 0
Tests/KingfisherTests/KingfisherManagerTests.swift

@@ -781,6 +781,58 @@ class KingfisherManagerTests: XCTestCase {
 
         waitForExpectations(timeout: 1, handler: nil)
     }
+
+    func testRetrievingAlternativeSourceTaskUpdateBlockCalled() {
+        let exp = expectation(description: #function)
+        let url = testURLs[0]
+        stub(url, data: testImageData)
+
+        let brokenURL = URL(string: "brokenurl")!
+        stub(brokenURL, data: Data())
+
+        var downloadTaskUpdatedCount = 0
+        let task = manager.retrieveImage(
+          with: .network(brokenURL),
+          options: [.alternativeSources([.network(url)])],
+          downloadTaskUpdated: { newTask in
+            downloadTaskUpdatedCount += 1
+            XCTAssertEqual(newTask?.sessionTask.task.currentRequest?.url, url)
+          })
+          {
+            result in
+            XCTAssertEqual(downloadTaskUpdatedCount, 1)
+            exp.fulfill()
+        }
+
+        XCTAssertEqual(task?.sessionTask.task.currentRequest?.url, brokenURL)
+
+        waitForExpectations(timeout: 1, handler: nil)
+    }
+
+    func testRetrievingAlternativeSourceCancelled() {
+        let exp = expectation(description: #function)
+        let url = testURLs[0]
+        stub(url, data: testImageData)
+
+        let brokenURL = URL(string: "brokenurl")!
+        stub(brokenURL, data: Data())
+
+        let task = manager.retrieveImage(
+            with: .network(brokenURL),
+            options: [.alternativeSources([.network(url)])]
+        )
+        {
+            result in
+            print(result)
+            XCTAssertNotNil(result.error)
+            XCTAssertTrue(result.error!.isTaskCancelled)
+            exp.fulfill()
+        }
+        task?.cancel()
+
+        waitForExpectations(timeout: 1, handler: nil)
+
+    }
 }
 
 class SimpleProcessor: ImageProcessor {