Explorar el Código

Add .onFailureImage(Image) option to set default image when loading request will be failed

Dominique Stranz hace 7 años
padre
commit
81c59f5ffd

+ 3 - 0
Sources/Extensions/ImageView+Kingfisher.swift

@@ -134,6 +134,9 @@ extension KingfisherClass where Base: ImageView {
                         )
                         #endif
                     case .failure:
+                        if let image = options.onFailureImage {
+                            self.base.image = image
+                        }
                         completionHandler?(result)
                     }
                 }

+ 6 - 0
Sources/Extensions/NSButton+Kingfisher.swift

@@ -88,6 +88,9 @@ extension KingfisherClass where Base: NSButton {
                         self.base.image = value.image
                         completionHandler?(result)
                     case .failure:
+                        if let image = options.onFailureImage {
+                            self.base.image = image
+                        }
                         completionHandler?(result)
                     }
                 }
@@ -165,6 +168,9 @@ extension KingfisherClass where Base: NSButton {
                         self.base.alternateImage = value.image
                         completionHandler?(result)
                     case .failure:
+                        if let image = options.onFailureImage {
+                            self.base.alternateImage = image
+                        }
                         completionHandler?(result)
                     }
                 }

+ 6 - 0
Sources/Extensions/UIButton+Kingfisher.swift

@@ -94,6 +94,9 @@ extension KingfisherClass where Base: UIButton {
                         self.base.setImage(value.image, for: state)
                         completionHandler?(result)
                     case .failure:
+                        if let image = options.onFailureImage {
+                            self.base.setImage(image, for: state)
+                        }
                         completionHandler?(result)
                     }
                 }
@@ -178,6 +181,9 @@ extension KingfisherClass where Base: UIButton {
                         self.base.setBackgroundImage(value.image, for: state)
                         completionHandler?(result)
                     case .failure:
+                        if let image = options.onFailureImage {
+                            self.base.setBackgroundImage(image, for: state)
+                        }
                         completionHandler?(result)
                     }
                 }

+ 3 - 0
Sources/Extensions/WKInterfaceImage+Kingfisher.swift

@@ -84,6 +84,9 @@ extension KingfisherClass where Base: WKInterfaceImage {
                         self.base.setImage(value.image)
                         completionHandler?(result)
                     case .failure:
+                        if let image = options.onFailureImage {
+                            self.base.setImage(image)
+                        }
                         completionHandler?(result)
                     }
                 }

+ 16 - 0
Sources/General/KingfisherOptionsInfo.swift

@@ -176,6 +176,11 @@ public enum KingfisherOptionsInfoItem {
     /// resource, instead of downloading it again. You can use `.originalCache` to specify a cache or the original
     /// images if neccessary.
     case cacheOriginalImage
+    
+    /// If set and a downloading error occurred Kingfisher will set provided image
+    /// in place of requested one. It's useful when you don't want to show placeholder
+    /// during loading time but wants to use some default image when requests will be failed.
+    case onFailureImage(Image)
 }
 
 precedencegroup ItemComparisonPrecedence {
@@ -212,6 +217,7 @@ func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Boo
     case (.keepCurrentImageWhileLoading, .keepCurrentImageWhileLoading): return true
     case (.onlyLoadFirstFrame, .onlyLoadFirstFrame): return true
     case (.cacheOriginalImage, .cacheOriginalImage): return true
+    case (.onFailureImage(_), .onFailureImage(_)): return true
     default: return false
     }
 }
@@ -395,4 +401,14 @@ public extension Collection where Iterator.Element == KingfisherOptionsInfoItem
     public var cacheOriginalImage: Bool {
         return contains { $0 <== .cacheOriginalImage }
     }
+    
+    public var onFailureImage: Image? {
+        if let item = lastMatchIgnoringAssociatedValue(.onFailureImage(Image())),
+            case .onFailureImage(let image) = item
+        {
+            return image
+        }
+        
+        return nil
+    }
 }

+ 14 - 0
Tests/KingfisherTests/ImageViewExtensionTests.swift

@@ -573,6 +573,20 @@ class ImageViewExtensionTests: XCTestCase {
 
         waitForExpectations(timeout: 1, handler: nil)
     }
+    
+    func testSettingNonWorkingImageWithFailureImage() {
+        let expectation = self.expectation(description: "wait for downloading image")
+        let url = testURLs[0]
+        stub(url, errorCode: 404)
+
+        imageView.kf.setImage(with: url, options: [.onFailureImage(testImage)]) { (result) -> Void in
+            XCTAssertNil(result.value)
+            expectation.fulfill()
+        }
+        XCTAssertNil(imageView.image)
+        waitForExpectations(timeout: 5, handler: nil)
+        XCTAssertEqual(testImage, imageView.image)
+    }
 }
 
 extension View: Placeholder {}

+ 30 - 0
Tests/KingfisherTests/NSButtonExtensionTests.swift

@@ -158,5 +158,35 @@ class NSButtonExtensionTests: XCTestCase {
         
         waitForExpectations(timeout: 1, handler: nil)
     }
+    
+    func testSettingNonWorkingImageWithFailureImage() {
+        let expectation = self.expectation(description: "wait for downloading image")
+        let url = testURLs[0]
+        stub(url, errorCode: 404)
+        
+        button.kf.setImage(with: url, options: [.onFailureImage(testImage)]) { (result) -> Void in
+            XCTAssertNil(result.value)
+            expectation.fulfill()
+        }
+        
+        XCTAssertNil(button.image)
+        waitForExpectations(timeout: 5, handler: nil)
+        XCTAssertEqual(testImage, button.image)
+    }
+    
+    func testSettingNonWorkingAlternateImageWithFailureImage() {
+        let expectation = self.expectation(description: "wait for downloading image")
+        let url = testURLs[0]
+        stub(url, errorCode: 404)
+        
+        button.kf.setAlternateImage(with: url, options: [.onFailureImage(testImage)]) { (result) -> Void in
+            XCTAssertNil(result.value)
+            expectation.fulfill()
+        }
+        
+        XCTAssertNil(button.alternateImage)
+        waitForExpectations(timeout: 5, handler: nil)
+        XCTAssertEqual(testImage, button.alternateImage)
+    }
 
 }

+ 30 - 0
Tests/KingfisherTests/UIButtonExtensionTests.swift

@@ -161,4 +161,34 @@ class UIButtonExtensionTests: XCTestCase {
         
         waitForExpectations(timeout: 1, handler: nil)
     }
+    
+    func testSettingNonWorkingImageWithFailureImage() {
+        let expectation = self.expectation(description: "wait for downloading image")
+        let url = testURLs[0]
+        stub(url, errorCode: 404)
+        let state = UIControl.State()
+        
+        button.kf.setImage(with: url, for: state, options: [.onFailureImage(testImage)]) { (result) -> Void in
+            XCTAssertNil(result.value)
+            expectation.fulfill()
+        }
+        XCTAssertNil(button.image(for: state))
+        waitForExpectations(timeout: 5, handler: nil)
+        XCTAssertEqual(testImage, button.image(for: state))
+    }
+    
+    func testSettingNonWorkingBackgroundImageWithFailureImage() {
+        let expectation = self.expectation(description: "wait for downloading image")
+        let url = testURLs[0]
+        stub(url, errorCode: 404)
+        let state = UIControl.State()
+        
+        button.kf.setBackgroundImage(with: url, for: state, options: [.onFailureImage(testImage)]) { (result) -> Void in
+            XCTAssertNil(result.value)
+            expectation.fulfill()
+        }
+        XCTAssertNil(button.backgroundImage(for: state))
+        waitForExpectations(timeout: 5, handler: nil)
+        XCTAssertEqual(testImage, button.backgroundImage(for: state))
+    }
 }