Skip to content

Commit 3f2f135

Browse files
authored
Merge pull request #109 from SDWebImage/feature_support_data_arg_2
Add the same overload method for onSuccess API, which introduce the image data arg. Keep the source code compatibility
2 parents 8a347dc + f36440f commit 3f2f135

File tree

6 files changed

+109
-19
lines changed

6 files changed

+109
-19
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,9 @@ github "SDWebImage/SDWebImageSwiftUI"
111111
var body: some View {
112112
WebImage(url: URL(string: "https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic"))
113113
// Supports options and context, like `.delayPlaceholder` to show placeholder only when error
114-
.onSuccess { image, cacheType in
114+
.onSuccess { image, data, cacheType in
115115
// Success
116+
// Note: Data exist only when queried from disk cache or network. Use `.queryMemoryData` if you really need data
116117
}
117118
.resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size
118119
.placeholder(Image(systemName: "photo")) // Placeholder Image

SDWebImageSwiftUI/Classes/AnimatedImage.swift

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ final class AnimatedLoadingModel : ObservableObject, IndicatorReportable {
4949
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
5050
final class AnimatedImageHandler: ObservableObject {
5151
// Completion Handler
52-
@Published var successBlock: ((PlatformImage, SDImageCacheType) -> Void)?
52+
@Published var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)?
5353
@Published var failureBlock: ((Error) -> Void)?
5454
@Published var progressBlock: ((Int, Int) -> Void)?
5555
// Coordinator Handler
@@ -208,12 +208,15 @@ public struct AnimatedImage : PlatformViewRepresentable {
208208
return
209209
}
210210
self.imageLoading.isLoading = true
211-
if imageModel.webOptions.contains(.delayPlaceholder) {
211+
let options = imageModel.webOptions
212+
if options.contains(.delayPlaceholder) {
212213
self.imageConfiguration.placeholderView?.isHidden = true
213214
} else {
214215
self.imageConfiguration.placeholderView?.isHidden = false
215216
}
216-
view.wrapped.sd_setImage(with: imageModel.url, placeholderImage: imageConfiguration.placeholder, options: imageModel.webOptions, context: imageModel.webContext, progress: { (receivedSize, expectedSize, _) in
217+
var context = imageModel.webContext ?? [:]
218+
context[.animatedImageClass] = SDAnimatedImage.self
219+
view.wrapped.sd_internalSetImage(with: imageModel.url, placeholderImage: imageConfiguration.placeholder, options: options, context: context, setImageBlock: nil, progress: { (receivedSize, expectedSize, _) in
217220
let progress: Double
218221
if (expectedSize > 0) {
219222
progress = Double(receivedSize) / Double(expectedSize)
@@ -224,7 +227,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
224227
self.imageLoading.progress = progress
225228
}
226229
self.imageHandler.progressBlock?(receivedSize, expectedSize)
227-
}) { (image, error, cacheType, _) in
230+
}) { (image, data, error, cacheType, finished, _) in
228231
// This is a hack because of Xcode 11.3 bug, the @Published does not trigger another `updateUIView` call
229232
// Here I have to use UIKit/AppKit API to triger the same effect (the window change implicitly cause re-render)
230233
if let hostingView = AnimatedImage.findHostingView(from: view) {
@@ -241,7 +244,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
241244
self.imageLoading.progress = 1
242245
if let image = image {
243246
self.imageConfiguration.placeholderView?.isHidden = true
244-
self.imageHandler.successBlock?(image, cacheType)
247+
self.imageHandler.successBlock?(image, data, cacheType)
245248
} else {
246249
self.imageConfiguration.placeholderView?.isHidden = false
247250
self.imageHandler.failureBlock?(error ?? NSError())
@@ -702,11 +705,33 @@ extension AnimatedImage {
702705
return self
703706
}
704707

708+
/// Provide the action when image load successes.
709+
/// - Parameters:
710+
/// - action: The action to perform. The first arg is the loaded image. If `action` is `nil`, the call has no effect.
711+
/// - Returns: A view that triggers `action` when this image load successes.
712+
public func onSuccess(perform action: @escaping (PlatformImage) -> Void) -> AnimatedImage {
713+
self.imageHandler.successBlock = { image, _, _ in
714+
action(image)
715+
}
716+
return self
717+
}
718+
705719
/// Provide the action when image load successes.
706720
/// - Parameters:
707721
/// - action: The action to perform. The first arg is the loaded image, the second arg is the cache type loaded from. If `action` is `nil`, the call has no effect.
708722
/// - Returns: A view that triggers `action` when this image load successes.
709-
public func onSuccess(perform action: ((PlatformImage, SDImageCacheType) -> Void)? = nil) -> AnimatedImage {
723+
public func onSuccess(perform action: @escaping (PlatformImage, SDImageCacheType) -> Void) -> AnimatedImage {
724+
self.imageHandler.successBlock = { image, _, cacheType in
725+
action(image, cacheType)
726+
}
727+
return self
728+
}
729+
730+
/// Provide the action when image load successes.
731+
/// - Parameters:
732+
/// - action: The action to perform. The first arg is the loaded image, the second arg is the loaded image data, the third arg is the cache type loaded from. If `action` is `nil`, the call has no effect.
733+
/// - Returns: A view that triggers `action` when this image load successes.
734+
public func onSuccess(perform action: ((PlatformImage, Data?, SDImageCacheType) -> Void)? = nil) -> AnimatedImage {
710735
self.imageHandler.successBlock = action
711736
return self
712737
}

SDWebImageSwiftUI/Classes/ImageManager.swift

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public final class ImageManager : ObservableObject {
1717
@Published public var image: PlatformImage?
1818
/// loaded image data, may be nil if hit from memory cache. This will only published once even on incremental image loading
1919
@Published public var imageData: Data?
20+
/// loaded image cache type, .none means from network
21+
@Published public var cacheType: SDImageCacheType = .none
2022
/// loading error, you can grab the error code and reason listed in `SDWebImageErrorDomain`, to provide a user interface about the error reason
2123
@Published public var error: Error?
2224
/// whether network is loading or cache is querying, should only be used for indicator binding
@@ -33,7 +35,7 @@ public final class ImageManager : ObservableObject {
3335
var url: URL?
3436
var options: SDWebImageOptions
3537
var context: [SDWebImageContextOption : Any]?
36-
var successBlock: ((PlatformImage, SDImageCacheType) -> Void)?
38+
var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)?
3739
var failureBlock: ((Error) -> Void)?
3840
var progressBlock: ((Int, Int) -> Void)?
3941

@@ -89,10 +91,11 @@ public final class ImageManager : ObservableObject {
8991
self.isIncremental = !finished
9092
if finished {
9193
self.imageData = data
94+
self.cacheType = cacheType
9295
self.isLoading = false
9396
self.progress = 1
9497
if let image = image {
95-
self.successBlock?(image, cacheType)
98+
self.successBlock?(image, data, cacheType)
9699
} else {
97100
self.failureBlock?(error ?? NSError())
98101
}
@@ -120,10 +123,28 @@ extension ImageManager {
120123
self.failureBlock = action
121124
}
122125

126+
/// Provide the action when image load successes.
127+
/// - Parameters:
128+
/// - action: The action to perform. The first arg is the loaded image. If `action` is `nil`, the call has no effect.
129+
public func setOnSuccess(perform action: @escaping (PlatformImage) -> Void) {
130+
self.successBlock = { image, _, _ in
131+
action(image)
132+
}
133+
}
134+
123135
/// Provide the action when image load successes.
124136
/// - Parameters:
125137
/// - action: The action to perform. The first arg is the loaded image, the second arg is the cache type loaded from. If `action` is `nil`, the call has no effect.
126-
public func setOnSuccess(perform action: ((PlatformImage, SDImageCacheType) -> Void)? = nil) {
138+
public func setOnSuccess(perform action: @escaping (PlatformImage, SDImageCacheType) -> Void) {
139+
self.successBlock = { image, _, cacheType in
140+
action(image, cacheType)
141+
}
142+
}
143+
144+
/// Provide the action when image load successes.
145+
/// - Parameters:
146+
/// - action: The action to perform. The first arg is the loaded image, the second arg is the loaded image data, the third arg is the cache type loaded from. If `action` is `nil`, the call has no effect.
147+
public func setOnSuccess(perform action: ((PlatformImage, Data?, SDImageCacheType) -> Void)? = nil) {
127148
self.successBlock = action
128149
}
129150

SDWebImageSwiftUI/Classes/WebImage.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,34 @@ extension WebImage {
264264
return self
265265
}
266266

267+
/// Provide the action when image load successes.
268+
/// - Parameters:
269+
/// - action: The action to perform. The first arg is the loaded image. If `action` is `nil`, the call has no effect.
270+
/// - Returns: A view that triggers `action` when this image load successes.
271+
public func onSuccess(perform action: @escaping (PlatformImage) -> Void) -> WebImage {
272+
let action = action
273+
self.imageManager.successBlock = { image, _, _ in
274+
action(image)
275+
}
276+
return self
277+
}
278+
267279
/// Provide the action when image load successes.
268280
/// - Parameters:
269281
/// - action: The action to perform. The first arg is the loaded image, the second arg is the cache type loaded from. If `action` is `nil`, the call has no effect.
270282
/// - Returns: A view that triggers `action` when this image load successes.
271-
public func onSuccess(perform action: ((PlatformImage, SDImageCacheType) -> Void)? = nil) -> WebImage {
283+
public func onSuccess(perform action: @escaping (PlatformImage, SDImageCacheType) -> Void) -> WebImage {
284+
self.imageManager.successBlock = { image, _, cacheType in
285+
action(image, cacheType)
286+
}
287+
return self
288+
}
289+
290+
/// Provide the action when image load successes.
291+
/// - Parameters:
292+
/// - action: The action to perform. The first arg is the loaded image, the second arg is the loaded image data, the third arg is the cache type loaded from. If `action` is `nil`, the call has no effect.
293+
/// - Returns: A view that triggers `action` when this image load successes.
294+
public func onSuccess(perform action: ((PlatformImage, Data?, SDImageCacheType) -> Void)? = nil) -> WebImage {
272295
self.imageManager.successBlock = action
273296
return self
274297
}

Tests/AnimatedImageTests.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ class AnimatedImageTests: XCTestCase {
7474
let expectation = self.expectation(description: "AnimatedImage url initializer")
7575
let imageUrl = URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif")
7676
let imageView = AnimatedImage(url: imageUrl)
77-
.onSuccess { image, cacheType in
77+
.onSuccess { image, data, cacheType in
78+
XCTAssertNotNil(image)
7879
if let animatedImage = image as? SDAnimatedImage {
7980
XCTAssertEqual(animatedImage.animatedImageLoopCount, 0)
8081
XCTAssertEqual(animatedImage.animatedImageFrameCount, 389)
@@ -88,7 +89,7 @@ class AnimatedImageTests: XCTestCase {
8889
ViewHosting.host(view: imageView)
8990
let animatedImageView = try imageView.inspect().actualView().platformView().wrapped
9091
XCTAssertEqual(animatedImageView.sd_imageURL, imageUrl)
91-
self.waitForExpectations(timeout: 5, handler: nil)
92+
self.waitForExpectations(timeout: 10, handler: nil)
9293
ViewHosting.expel()
9394
}
9495

Tests/WebImageTests.swift

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class WebImageTests: XCTestCase {
2121
let expectation = self.expectation(description: "WebImage static url initializer")
2222
let imageUrl = URL(string: "https://nr-platform.s3.amazonaws.com/uploads/platform/published_extension/branding_icon/275/AmazonS3.png")
2323
let imageView = WebImage(url: imageUrl)
24-
let introspectView = imageView.onSuccess { image, cacheType in
24+
let introspectView = imageView.onSuccess { image, data, cacheType in
2525
#if os(iOS) || os(tvOS)
2626
let displayImage = try? imageView.inspect().group().image(0).uiImage()
2727
#else
@@ -43,7 +43,7 @@ class WebImageTests: XCTestCase {
4343
let imageUrl = URL(string: "https://apng.onevcat.com/assets/elephant.png")
4444
let binding = Binding<Bool>(wrappedValue: true)
4545
let imageView = WebImage(url: imageUrl, isAnimating: binding)
46-
let introspectView = imageView.onSuccess { image, cacheType in
46+
let introspectView = imageView.onSuccess { image, data, cacheType in
4747
if let animatedImage = image as? SDAnimatedImage {
4848
XCTAssertTrue(imageView.isAnimating)
4949
#if os(iOS) || os(tvOS)
@@ -73,7 +73,7 @@ class WebImageTests: XCTestCase {
7373
let imageUrl = URL(string: "https://raw.githubusercontent.com/ibireme/YYImage/master/Demo/YYImageDemo/mew_baseline.jpg")
7474
let imageView = WebImage(url: imageUrl, options: [.progressiveLoad], context: [.imageScaleFactor: 1])
7575
let introspectView = imageView
76-
.onSuccess { _, _ in
76+
.onSuccess { _, _, _ in
7777
expectation.fulfill()
7878
}
7979
.onFailure { _ in
@@ -111,7 +111,7 @@ class WebImageTests: XCTestCase {
111111
}
112112

113113
func testWebImageOnSuccessWhenMemoryCacheHit() throws {
114-
let expectation = self.expectation(description: "WebImage onSuccess when memory hit")
114+
let expectation = self.expectation(description: "WebImage onSuccess when memory cache hit")
115115
let imageUrl = URL(string: "https://foo.bar/buzz.png")
116116
let cacheKey = SDWebImageManager.shared.cacheKey(for: imageUrl)
117117
#if os(macOS)
@@ -121,7 +121,7 @@ class WebImageTests: XCTestCase {
121121
#endif
122122
SDImageCache.shared.storeImage(toMemory: testImage, forKey: cacheKey)
123123
let imageView = WebImage(url: imageUrl)
124-
let introspectView = imageView.onSuccess { image, cacheType in
124+
let introspectView = imageView.onSuccess { image, data, cacheType in
125125
XCTAssert(cacheType == .memory)
126126
XCTAssertNotNil(image)
127127
XCTAssertEqual(image, testImage)
@@ -133,12 +133,31 @@ class WebImageTests: XCTestCase {
133133
ViewHosting.expel()
134134
}
135135

136+
func testWebImageOnSuccessWhenCacheMiss() throws {
137+
let expectation = self.expectation(description: "WebImage onSuccess when cache miss")
138+
let imageUrl = URL(string: "http://via.placeholder.com/100x100.png")
139+
let cacheKey = SDWebImageManager.shared.cacheKey(for: imageUrl)
140+
SDImageCache.shared.removeImageFromMemory(forKey: cacheKey)
141+
SDImageCache.shared.removeImageFromDisk(forKey: cacheKey)
142+
let imageView = WebImage(url: imageUrl)
143+
let introspectView = imageView.onSuccess { image, data, cacheType in
144+
XCTAssert(cacheType == .none)
145+
XCTAssertNotNil(image)
146+
XCTAssertNotNil(data)
147+
expectation.fulfill()
148+
}
149+
_ = try introspectView.inspect()
150+
ViewHosting.host(view: introspectView)
151+
self.waitForExpectations(timeout: 5, handler: nil)
152+
ViewHosting.expel()
153+
}
154+
136155
func testWebImageEXIFImage() throws {
137156
let expectation = self.expectation(description: "WebImage EXIF image url")
138157
// EXIF 5, Left Mirrored
139158
let imageUrl = URL(string: "https://raw.githubusercontent.com/recurser/exif-orientation-examples/master/Landscape_5.jpg")
140159
let imageView = WebImage(url: imageUrl)
141-
let introspectView = imageView.onSuccess { image, cacheType in
160+
let introspectView = imageView.onSuccess { image, data, cacheType in
142161
let displayImage = try? imageView.inspect().group().image(0).cgImage()
143162
let orientation = try! imageView.inspect().group().image(0).orientation()
144163
XCTAssertNotNil(displayImage)

0 commit comments

Comments
 (0)