فهرست منبع

Abstract async operation

onevcat 5 سال پیش
والد
کامیت
d87ee58759
3فایلهای تغییر یافته به همراه71 افزوده شده و 26 حذف شده
  1. 2 2
      Sources/General/KingfisherOptionsInfo.swift
  2. 54 23
      Sources/Networking/ImageDownloader.swift
  3. 15 1
      Sources/Networking/RequestModifier.swift

+ 2 - 2
Sources/General/KingfisherOptionsInfo.swift

@@ -127,7 +127,7 @@ public enum KingfisherOptionsInfoItem {
     /// This is the last chance you can modify the image download request. You can modify the request for some
     /// customizing purpose, such as adding auth token to the header, do basic HTTP auth or something like url mapping.
     /// The original request will be sent without any modification by default.
-    case requestModifier(ImageDownloadRequestModifier)
+    case requestModifier(AsyncImageDownloadRequestModifier)
     
     /// The `ImageDownloadRedirectHandler` contained will be used to change the request before redirection.
     /// This is the possibility you can modify the image download request during redirect. You can modify the request for
@@ -272,7 +272,7 @@ public struct KingfisherParsedOptionsInfo {
     public var preloadAllAnimationData = false
     public var callbackQueue: CallbackQueue = .mainCurrentOrAsync
     public var scaleFactor: CGFloat = 1.0
-    public var requestModifier: ImageDownloadRequestModifier? = nil
+    public var requestModifier: AsyncImageDownloadRequestModifier? = nil
     public var redirectHandler: ImageDownloadRedirectHandler? = nil
     public var processor: ImageProcessor = DefaultImageProcessor.default
     public var imageModifier: ImageModifier? = nil

+ 54 - 23
Sources/Networking/ImageDownloader.swift

@@ -218,6 +218,42 @@ open class ImageDownloader {
         )
     }
 
+    private func createDownloadContext(
+        with url: URL,
+        options: KingfisherParsedOptionsInfo,
+        done: ((Result<DownloadingContext, KingfisherError>) -> Void)
+    )
+    {
+        func checkRequestAndDone(r: URLRequest) {
+
+            // There is a possibility that request modifier changed the url to `nil` or empty.
+            // In this case, throw an error.
+            guard let url = r.url, !url.absoluteString.isEmpty else {
+                done(.failure(KingfisherError.requestError(reason: .invalidURL(request: r))))
+                return
+            }
+
+            done(.success(DownloadingContext(url: url, request: r, options: options)))
+        }
+
+        // Creates default request.
+        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: downloadTimeout)
+        request.httpShouldUsePipelining = requestsUsePipelining
+
+        if let requestModifier = options.requestModifier {
+            // Modifies request before sending.
+            requestModifier.modified(for: request) { result in
+                guard let finalRequest = result else {
+                    done(.failure(KingfisherError.requestError(reason: .emptyRequest)))
+                    return
+                }
+                checkRequestAndDone(r: finalRequest)
+            }
+        } else {
+            checkRequestAndDone(r: request)
+        }
+    }
+
     private func addDownloadTask(
         context: DownloadingContext,
         callback: SessionDataTask.TaskCallback
@@ -319,34 +355,29 @@ open class ImageDownloader {
         options: KingfisherParsedOptionsInfo,
         completionHandler: ((Result<ImageLoadingResult, KingfisherError>) -> Void)? = nil) -> DownloadTask?
     {
-        // Creates default request.
-        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: downloadTimeout)
-        request.httpShouldUsePipelining = requestsUsePipelining
-
-        if let requestModifier = options.requestModifier {
-            // Modifies request before sending.
-            guard let r = requestModifier.modified(for: request) else {
+        var downloadTask: DownloadTask?
+        createDownloadContext(with: url, options: options) { result in
+            switch result {
+            case .success(let context):
+                // `downloadTask` will be set if the downloading started immediately. This is the case when no request
+                // modifier or a sync modifier (`ImageDownloadRequestModifier`) is used. Otherwise, when an
+                // `AsyncImageDownloadRequestModifier` is used the returned `downloadTask` of this method will be `nil`
+                // and the actual "delayed" task is given in `AsyncImageDownloadRequestModifier.onDownloadTaskStarted`
+                // callback.
+                downloadTask = startDownloadTask(
+                    context: context,
+                    callback: createTaskCallback(completionHandler, options: options)
+                )
+                if let modifier = options.requestModifier {
+                    modifier.onDownloadTaskStarted?(downloadTask)
+                }
+            case .failure(let error):
                 options.callbackQueue.execute {
-                    completionHandler?(.failure(KingfisherError.requestError(reason: .emptyRequest)))
+                    completionHandler?(.failure(error))
                 }
-                return nil
-            }
-            request = r
-        }
-        
-        // There is a possibility that request modifier changed the url to `nil` or empty.
-        // In this case, throw an error.
-        guard let url = request.url, !url.absoluteString.isEmpty else {
-            options.callbackQueue.execute {
-                completionHandler?(.failure(KingfisherError.requestError(reason: .invalidURL(request: request))))
             }
-            return nil
         }
 
-        let downloadTask = startDownloadTask(
-            context: DownloadingContext(url: url, request: request, options: options),
-            callback: createTaskCallback(completionHandler, options: options)
-        )
         return downloadTask
     }
 

+ 15 - 1
Sources/Networking/RequestModifier.swift

@@ -26,8 +26,13 @@
 
 import Foundation
 
+public protocol AsyncImageDownloadRequestModifier {
+    func modified(for request: URLRequest, modify: (URLRequest?) -> Void)
+    var onDownloadTaskStarted: ((DownloadTask?) -> Void)? { get }
+}
+
 /// Represents and wraps a method for modifying request before an image download request starts.
-public protocol ImageDownloadRequestModifier {
+public protocol ImageDownloadRequestModifier: AsyncImageDownloadRequestModifier {
 
     /// A method will be called just before the `request` being sent.
     /// This is the last chance you can modify the image download request. You can modify the request for some
@@ -46,6 +51,15 @@ public protocol ImageDownloadRequestModifier {
     func modified(for request: URLRequest) -> URLRequest?
 }
 
+extension ImageDownloadRequestModifier {
+    public func modified(for request: URLRequest, modify: (URLRequest?) -> Void) {
+        let request = modified(for: request)
+        modify(request)
+    }
+
+    public var onDownloadTaskStarted: ((DownloadTask?) -> Void)? { return nil }
+}
+
 /// A wrapper for creating an `ImageDownloadRequestModifier` easier.
 /// This type conforms to `ImageDownloadRequestModifier` and wraps an image modify block.
 public struct AnyModifier: ImageDownloadRequestModifier {