onevcat / Kingfisher

A lightweight, pure-Swift library for downloading and caching images from the web.
MIT License
23.41k stars 2.66k forks source link

Fade transition still occurs when cache is used with downsampling enabled #2226

Open bohdany-cricut opened 7 months ago

bohdany-cricut commented 7 months ago

Check List

Issue Description

What

The documentation for the fade transition says that

The transition will not happen when the image is retrieved from either memory or disk cache by default

But in one particular case it seems to still being applied - when it's used with downsampling. I noticed that my snapshot tests started to fail after I added downsampling to the KFImage I use. Upon closer look, it turned out to be that images now don't appear instantly but with a slight delay and a fade transition animation. Here's the image setup:

KFImage
  .url(url)
  .downsampling(size: customSize)
  .fade(duration: 0.2)

And here is the cache setup:

func setup() {
    let cache = ImageCache(name: name)

    imagesToCache.forEach { (image, url) in
        let data = image.loadFromResources(.png)
        cache.store(.init(data: data)!, forKey: url, options: .init(nil))
    }

    KingfisherManager.shared.defaultOptions = [
        .onlyFromCache,
        .targetCache(cache),
    ]
}

Reproduce

Use KFImage with fade and downsampling applied along with cache being set up. Upon retrieval images from the cache fade transition is still being applied.

Other Comment

I did try using .cacheOriginalImage but it did not make any difference.

CraigSiemens commented 3 months ago

I just ran into this as well.

It appears to be caused in KingfisherManager.retrieveImageFromCache

It fails to find the processed image since kingfisher appends the processor identifier to the cache key when saving and restoring.

This is probably expected behaviour since the final image wasn't found in the cache so retrieving it could take long enough that the fade is needed.


My current workaround is to make a MockImageCache for use in the tests, which always discards the options when retrieving an image since the options are used to modify the cache key.

The one downside with this approach is it'll bypass running any processor on the image, so your snapshots wont accurately test whether the correct processor is being used.

private final class MockImageCache: ImageCache {
    override func imageCachedType(
        forKey key: String,
        processorIdentifier identifier: String = DefaultImageProcessor.default.identifier
    ) -> CacheType {
        .memory
    }

    override func retrieveImage(
        forKey key: String,
        options: KingfisherParsedOptionsInfo,
        callbackQueue: CallbackQueue = .mainCurrentOrAsync,
        completionHandler: ((Result<ImageCacheResult, KingfisherError>) -> Void)?
    ) {
        var options = options
        options.processor = DefaultImageProcessor.default

        super.retrieveImage(
            forKey: key,
            options: options,
            callbackQueue: callbackQueue,
            completionHandler: completionHandler
        )
    }
}