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

Improve code quality and tests

onevcat 7 лет назад
Родитель
Сommit
5f80f07303

+ 0 - 16
Kingfisher.xcodeproj/project.pbxproj

@@ -7,7 +7,6 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
-		008050DF31B6F246FFC8BEA1 /* libPods-KingfisherTests-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1446B878F6627F0B79BB366C /* libPods-KingfisherTests-tvOS.a */; };
 		185218B61CC07F8300BD58DE /* NSButtonExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 185218B51CC07F8300BD58DE /* NSButtonExtensionTests.swift */; };
 		4B10480D216F157000300C61 /* ImageDataProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B10480C216F157000300C61 /* ImageDataProcessor.swift */; };
 		4B10480E216F157000300C61 /* ImageDataProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B10480C216F157000300C61 /* ImageDataProcessor.swift */; };
@@ -41,7 +40,6 @@
 		4B8E291D216F40AA0095FAD1 /* AuthenticationChallengeResponsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8E291B216F40AA0095FAD1 /* AuthenticationChallengeResponsable.swift */; };
 		4B8E291E216F40AA0095FAD1 /* AuthenticationChallengeResponsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8E291B216F40AA0095FAD1 /* AuthenticationChallengeResponsable.swift */; };
 		4B8E291F216F40AA0095FAD1 /* AuthenticationChallengeResponsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8E291B216F40AA0095FAD1 /* AuthenticationChallengeResponsable.swift */; };
-		B8BBB7092D89EAC97D6ED888 /* libPods-KingfisherTests-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0268A213AE27BC6133BC5E0F /* libPods-KingfisherTests-macOS.a */; };
 		D10EC2361C3D632300A4211C /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B2944481C3D01B20088C3E7 /* Kingfisher.framework */; };
 		D114F36E215D2D0B00A01349 /* String+MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12AB6BC215D2BB50013BA68 /* String+MD5.swift */; };
 		D12AB6C0215D2BB50013BA68 /* RequestModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12AB69D215D2BB50013BA68 /* RequestModifier.swift */; };
@@ -202,7 +200,6 @@
 		D1DC4B421D60996D00DFDFAA /* StringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1DC4B401D60996D00DFDFAA /* StringExtensionTests.swift */; };
 		D1DC4B431D60996D00DFDFAA /* StringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1DC4B401D60996D00DFDFAA /* StringExtensionTests.swift */; };
 		D1ED2D401AD2D09F00CFC3EB /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1ED2D351AD2D09F00CFC3EB /* Kingfisher.framework */; };
-		D2F93EDA375F32898AFEE242 /* libPods-KingfisherTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D0E767B01589AA8BE21FFA6 /* libPods-KingfisherTests.a */; };
 		D9638BA61C7DC71F0046523D /* ImagePrefetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9638BA41C7DC71F0046523D /* ImagePrefetcherTests.swift */; };
 		D9638BA71C7DCF560046523D /* ImagePrefetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9638BA41C7DC71F0046523D /* ImagePrefetcherTests.swift */; };
 		D9638BA81C7DCF570046523D /* ImagePrefetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9638BA41C7DC71F0046523D /* ImagePrefetcherTests.swift */; };
@@ -236,17 +233,13 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
-		0268A213AE27BC6133BC5E0F /* libPods-KingfisherTests-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-KingfisherTests-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		033CD4A1D8C6D03D7AC150BE /* Pods-KingfisherTests-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KingfisherTests-tvOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-KingfisherTests-tvOS/Pods-KingfisherTests-tvOS.release.xcconfig"; sourceTree = "<group>"; };
-		1446B878F6627F0B79BB366C /* libPods-KingfisherTests-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-KingfisherTests-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		185218B51CC07F8300BD58DE /* NSButtonExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSButtonExtensionTests.swift; sourceTree = "<group>"; };
 		4AB2628A580157ADADCE0011 /* Pods-KingfisherTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KingfisherTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-KingfisherTests/Pods-KingfisherTests.debug.xcconfig"; sourceTree = "<group>"; };
 		4B10480C216F157000300C61 /* ImageDataProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDataProcessor.swift; sourceTree = "<group>"; };
 		4B164ACE1B8D554200768EC6 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
 		4B2944481C3D01B20088C3E7 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		4B37667F1C478F940001443F /* KingfisherTests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "KingfisherTests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
-		4B37669F1C4794460001443F /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.1.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; };
-		4B3766A11C47944D0001443F /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; };
 		4B3E714D1B01FEB200F5AAED /* WatchKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WatchKit.framework; path = System/Library/Frameworks/WatchKit.framework; sourceTree = SDKROOT; };
 		4B46CC5E217449C600D90C4A /* MemoryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryStorage.swift; sourceTree = "<group>"; };
 		4B46CC63217449E000D90C4A /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
@@ -258,7 +251,6 @@
 		4BCCF3441D5B0457003387C2 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		6CD5C0134AA4B1C0892E7319 /* Pods-KingfisherTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KingfisherTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-KingfisherTests/Pods-KingfisherTests.release.xcconfig"; sourceTree = "<group>"; };
 		7204D40BEFEA059FA25864C4 /* Pods-KingfisherTests-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KingfisherTests-macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-KingfisherTests-macOS/Pods-KingfisherTests-macOS.debug.xcconfig"; sourceTree = "<group>"; };
-		9D0E767B01589AA8BE21FFA6 /* libPods-KingfisherTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-KingfisherTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		D10EC2311C3D632300A4211C /* KingfisherTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "KingfisherTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		D12AB69D215D2BB50013BA68 /* RequestModifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestModifier.swift; sourceTree = "<group>"; };
 		D12AB69E215D2BB50013BA68 /* Resource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Resource.swift; sourceTree = "<group>"; };
@@ -333,7 +325,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				4B3766841C478F940001443F /* Kingfisher.framework in Frameworks */,
-				008050DF31B6F246FFC8BEA1 /* libPods-KingfisherTests-tvOS.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -342,7 +333,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				D10EC2361C3D632300A4211C /* Kingfisher.framework in Frameworks */,
-				B8BBB7092D89EAC97D6ED888 /* libPods-KingfisherTests-macOS.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -372,7 +362,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				D1ED2D401AD2D09F00CFC3EB /* Kingfisher.framework in Frameworks */,
-				D2F93EDA375F32898AFEE242 /* libPods-KingfisherTests.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -587,13 +576,8 @@
 		EA99D30544BD22799F7A5367 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
-				4B3766A11C47944D0001443F /* CFNetwork.framework */,
-				4B37669F1C4794460001443F /* CFNetwork.framework */,
 				4B164ACE1B8D554200768EC6 /* CFNetwork.framework */,
 				4B3E714D1B01FEB200F5AAED /* WatchKit.framework */,
-				9D0E767B01589AA8BE21FFA6 /* libPods-KingfisherTests.a */,
-				0268A213AE27BC6133BC5E0F /* libPods-KingfisherTests-macOS.a */,
-				1446B878F6627F0B79BB366C /* libPods-KingfisherTests-tvOS.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";

+ 3 - 3
Sources/Cache/ImageCache.swift

@@ -202,7 +202,7 @@ open class ImageCache {
                       processorIdentifier identifier: String = "",
                       cacheSerializer serializer: CacheSerializer = DefaultCacheSerializer.default,
                       toDisk: Bool = true,
-                      callbackQueue: CallbackQueue = .current,
+                      callbackQueue: CallbackQueue = .untouch,
                       completionHandler: ((CacheStoreResult) -> Void)? = nil)
     {
         let computedKey = key.computedKey(with: identifier)
@@ -289,7 +289,7 @@ open class ImageCache {
     */
     open func retrieveImage(forKey key: String,
                                options: KingfisherOptionsInfo? = nil,
-                        callbackQueue: CallbackQueue = .current,
+                        callbackQueue: CallbackQueue = .untouch,
                      completionHandler: ((Result<(ImageCacheResult)>) -> Void)?)
     {
         // No completion handler. Not start working and early return.
@@ -360,7 +360,7 @@ open class ImageCache {
     open func retrieveImageInDiskCache(
         forKey key: String,
         options: KingfisherOptionsInfo? = nil,
-        callbackQueue: CallbackQueue = .current,
+        callbackQueue: CallbackQueue = .untouch,
         completionHandler: @escaping (Result<Image?>) -> Void)
     {
         let options = options ?? .empty

+ 13 - 0
Sources/General/Deprecated.swift

@@ -424,3 +424,16 @@ extension ImageCache {
         }
     }
 }
+
+public extension Collection where Iterator.Element == KingfisherOptionsInfoItem {
+    /// The queue of callbacks should happen from Kingfisher.
+    @available(*, deprecated, message: "Use `callbackQueue` instead.", renamed: "callbackQueue")
+    public var callbackDispatchQueue: DispatchQueue {
+        if let item = lastMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)),
+            case .callbackDispatchQueue(let queue) = item
+        {
+            return queue ?? DispatchQueue.main
+        }
+        return callbackQueue.queue
+    }
+}

+ 1 - 1
Sources/General/KingfisherManager.swift

@@ -189,7 +189,7 @@ public class KingfisherManager {
                     processorIdentifier: options.processor.identifier,
                     cacheSerializer: options.cacheSerializer,
                     toDisk: !options.cacheMemoryOnly,
-                    callbackQueue: .dispatch(options.callbackDispatchQueue))
+                    callbackQueue: options.callbackQueue)
                 {
                     _ in
                     if options.waitForCache {

+ 24 - 17
Sources/General/KingfisherOptionsInfo.swift

@@ -38,7 +38,8 @@ public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem]
 extension Array where Element == KingfisherOptionsInfoItem {
     
     static var empty: KingfisherOptionsInfo = []
-    
+
+    // A connivence property to generate an image creating option with current `KingfisherOptionsInfo`.
     var imageCreatingOptions: ImageCreatingOptions {
         return ImageCreatingOptions(
             scale: scaleFactor,
@@ -102,7 +103,12 @@ public enum KingfisherOptionsInfoItem {
     
     /// The associated value of this member will be used as the target queue of dispatch callbacks when
     /// retrieving images from cache. If not set, `Kingfisher` will use main queue for callbacks.
+    @available(*, deprecated, message: "Use `.callbackQueue(CallbackQueue)` instead.")
     case callbackDispatchQueue(DispatchQueue?)
+
+    /// The associated value of this member will be used as the target queue of dispatch callbacks when
+    /// retrieving images from cache. If not set, `Kingfisher` will use `.mainCurrentOrAsync` for callbacks.
+    case callbackQueue(CallbackQueue)
     
     /// The associated value of this member will be used as the scale factor when converting retrieved data to an image.
     /// It is the image scale, instead of your screen scale. You may need to specify the correct scale when you dealing 
@@ -110,9 +116,9 @@ public enum KingfisherOptionsInfoItem {
     case scaleFactor(CGFloat)
 
     /// Whether all the animated image data should be preloaded. Default it false, which means following frames will be
-    /// loaded on need. If true, all the animated image data will be loaded and decoded into memory. This option is mainly
-    /// used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload
-    /// all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use
+    /// loaded on need. If true, all the animated image data will be loaded and decoded into memory. This option is
+    /// mainly used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not
+    /// preload all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use
     /// corresponding image view type instead of setting this option.
     case preloadAllAnimationData
     
@@ -133,11 +139,10 @@ public enum KingfisherOptionsInfoItem {
     /// `DefaultCacheSerializer.default` will be used by default.
     case cacheSerializer(CacheSerializer)
 
-    /// Modifier for modifying an image right before it is used.
-    /// If the image was fetched directly from the downloader, the modifier will
-    /// run directly after the processor.
-    /// If the image is being fetched from a cache, the modifier will run after
-    /// the cacheSerializer.
+    /// Modifier for modifying an image right before it is used. If the image was fetched directly from the downloader,
+    /// the modifier will run directly after the processor. If the image is being fetched from a cache, the modifier
+    /// will run after the cacheSerializer.
+    ///
     /// Use `ImageModifier` when you need to set properties on a concrete type
     /// of `Image`, such as a `UIImage`, that do not persist when caching the image.
     case imageModifier(ImageModifier)
@@ -150,6 +155,7 @@ public enum KingfisherOptionsInfoItem {
     /// If set, Kingfisher will only load the first frame from a animated image data file as a single image.
     /// Loading a lot of animated images may take too much memory. It will be useful when you want to display a
     /// static preview of the first frame from a animated image.
+    ///
     /// This option will be ignored if the target image is not animated image data.
     case onlyLoadFirstFrame
     
@@ -167,7 +173,8 @@ precedencegroup ItemComparisonPrecedence {
 
 infix operator <== : ItemComparisonPrecedence
 
-// This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values.
+// This operator returns true if two `KingfisherOptionsInfoItem` enum is the same,
+// without considering the associated values.
 func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool {
     switch (lhs, rhs) {
     case (.targetCache, .targetCache): return true
@@ -183,6 +190,7 @@ func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Boo
     case (.onlyFromCache, .onlyFromCache): return true
     case (.backgroundDecode, .backgroundDecode): return true
     case (.callbackDispatchQueue, .callbackDispatchQueue): return true
+    case (.callbackQueue, .callbackQueue): return true
     case (.scaleFactor, .scaleFactor): return true
     case (.preloadAllAnimationData, .preloadAllAnimationData): return true
     case (.requestModifier, .requestModifier): return true
@@ -298,15 +306,14 @@ public extension Collection where Iterator.Element == KingfisherOptionsInfoItem
     public var preloadAllAnimationData: Bool {
         return contains { $0 <== .preloadAllAnimationData }
     }
-    
-    /// The queue of callbacks should happen from Kingfisher.
-    public var callbackDispatchQueue: DispatchQueue {
-        if let item = lastMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)),
-            case .callbackDispatchQueue(let queue) = item
+
+    public var callbackQueue: CallbackQueue {
+        if let item = lastMatchIgnoringAssociatedValue(.callbackQueue(.untouch)),
+            case .callbackQueue(let queue) = item
         {
-            return queue ?? DispatchQueue.main
+            return queue
         }
-        return DispatchQueue.main
+        return .mainCurrentOrAsync
     }
     
     /// The scale factor which should be used for the image.

+ 12 - 5
Sources/Image/Image.swift

@@ -89,7 +89,8 @@ extension KingfisherClass where Base: Image {
         set { setRetainedAssociatedObject(base, &imageSourceKey, newValue) }
     }
     #endif
-    
+
+    // Bitmap memory cost with KB.
     var cost: Int {
         let pixel = Int(size.width * size.height * scale * scale)
         guard let cgImage = cgImage else {
@@ -116,7 +117,7 @@ extension KingfisherClass where Base: Image {
         return Image(cgImage: cgImage, scale: scale, orientation: refImage?.imageOrientation ?? .up)
     }
     
-    /// Normalized image of current `base`.
+    /// Returns normalized image for current `base` image.
     /// This method will try to redraw an image with orientation and scale considered.
     public var normalized: Image {
         // prevent animated image (GIF) lose it's images
@@ -134,7 +135,6 @@ extension KingfisherClass where Base: Image {
 // MARK: - Image Representation
 extension KingfisherClass where Base: Image {
     // MARK: - PNG
-    
     /// Returns PNG representation of `base` image.
     ///
     /// - Returns: PNG data of image.
@@ -151,7 +151,6 @@ extension KingfisherClass where Base: Image {
     }
     
     // MARK: - JPEG
-    
     /// Returns JPEG representation of `base` image.
     ///
     /// - Parameter compressionQuality: The compression quality when converting image to JPEG data.
@@ -169,7 +168,6 @@ extension KingfisherClass where Base: Image {
     }
     
     // MARK: - GIF
-    
     /// Returns GIF representation of `base` image.
     ///
     /// - Returns: Original GIF data of image.
@@ -239,6 +237,15 @@ extension KingfisherClass where Base: Image {
         #endif
     }
 
+    /// Creates an image from a given data and options. `.JPEG`, `.PNG` or `.GIF` is supported. For other
+    /// image format, image initilizer from system will be used. If no image object could be created from
+    /// the given `data`, `nil` will be returned.
+    ///
+    /// - Parameters:
+    ///   - data: The image data representation.
+    ///   - options: Options to use when creating the image.
+    /// - Returns: An `Image` object represents the image if created. If the `data` is invalid or not supported, `nil`
+    ///            will be returned.
     public static func image(data: Data, options: ImageCreatingOptions) -> Image? {
         var image: Image?
         switch data.kf.imageFormat {

+ 4 - 4
Sources/Networking/ImageDownloader.swift

@@ -201,15 +201,15 @@ open class ImageDownloader {
                     }
 
                     let imageResult = result.map { ImageDownloadResult(image: $0, url: url, originalData: data) }
-                    let queue = callback.options.callbackDispatchQueue
-                    queue.async { callback.onCompleted?.call(imageResult) }
+                    let queue = callback.options.callbackQueue
+                    queue.execute { callback.onCompleted?.call(imageResult) }
                 }
                 self.processQueue.async { prosessor.process() }
 
             case .failure(let error):
                 callbacks.forEach { callback in
-                    let queue = callback.options.callbackDispatchQueue
-                    queue.async { callback.onCompleted?.call(.failure(error)) }
+                    let queue = callback.options.callbackQueue
+                    queue.execute { callback.onCompleted?.call(.failure(error)) }
                 }
             }
         }

+ 2 - 2
Sources/Networking/ImagePrefetcher.swift

@@ -137,10 +137,10 @@ public class ImagePrefetcher {
         
         // We want all callbacks from our prefetch queue, so we should ignore the call back queue in options
         var optionsInfoWithoutQueue = (options ?? .empty)
-            .removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil))
+            .removeAllMatchesIgnoringAssociatedValue(.callbackQueue(.untouch))
         
         // Add our own callback dispatch queue to make sure all callbacks are coming back in our expected queue
-        optionsInfoWithoutQueue.append(.callbackDispatchQueue(prefetchQueue))
+        optionsInfoWithoutQueue.append(.callbackQueue(.dispatch(prefetchQueue)))
         
         optionsInfo = optionsInfoWithoutQueue
         

+ 12 - 3
Sources/Utility/CallbackQueue.swift

@@ -36,21 +36,30 @@ import Foundation
 public enum CallbackQueue {
     case mainAsync
     case mainCurrentOrAsync
-    case current
+    case untouch
     case dispatch(DispatchQueue)
     
-    func execute(_ block: @escaping () -> Void) {
+    public func execute(_ block: @escaping () -> Void) {
         switch self {
         case .mainAsync:
             DispatchQueue.main.async { block() }
         case .mainCurrentOrAsync:
             DispatchQueue.main.safeAsync { block() }
-        case .current:
+        case .untouch:
             block()
         case .dispatch(let queue):
             queue.async { block() }
         }
     }
+
+    var queue: DispatchQueue {
+        switch self {
+        case .mainAsync: return .main
+        case .mainCurrentOrAsync: return .main
+        case .untouch: return OperationQueue.current?.underlyingQueue ?? .main
+        case .dispatch(let queue): return queue
+        }
+    }
 }
 
 extension DispatchQueue {

+ 27 - 0
Tests/KingfisherTests/ImageExtensionTests.swift

@@ -47,6 +47,13 @@ class ImageExtensionTests: XCTestCase {
         XCTAssertEqual(format, .unknown)
     }
     
+    func testGenerateJPEGImage() {
+        let options = ImageCreatingOptions()
+        let image = KingfisherClass<Image>.image(data: testImageJEPGData, options: options)
+        XCTAssertNotNil(image)
+        XCTAssertTrue(image!.renderEqual(to: Image(data: testImageJEPGData)!))
+    }
+    
     func testGenerateGIFImage() {
         let options = ImageCreatingOptions()
         let image = KingfisherClass<Image>.animatedImage(data: testImageGIFData, options: options)
@@ -98,6 +105,13 @@ class ImageExtensionTests: XCTestCase {
 #endif
     }
     
+    func testGenerateFromNonImage() {
+        let data = "hello".data(using: .utf8)!
+        let options = ImageCreatingOptions()
+        let image = KingfisherClass<Image>.image(data: data, options: options)
+        XCTAssertNil(image)
+    }
+    
     func testPreloadAllAnimationData() {
         let preloadOptions = ImageCreatingOptions(preloadAll: true)
         let image = KingfisherClass<Image>.animatedImage(data: testImageSingleFrameGIFData, options: preloadOptions)!
@@ -257,6 +271,19 @@ class ImageExtensionTests: XCTestCase {
         XCTAssertEqual(decoded_2x.size, CGSize(width: 32, height: 32))
         XCTAssertEqual(decoded_2x.scale, 2.0)
         #endif
+    }
+    
+    func testNormalized() {
+        // Full loaded GIF image should not be normalized since it is a set of images.
+        let options = ImageCreatingOptions()
+        let gifImage = KingfisherClass<Image>.animatedImage(data: testImageGIFData, options: options)
+        
+        XCTAssertNotNil(gifImage)
+        XCTAssertEqual(gifImage!.kf.normalized, gifImage!)
         
+        // No need to normalize up orientation image.
+        let normalImage = testImage
+        XCTAssertEqual(normalImage.imageOrientation, .up)
+        XCTAssertEqual(normalImage, testImage)
     }
 }

+ 1 - 1
Tests/KingfisherTests/ImageViewExtensionTests.swift

@@ -98,7 +98,7 @@ class ImageViewExtensionTests: XCTestCase {
         let customQueue = DispatchQueue(label: "com.kingfisher.testQueue")
         imageView.kf.setImage(
             with: url,
-            options: [.callbackDispatchQueue(customQueue)],
+            options: [.callbackQueue(.dispatch(customQueue))],
             progressBlock: { _, _ in XCTAssertTrue(Thread.isMainThread) })
         {
             result in

+ 2 - 1
Tests/KingfisherTests/KingfisherManagerTests.swift

@@ -233,7 +233,8 @@ class KingfisherManagerTests: XCTestCase {
         stub(url, data: testImageData, length: 123)
 
         let customQueue = DispatchQueue(label: "com.kingfisher.testQueue")
-        manager.retrieveImage(with: url, options: [.callbackDispatchQueue(customQueue)], progressBlock: { _, _ in
+        let options: KingfisherOptionsInfo = [.callbackQueue(.dispatch(customQueue))]
+        manager.retrieveImage(with: url, options: options, progressBlock: { _, _ in
             XCTAssertTrue(Thread.isMainThread)
             progressExpectation.fulfill()
         })

+ 19 - 15
Tests/KingfisherTests/KingfisherOptionsInfoTests.swift

@@ -29,17 +29,7 @@ import XCTest
 @testable import Kingfisher
 
 class KingfisherOptionsInfoTests: XCTestCase {
-    
-    override func setUp() {
-        super.setUp()
-        // Put setup code here. This method is called before the invocation of each test method in the class.
-    }
-    
-    override func tearDown() {
-        // Put teardown code here. This method is called after the invocation of each test method in the class.
-        super.tearDown()
-    }
-    
+
     func testEmptyOptionsShouldParseCorrectly() {
         let options = KingfisherOptionsInfo.empty
         XCTAssertTrue(options.targetCache === nil)
@@ -57,7 +47,7 @@ class KingfisherOptionsInfoTests: XCTestCase {
         XCTAssertFalse(options.fromMemoryCacheOrRefresh)
         XCTAssertFalse(options.cacheMemoryOnly)
         XCTAssertFalse(options.backgroundDecode)
-        XCTAssertEqual(options.callbackDispatchQueue.label, DispatchQueue.main.label)
+        XCTAssertEqual(options.callbackQueue.queue.label, DispatchQueue.main.label)
         XCTAssertEqual(options.scaleFactor, 1.0)
         XCTAssertFalse(options.keepCurrentImageWhileLoading)
         XCTAssertFalse(options.onlyLoadFirstFrame)
@@ -77,27 +67,36 @@ class KingfisherOptionsInfoTests: XCTestCase {
         let queue = DispatchQueue.global(qos: .default)
         let testModifier = TestModifier()
         let processor = RoundCornerImageProcessor(cornerRadius: 20)
-        
+        let serializer = FormatIndicatedCacheSerializer.png
+        let modifier = DefaultImageModifier.default
+
         let options: KingfisherOptionsInfo = [
             .targetCache(cache),
             .downloader(downloader),
+            .originalCache(cache),
             .transition(transition),
             .downloadPriority(0.8),
             .forceRefresh,
+            .forceTransition,
             .fromMemoryCacheOrRefresh,
             .cacheMemoryOnly,
+            .waitForCache,
             .onlyFromCache,
             .backgroundDecode,
-            .callbackDispatchQueue(queue),
+            .callbackQueue(.dispatch(queue)),
             KingfisherOptionsInfoItem.scaleFactor(2.0),
+            .preloadAllAnimationData,
             .requestModifier(testModifier),
             .processor(processor),
+            .cacheSerializer(serializer),
+            .imageModifier(modifier),
             .keepCurrentImageWhileLoading,
             .onlyLoadFirstFrame,
             .cacheOriginalImage
         ]
         
         XCTAssertTrue(options.targetCache === cache)
+        XCTAssertTrue(options.originalCache === cache)
         XCTAssertTrue(options.downloader === downloader)
 
 #if !os(macOS)
@@ -110,14 +109,19 @@ class KingfisherOptionsInfoTests: XCTestCase {
         XCTAssertEqual(options.downloadPriority, 0.8)
         XCTAssertTrue(options.forceRefresh)
         XCTAssertTrue(options.fromMemoryCacheOrRefresh)
+        XCTAssertTrue(options.forceTransition)
         XCTAssertTrue(options.cacheMemoryOnly)
+        XCTAssertTrue(options.waitForCache)
         XCTAssertTrue(options.onlyFromCache)
         XCTAssertTrue(options.backgroundDecode)
         
-        XCTAssertEqual(options.callbackDispatchQueue.label, queue.label)
+        XCTAssertEqual(options.callbackQueue.queue.label, queue.label)
         XCTAssertEqual(options.scaleFactor, 2.0)
+        XCTAssertTrue(options.preloadAllAnimationData)
         XCTAssertTrue(options.modifier is TestModifier)
         XCTAssertEqual(options.processor.identifier, processor.identifier)
+        XCTAssertTrue(options.cacheSerializer is FormatIndicatedCacheSerializer)
+        XCTAssertTrue(options.imageModifier is DefaultImageModifier)
         XCTAssertTrue(options.keepCurrentImageWhileLoading)
         XCTAssertTrue(options.onlyLoadFirstFrame)
         XCTAssertTrue(options.cacheOriginalImage)