Browse Source

Add comment for manager class

onevcat 7 years ago
parent
commit
a82ab3aa22

+ 9 - 3
Sources/Cache/ImageCache.swift

@@ -202,6 +202,7 @@ open class ImageCache {
                       processorIdentifier identifier: String = "",
                       cacheSerializer serializer: CacheSerializer = DefaultCacheSerializer.default,
                       toDisk: Bool = true,
+                      callbackQueue: CallbackQueue = .current,
                       completionHandler: ((CacheStoreResult) -> Void)? = nil)
     {
         let computedKey = key.computedKey(with: identifier)
@@ -228,11 +229,15 @@ open class ImageCache {
                         reason: .cannotSerializeImage(image: image, original: original, serializer: serializer))
                     result = CacheStoreResult(memoryCacheResult: .success(()), diskCacheResult: .failure(diskError))
                 }
-                completionHandler?(result)
+                if let completionHandler = completionHandler {
+                    callbackQueue.execute { completionHandler(result) }
+                }
             }
         } else {
-            let result = CacheStoreResult(memoryCacheResult: .success(()), diskCacheResult: .success(()))
-            completionHandler?(result)
+            if let completionHandler = completionHandler {
+                let result = CacheStoreResult(memoryCacheResult: .success(()), diskCacheResult: .success(()))
+                callbackQueue.execute { completionHandler(result) }
+            }
         }
     }
     
@@ -312,6 +317,7 @@ open class ImageCache {
                         }
                     
                         // Cache the disk image to memory.
+                        // Memory cache does not change callback queue, which is already which we want.
                         self.store(
                             image,
                             forKey: key,

+ 132 - 59
Sources/General/KingfisherManager.swift

@@ -24,50 +24,63 @@
 //  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 //  THE SOFTWARE.
 
-#if os(macOS)
-import AppKit
-#else
-import UIKit
-#endif
 
+import Foundation
+
+/// The downloading progress block type.
+/// The parameter value is the `receivedSize` of current response.
+/// The second parameter is the total expected data length from response's "Content-Length" header.
+/// If the expected length is not available, this block will not be called.
 public typealias DownloadProgressBlock = ((_ receivedSize: Int64, _ totalSize: Int64) -> Void)
 
+/// Represents the result of a Kingfisher retrieving image task.
 public struct RetrieveImageResult {
+
+    /// Gets the image object of this result.
     public let image: Image
+
+    /// Gets the cache source of the image. It indicates from which layer of cache this image is retrieved.
+    /// If the image is just downloaded from network, `.none` will be returned.
     public let cacheType: CacheType
+
+    /// The resource URL of image.
     public let imageURL: URL
 }
 
-/// Main manager class of Kingfisher. It connects Kingfisher downloader and cache.
+/// Main manager class of Kingfisher. It connects Kingfisher downloader and cache,
+/// to provide a set of connivence methods to use Kingfisher for tasks.
 /// You can use this class to retrieve an image via a specified URL from web or cache.
 public class KingfisherManager {
     
-    /// Shared manager used by the extensions across Kingfisher.
+    /// Represents a shared manager used across Kingfisher.
+    /// Use this instance for getting or storing images with Kingfisher.
     public static let shared = KingfisherManager()
     
-    /// Cache used by this manager
+    /// The `ImageCache` used by this manager. It is `ImageCache.default` by default.
+    /// If a cache is specified in `KingfisherManager.defaultOptions`, the value in `defaultOptions` will be
+    /// used instead.
     public var cache: ImageCache
     
-    /// Downloader used by this manager
+    /// The `ImageDownloader` used by this manager. It is `ImageDownloader.default` by default.
+    /// If a downloader is specified in `KingfisherManager.defaultOptions`, the value in `defaultOptions` will be
+    /// used instead.
     public var downloader: ImageDownloader
     
     /// Default options used by the manager. This option will be used in
-    /// Kingfisher manager related methods, and all image view and
-    /// button extension methods. You can also passing other options for each image task by
-    /// sending an `options` parameter to Kingfisher's APIs, the per image options
-    /// will overwrite the default ones, if exist in both.
-    ///
-    /// - Note: This option will not be applied to independent using of `ImageDownloader` or `ImageCache`.
+    /// Kingfisher manager related methods, as well as all view extension methods.
+    /// You can also passing other options for each image task by sending an `options` parameter
+    /// to Kingfisher's APIs. The per image options will overwrite the default ones,
+    /// if the option exists in both.
     public var defaultOptions = KingfisherOptionsInfo.empty
     
     // Use `defaultOptions` to overwrite the `downloader` and `cache`.
-    var currentDefaultOptions: KingfisherOptionsInfo {
+    private var currentDefaultOptions: KingfisherOptionsInfo {
         return [.downloader(downloader), .targetCache(cache)] + defaultOptions
     }
 
     private let processQueue: DispatchQueue
     
-    convenience init() {
+    private convenience init() {
         self.init(downloader: .default, cache: .default)
     }
     
@@ -76,15 +89,32 @@ public class KingfisherManager {
         self.cache = cache
 
         let processQueueName = "com.onevcat.Kingfisher.KingfisherManager.processQueue.\(UUID().uuidString)"
-        processQueue = DispatchQueue(label: processQueueName, attributes: .concurrent)
+        processQueue = DispatchQueue(label: processQueueName)
     }
-    
+
+    /// Gets an image from a given resource.
+    ///
+    /// - Parameters:
+    ///   - resource: The `Resource` object defines data information like key or URL.
+    ///   - options: Options to use when creating the animated image.
+    ///   - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an
+    ///                    `expectedContentLength`, this block will not be called. `progressBlock` is always called in
+    ///                    main queue.
+    ///   - completionHandler: Called when the image retrieved and set finished. This completion handler will be invoked
+    ///                        from the `options.callbackQueue`. If not specified, the main queue will be used.
+    /// - Returns: A task represents the image downloading. If there is no downloading starts, `nil` is returned.
+    ///
+    /// - Note:
+    ///    This method will first check whether the requested `resource` is already in cache or not. If cached,
+    ///    it returns `nil` and invoke the `completionHandler` after the cached image retrieved. Otherwise, it
+    ///    will download the `resource`, store it in cache, then call `completionHandler`.
+    ///
     @discardableResult
-    public func retrieveImage(with resource: Resource,
-                              options: KingfisherOptionsInfo? = nil,
-                              progressBlock: DownloadProgressBlock? = nil,
-                              completionHandler: ((Result<RetrieveImageResult>) -> Void)?)
-        -> DownloadTask?
+    public func retrieveImage(
+        with resource: Resource,
+        options: KingfisherOptionsInfo? = nil,
+        progressBlock: DownloadProgressBlock? = nil,
+        completionHandler: ((Result<RetrieveImageResult>) -> Void)?) -> DownloadTask?
     {
         let options = currentDefaultOptions + (options ?? .empty)
         if options.forceRefresh {
@@ -95,14 +125,13 @@ public class KingfisherManager {
                 progressBlock: progressBlock,
                 completionHandler: completionHandler)
         } else {
-            let loadFromCache = tryToRetrieveImageFromCache(
+            let loadedFromCache = retrieveImageFromCache(
                 forKey: resource.cacheKey,
                 with: resource.downloadURL,
-                progressBlock: progressBlock,
-                completionHandler: completionHandler,
-                options: options)
+                options: options,
+                completionHandler: completionHandler)
 
-            if loadFromCache {
+            if loadedFromCache {
                 return nil
             }
 
@@ -121,25 +150,37 @@ public class KingfisherManager {
         }
     }
 
+    /// Download and cache the image with given parameters.
+    ///
+    /// - Parameters:
+    ///   - url: The target URL from where the image data could be downloaded.
+    ///   - key: The key to use when caching the image.
+    ///   - options: Options on how to process or serialize the image data.
+    ///   - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an
+    ///                    `expectedContentLength`, this block will not be called. `progressBlock` is always called in
+    ///                    main queue.
+    ///   - completionHandler: Called when the process finishes, either with succeeded
+    ///                        `RetrieveImageResult` or an error.
+    /// - Returns: A task represents the image downloading. If there is no downloading starts, `nil` is returned.
     @discardableResult
-    func downloadAndCacheImage(with url: URL,
-                             forKey key: String,
-                             options: KingfisherOptionsInfo,
-                          progressBlock: DownloadProgressBlock? = nil,
-                      completionHandler: ((Result<RetrieveImageResult>) -> Void)?)
-        -> DownloadTask?
+    func downloadAndCacheImage(
+        with url: URL,
+        forKey key: String,
+        options: KingfisherOptionsInfo,
+        progressBlock: DownloadProgressBlock? = nil,
+        completionHandler: ((Result<RetrieveImageResult>) -> Void)?) -> DownloadTask?
     {
         let downloader = options.downloader ?? self.downloader
-        let processQueue = self.processQueue
 
         return downloader.downloadImage(
             with: url,
             options: options,
-            progressBlock: { receivedSize, totalSize in progressBlock?(receivedSize, totalSize)})
+            progressBlock: progressBlock)
         {
             result in
             switch result {
             case .success(let value):
+                // Add image to cache.
                 let targetCache = options.targetCache ?? self.cache
                 targetCache.store(
                     value.image,
@@ -147,28 +188,37 @@ public class KingfisherManager {
                     forKey: key,
                     processorIdentifier: options.processor.identifier,
                     cacheSerializer: options.cacheSerializer,
-                    toDisk: !options.cacheMemoryOnly)
+                    toDisk: !options.cacheMemoryOnly,
+                    callbackQueue: .dispatch(options.callbackDispatchQueue))
                 {
                     _ in
-                    guard options.waitForCache else { return }
-                    let result = RetrieveImageResult(image: value.image, cacheType: .none, imageURL: url)
-                    options.callbackDispatchQueue.async { completionHandler?(.success(result)) }
+                    if options.waitForCache {
+                        let result = RetrieveImageResult(image: value.image, cacheType: .none, imageURL: url)
+                        completionHandler?(.success(result))
+                    }
                 }
 
-                if options.cacheOriginalImage && options.processor != DefaultImageProcessor.default {
-                    let originalCache = options.originalCache ?? targetCache
+                // Add original image to cache if necessary.
+                let needToCacheOriginalImage = options.cacheOriginalImage &&
+                                               options.processor != DefaultImageProcessor.default
+                if needToCacheOriginalImage {
                     let defaultProcessor = DefaultImageProcessor.default
-                    processQueue.async {
-                        if let originalImage = defaultProcessor.process(item: .data(value.originalData), options: options) {
-                            originalCache.store(originalImage,
-                                                original: value.originalData,
-                                                forKey: key,
-                                                processorIdentifier: defaultProcessor.identifier,
-                                                cacheSerializer: options.cacheSerializer,
-                                                toDisk: !options.cacheMemoryOnly)
-                        }
+                    self.processQueue.async {
+                        guard let originalImage =
+                            defaultProcessor.process(item: .data(value.originalData), options: options)
+                        else { return }
+
+                        let originalCache = options.originalCache ?? targetCache
+                        originalCache.store(
+                            originalImage,
+                            original: value.originalData,
+                            forKey: key,
+                            processorIdentifier: defaultProcessor.identifier,
+                            cacheSerializer: options.cacheSerializer,
+                            toDisk: !options.cacheMemoryOnly)
                     }
                 }
+
                 if !options.waitForCache {
                     let result = RetrieveImageResult(image: value.image, cacheType: .none, imageURL: url)
                     completionHandler?(.success(result))
@@ -179,11 +229,29 @@ public class KingfisherManager {
         }
     }
     
-    func tryToRetrieveImageFromCache(forKey key: String,
-                                       with url: URL,
-                                  progressBlock: DownloadProgressBlock?,
-                              completionHandler: ((Result<RetrieveImageResult>) -> Void)?,
-                                        options: KingfisherOptionsInfo) -> Bool
+    /// Retrieves image from memory or disk cache.
+    ///
+    /// - Parameters:
+    ///   - key: The key to use when caching the image.
+    ///   - url: Image request URL. This is not used when retrieving image from cache. It is just used for
+    ///          `RetrieveImageResult` callback compatibility.
+    ///   - options: Options on how to get the image from image cache.
+    ///   - completionHandler: Called when the image retrieving finishes, either with succeeded
+    ///                        `RetrieveImageResult` or an error.
+    /// - Returns: `true` if the requested image or the original image before being processed is existing in cache.
+    ///            Otherwise, this method returns `false`.
+    ///
+    /// - Note:
+    ///    The image retrieving could happen in either memory cache or disk cache. The `.processor` option in
+    ///    `options` will be considered when searching in the cache. If no processed image is found, Kingfisher
+    ///    will try to check whether an original version of that image is existing or not. If there is already an
+    ///    original, Kingfisher retrieves it from cache and processes it. Then, the processed image will be store
+    ///    back to cache for later use.
+    func retrieveImageFromCache(
+        forKey key: String,
+        with url: URL,
+        options: KingfisherOptionsInfo,
+        completionHandler: ((Result<RetrieveImageResult>) -> Void)?) -> Bool
     {
         // 1. Check whether the image was already in target cache. If so, just get it.
         let targetCache = options.targetCache ?? cache
@@ -208,14 +276,17 @@ public class KingfisherManager {
 
         // 2. Check whether the original image exists. If so, get it, process it, save to storage and return.
         let originalCache = options.originalCache ?? targetCache
-        // No need to search the same file in the same cache again.
+        // No need to store the same file in the same cache again.
         if originalCache === targetCache && options.processor == DefaultImageProcessor.default {
             return false
         }
 
+        // Check whether the unprocessed image existing or not.
         let originalImageCached = originalCache.imageCachedType(
             forKey: key, processorIdentifier: DefaultImageProcessor.default.identifier).cached
         if originalImageCached {
+            // Now we are ready to get found the original image from cache. We need the unprocessed image, so remove
+            // any processor from options first.
             let optionsWithoutProcessor = options.removeAllMatchesIgnoringAssociatedValue(.processor(options.processor))
             originalCache.retrieveImage(forKey: key, options: optionsWithoutProcessor) { result in
                 if let image = result.value?.image {
@@ -235,7 +306,7 @@ public class KingfisherManager {
                             forKey: key,
                             processorIdentifier: processor.identifier,
                             cacheSerializer: options.cacheSerializer,
-                            toDisk: !options.onlyFromCache)
+                            toDisk: !options.cacheMemoryOnly)
                         {
                             _ in
                             if options.waitForCache {
@@ -250,6 +321,8 @@ public class KingfisherManager {
                         }
                     }
                 } else {
+                    // This should not happen actually, since we already confirmed `originalImageCached` is `true`.
+                    // Just in case...
                     completionHandler?(.failure(KingfisherError.cacheError(reason: .imageNotExisting(key: key))))
                 }
             }

+ 2 - 3
Sources/General/KingfisherOptionsInfo.swift

@@ -31,9 +31,8 @@ import UIKit
 #endif
     
 
-/**
-*	KingfisherOptionsInfo is a typealias for [KingfisherOptionsInfoItem]. You can use the enum of option item with value to control some behaviors of Kingfisher.
-*/
+/// KingfisherOptionsInfo is a typealias for [KingfisherOptionsInfoItem].
+/// You can use the enum of option item with value to control some behaviors of Kingfisher.
 public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem]
 
 extension Array where Element == KingfisherOptionsInfoItem {