Просмотр исходного кода

Merge pull request #894 from onevcat/feature/cancel-all

API to cancel all downloading task
Wei Wang 7 лет назад
Родитель
Сommit
3d28d4d087
2 измененных файлов с 81 добавлено и 10 удалено
  1. 39 10
      Sources/ImageDownloader.swift
  2. 42 0
      Tests/KingfisherTests/ImageDownloaderTests.swift

+ 39 - 10
Sources/ImageDownloader.swift

@@ -43,11 +43,11 @@ public struct RetrieveImageDownloadTask {
     /// Downloader by which this task is intialized.
     public private(set) weak var ownerDownloader: ImageDownloader?
 
-    /**
-     Cancel this download task. It will trigger the completion handler with an NSURLErrorCancelled error.
-     */
+    
+    /// Cancel this download task. It will trigger the completion handler with an NSURLErrorCancelled error.
+    /// If you want to cancel all downloading tasks, call `cancelAll()` of `ImageDownloader` instance.
     public func cancel() {
-        ownerDownloader?.cancelDownloadingTask(self)
+        ownerDownloader?.cancel(self)
     }
     
     /// The original request URL of this download task.
@@ -402,13 +402,42 @@ extension ImageDownloader {
         }
     }
     
-    func cancelDownloadingTask(_ task: RetrieveImageDownloadTask) {
+    private func cancelTaskImpl(_ task: RetrieveImageDownloadTask, fetchLoad: ImageFetchLoad? = nil, ignoreTaskCount: Bool = false) {
+        
+        func getFetchLoad(from task: RetrieveImageDownloadTask) -> ImageFetchLoad? {
+            guard let URL = task.internalTask.originalRequest?.url,
+                  let imageFetchLoad = self.fetchLoads[URL] else
+            {
+                return nil
+            }
+            return imageFetchLoad
+        }
+        
+        guard let imageFetchLoad = fetchLoad ?? getFetchLoad(from: task) else {
+            return
+        }
+
+        imageFetchLoad.downloadTaskCount -= 1
+        if ignoreTaskCount || imageFetchLoad.downloadTaskCount == 0 {
+            task.internalTask.cancel()
+        }
+    }
+    
+    func cancel(_ task: RetrieveImageDownloadTask) {
+        barrierQueue.sync(flags: .barrier) { cancelTaskImpl(task) }
+    }
+    
+    /// Cancell all downloading tasks. It will trigger the completion handlers for all not-yet-finished
+    /// downloading tasks with an NSURLErrorCancelled error.
+    ///
+    /// If you need to only cacnel a certain task, call `cancel()` on the `RetrieveImageDownloadTask`
+    /// returned by the downloading methods.
+    public func cancelAll() {
         barrierQueue.sync(flags: .barrier) {
-            if let URL = task.internalTask.originalRequest?.url, let imageFetchLoad = self.fetchLoads[URL] {
-                imageFetchLoad.downloadTaskCount -= 1
-                if imageFetchLoad.downloadTaskCount == 0 {
-                    task.internalTask.cancel()
-                }
+            fetchLoads.forEach { v in
+                let fetchLoad = v.value
+                guard let task = fetchLoad.downloadTask else { return }
+                cancelTaskImpl(task, fetchLoad: fetchLoad, ignoreTaskCount: true)
             }
         }
     }

+ 42 - 0
Tests/KingfisherTests/ImageDownloaderTests.swift

@@ -274,6 +274,48 @@ class ImageDownloaderTests: XCTestCase {
         waitForExpectations(timeout: 5, handler: nil)
     }
     
+    func testCancelAllDownloadTasks() {
+        let expectation = self.expectation(description: "wait for downloading")
+        let URLString1 = testKeys[0]
+        let stub1 = stubRequest("GET", URLString1).andReturn(200)?.withBody(testImageData)?.delay()
+        let url1 = URL(string: URLString1)!
+        
+        let URLString2 = testKeys[1]
+        let stub2 = stubRequest("GET", URLString2).andReturn(200)?.withBody(testImageData)?.delay()
+        let url2 = URL(string: URLString2)!
+        
+        let group = DispatchGroup()
+        
+        group.enter()
+        let _ = downloader.downloadImage(with: url1) { _, error, _, _ in
+            XCTAssertNotNil(error)
+            XCTAssertEqual(error?.code, NSURLErrorCancelled)
+            group.leave()
+        }
+        
+        group.enter()
+        let _ = downloader.downloadImage(with: url1) { _, error, _, _ in
+            XCTAssertNotNil(error)
+            XCTAssertEqual(error?.code, NSURLErrorCancelled)
+            group.leave()
+        }
+        
+        group.enter()
+        let _ = downloader.downloadImage(with: url2) { _, error, _, _ in
+            XCTAssertNotNil(error)
+            XCTAssertEqual(error?.code, NSURLErrorCancelled)
+            group.leave()
+        }
+        
+        delay(0.1) {
+            self.downloader.cancelAll()
+            _ = stub1!.go()
+            _ = stub2!.go()
+        }
+        group.notify(queue: .main, execute: expectation.fulfill)
+        waitForExpectations(timeout: 5, handler: nil)
+    }
+    
     // Issue 532 https://github.com/onevcat/Kingfisher/issues/532#issuecomment-305644311
     func testCancelThenRestartSameDownload() {
         let expectation = self.expectation(description: "wait for downloading")