added iOS source code
[wl-app.git] / iOS / Pods / Kingfisher / Sources / KingfisherManager.swift
diff --git a/iOS/Pods/Kingfisher/Sources/KingfisherManager.swift b/iOS/Pods/Kingfisher/Sources/KingfisherManager.swift
new file mode 100755 (executable)
index 0000000..1f8a8f4
--- /dev/null
@@ -0,0 +1,265 @@
+//
+//  KingfisherManager.swift
+//  Kingfisher
+//
+//  Created by Wei Wang on 15/4/6.
+//
+//  Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  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
+
+public typealias DownloadProgressBlock = ((_ receivedSize: Int64, _ totalSize: Int64) -> Void)
+public typealias CompletionHandler = ((_ image: Image?, _ error: NSError?, _ cacheType: CacheType, _ imageURL: URL?) -> Void)
+
+/// RetrieveImageTask represents a task of image retrieving process.
+/// It contains an async task of getting image from disk and from network.
+public final class RetrieveImageTask {
+    
+    public static let empty = RetrieveImageTask()
+    
+    // If task is canceled before the download task started (which means the `downloadTask` is nil),
+    // the download task should not begin.
+    var cancelledBeforeDownloadStarting: Bool = false
+    
+    /// The network retrieve task in this image task.
+    public var downloadTask: RetrieveImageDownloadTask?
+    
+    /**
+    Cancel current task. If this task is already done, do nothing.
+    */
+    public func cancel() {
+        if let downloadTask = downloadTask {
+            downloadTask.cancel()
+        } else {
+            cancelledBeforeDownloadStarting = true
+        }
+    }
+}
+
+/// Error domain of Kingfisher
+public let KingfisherErrorDomain = "com.onevcat.Kingfisher.Error"
+
+/// Main manager class of Kingfisher. It connects Kingfisher downloader and cache.
+/// 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.
+    public static let shared = KingfisherManager()
+    
+    /// Cache used by this manager
+    public var cache: ImageCache
+    
+    /// Downloader used by this manager
+    public var downloader: ImageDownloader
+    
+    /// Default options used by the manager. This option will be used in 
+    /// Kingfisher manager related methods, including all image view and 
+    /// button extension methods. You can also passing the options per image by 
+    /// sending an `options` parameter to Kingfisher's APIs, the per image option 
+    /// will overwrite the default ones if exist.
+    ///
+    /// - Note: This option will not be applied to independent using of `ImageDownloader` or `ImageCache`.
+    public var defaultOptions = KingfisherEmptyOptionsInfo
+    
+    var currentDefaultOptions: KingfisherOptionsInfo {
+        return [.downloader(downloader), .targetCache(cache)] + defaultOptions
+    }
+    
+    convenience init() {
+        self.init(downloader: .default, cache: .default)
+    }
+    
+    init(downloader: ImageDownloader, cache: ImageCache) {
+        self.downloader = downloader
+        self.cache = cache
+    }
+    
+    /**
+    Get an image with resource.
+    If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first.
+    If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`.
+    These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more.
+    
+    - parameter resource:          Resource object contains information such as `cacheKey` and `downloadURL`.
+    - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+    - parameter progressBlock:     Called every time downloaded data changed. This could be used as a progress UI.
+    - parameter completionHandler: Called when the whole retrieving process finished.
+    
+    - returns: A `RetrieveImageTask` task object. You can use this object to cancel the task.
+    */
+    @discardableResult
+    public func retrieveImage(with resource: Resource,
+        options: KingfisherOptionsInfo?,
+        progressBlock: DownloadProgressBlock?,
+        completionHandler: CompletionHandler?) -> RetrieveImageTask
+    {
+        let task = RetrieveImageTask()
+        let options = currentDefaultOptions + (options ?? KingfisherEmptyOptionsInfo)
+        if options.forceRefresh {
+            _ = downloadAndCacheImage(
+                with: resource.downloadURL,
+                forKey: resource.cacheKey,
+                retrieveImageTask: task,
+                progressBlock: progressBlock,
+                completionHandler: completionHandler,
+                options: options)
+        } else {
+            tryToRetrieveImageFromCache(
+                forKey: resource.cacheKey,
+                with: resource.downloadURL,
+                retrieveImageTask: task,
+                progressBlock: progressBlock,
+                completionHandler: completionHandler,
+                options: options)
+        }
+        
+        return task
+    }
+
+    @discardableResult
+    func downloadAndCacheImage(with url: URL,
+                             forKey key: String,
+                      retrieveImageTask: RetrieveImageTask,
+                          progressBlock: DownloadProgressBlock?,
+                      completionHandler: CompletionHandler?,
+                                options: KingfisherOptionsInfo) -> RetrieveImageDownloadTask?
+    {
+        let downloader = options.downloader
+        return downloader.downloadImage(with: url, retrieveImageTask: retrieveImageTask, options: options,
+            progressBlock: { receivedSize, totalSize in
+                progressBlock?(receivedSize, totalSize)
+            },
+            completionHandler: { image, error, imageURL, originalData in
+
+                let targetCache = options.targetCache
+                if let error = error, error.code == KingfisherError.notModified.rawValue {
+                    // Not modified. Try to find the image from cache.
+                    // (The image should be in cache. It should be guaranteed by the framework users.)
+                    targetCache.retrieveImage(forKey: key, options: options, completionHandler: { (cacheImage, cacheType) -> Void in
+                        completionHandler?(cacheImage, nil, cacheType, url)
+                    })
+                    return
+                }
+                
+                if let image = image, let originalData = originalData {
+                    targetCache.store(image,
+                                      original: originalData,
+                                      forKey: key,
+                                      processorIdentifier:options.processor.identifier,
+                                      cacheSerializer: options.cacheSerializer,
+                                      toDisk: !options.cacheMemoryOnly,
+                                      completionHandler: nil)
+                    if options.cacheOriginalImage && options.processor != DefaultImageProcessor.default {
+                        let originalCache = options.originalCache
+                        let defaultProcessor = DefaultImageProcessor.default
+                        if let originalImage = defaultProcessor.process(item: .data(originalData), options: options) {
+                            originalCache.store(originalImage,
+                                              original: originalData,
+                                              forKey: key,
+                                              processorIdentifier: defaultProcessor.identifier,
+                                              cacheSerializer: options.cacheSerializer,
+                                              toDisk: !options.cacheMemoryOnly,
+                                              completionHandler: nil)
+                        }
+                    }
+                }
+
+                completionHandler?(image, error, .none, url)
+
+            })
+    }
+    
+    func tryToRetrieveImageFromCache(forKey key: String,
+                                       with url: URL,
+                              retrieveImageTask: RetrieveImageTask,
+                                  progressBlock: DownloadProgressBlock?,
+                              completionHandler: CompletionHandler?,
+                                        options: KingfisherOptionsInfo)
+    {
+
+        let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> Void in
+            completionHandler?(image, error, cacheType, imageURL)
+        }
+        
+        func handleNoCache() {
+            if options.onlyFromCache {
+                let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notCached.rawValue, userInfo: nil)
+                diskTaskCompletionHandler(nil, error, .none, url)
+                return
+            }
+            self.downloadAndCacheImage(
+                with: url,
+                forKey: key,
+                retrieveImageTask: retrieveImageTask,
+                progressBlock: progressBlock,
+                completionHandler: diskTaskCompletionHandler,
+                options: options)
+            
+        }
+        
+        let targetCache = options.targetCache
+        // First, try to get the exactly image from cache
+        targetCache.retrieveImage(forKey: key, options: options) { image, cacheType in
+            // If found, we could finish now.
+            if image != nil {
+                diskTaskCompletionHandler(image, nil, cacheType, url)
+                return
+            }
+            
+            // If not found, and we are using a default processor, download it!
+            let processor = options.processor
+            guard processor != DefaultImageProcessor.default else {
+                handleNoCache()
+                return
+            }
+            
+            // If processor is not the default one, we have a chance to check whether
+            // the original image is already in cache.
+            let originalCache = options.originalCache
+            let optionsWithoutProcessor = options.removeAllMatchesIgnoringAssociatedValue(.processor(processor))
+            originalCache.retrieveImage(forKey: key, options: optionsWithoutProcessor) { image, cacheType in
+                // If we found the original image, there is no need to download it again.
+                // We could just apply processor to it now.
+                guard let image = image else {
+                    handleNoCache()
+                    return
+                }
+                
+                guard let processedImage = processor.process(item: .image(image), options: options) else {
+                    diskTaskCompletionHandler(nil, nil, .none, url)
+                    return
+                }
+                targetCache.store(processedImage,
+                                  original: nil,
+                                  forKey: key,
+                                  processorIdentifier:options.processor.identifier,
+                                  cacheSerializer: options.cacheSerializer,
+                                  toDisk: !options.cacheMemoryOnly,
+                                  completionHandler: nil)
+                diskTaskCompletionHandler(processedImage, nil, .none, url)
+            }
+        }
+    }
+}