Kingfisher: Images from Firebase url are not being cached

Created on 16 Aug 2016  ·  18Comments  ·  Source: onevcat/Kingfisher

Hello! I have a little problem.. For some reason images from Firebase url are not cached. What should be the problem? I tried with your example url, they were cached but like this they are not cached:

let productImageref = productsValue[indexPath.row]["Products"] as? String

    cell.snusProductImageView.image = nil
    cell.snusProductImageView.kf_showIndicatorWhenLoading = true



    FIRStorage.storage().reference().child("\(productImageref!).png").downloadURLWithCompletion({(url, error)in

        if error != nil{
            print(error)
            return
        }else{

            cell.snusProductImageView.kf_setImageWithURL(url, placeholderImage: nil,
                optionsInfo: [.Transition(ImageTransition.Fade(1))],
                progressBlock: { receivedSize, totalSize in
                    print("\(indexPath.row + 1): \(receivedSize)/\(totalSize)")
                    print(url)
                },
                completionHandler: { image, error, cacheType, imageURL in
                    print("\(indexPath.row + 1): Finished")
                    print(url)
            })
        }
    })

Can you say why it is like this?

Most helpful comment

@MaeseppTarvo Hi, I just tried your demo.

It is not related to the cache control settings in the response, Kingfisher has its own cache layer so it will be simply ignored. The problem is Kingfisher is using the full url for cache key by default, so you need to have the same url for that url or the cache could not be retrieved correctly. In your app, the image url is appended by a varying token every time, so Kingfisher sees different url and since that url is not in cache, Kingfisher will download it again.

Instead of using the url as the key, you could choose to create a resource object and use your product id (or something could identify your image) as the cache key. Then, load the resource instead of plain url, to make Kingfisher search/cache for that key.

Some code below based on your current implementation, hope it could help:

let productImageref = productsValue[indexPath.row]["Products"] as? String
FIRStorage.storage().reference().child("\(productImageref!).png").downloadURLWithCompletion({(url, error)in
            guard let url = url else {
                return
            }
            let resource = Resource(downloadURL: url, cacheKey: productImageref)
            cell.snusProductImageView.kf_setImageWithResource(resource)
        })

All 18 comments

Not sure what happened. Can you build a simple demo project to show it, so I can dig into it deeper.

Yeah sure. Check this out, thats the project I am working with: https://github.com/MaeseppTarvo/SnusPedia

I think there is something with the Cache-Control: max-age

@MaeseppTarvo Hi, I just tried your demo.

It is not related to the cache control settings in the response, Kingfisher has its own cache layer so it will be simply ignored. The problem is Kingfisher is using the full url for cache key by default, so you need to have the same url for that url or the cache could not be retrieved correctly. In your app, the image url is appended by a varying token every time, so Kingfisher sees different url and since that url is not in cache, Kingfisher will download it again.

Instead of using the url as the key, you could choose to create a resource object and use your product id (or something could identify your image) as the cache key. Then, load the resource instead of plain url, to make Kingfisher search/cache for that key.

Some code below based on your current implementation, hope it could help:

let productImageref = productsValue[indexPath.row]["Products"] as? String
FIRStorage.storage().reference().child("\(productImageref!).png").downloadURLWithCompletion({(url, error)in
            guard let url = url else {
                return
            }
            let resource = Resource(downloadURL: url, cacheKey: productImageref)
            cell.snusProductImageView.kf_setImageWithResource(resource)
        })

@onevcat I tried that but it still doesn't cache them :( I think the problem is Firebase not this library. But I think it should work since many people nowadays uses the Firebase and its Storage but there is no other way to get the url from Firebase according to their documentation.

@MaeseppTarvo The above code works well for me. I could see these log, which indicates the image are from disk:

cell.snusProductImageView.kf_setImageWithResource(resource, placeholderImage: nil,
                optionsInfo: [.Transition(ImageTransition.Fade(1))],
                progressBlock: { receivedSize, totalSize in
                    print("\(indexPath.row + 1): \(receivedSize)/\(totalSize)")
                },
                completionHandler: { image, error, cacheType, imageURL in
                    print("\(indexPath.row + 1): Finished, \(cacheType)")
            })
3: Finished, Disk
1: Finished, Disk
2: Finished, Disk

@onevcat First it shows the Disk but after scrolling shows Memory:

let productImageref = productsValue[indexPath.row]["Products"] as? String

        cell.snusProductImageView.image = nil
        cell.snusProductImageView.kf_showIndicatorWhenLoading = true

        FIRStorage.storage().reference().child("\(productImageref!).png").downloadURLWithCompletion({(url, error)in
            guard let url = url else {
                return
            }
            let resource = Resource(downloadURL: url, cacheKey: productImageref)
            cell.snusProductImageView.kf_setImageWithResource(resource, placeholderImage: nil,
                optionsInfo: [.Transition(ImageTransition.Fade(1))],
                progressBlock: { receivedSize, totalSize in
                    print("\(indexPath.row + 1): \(receivedSize)/\(totalSize)")
                },
                completionHandler: { image, error, cacheType, imageURL in
                    print("\(indexPath.row + 1): Finished, \(cacheType)")
            })
        })
3: Finished, Disk
1: Finished, Disk
2: Finished, Disk
4: Finished, Disk
7: Finished, Disk
6: Finished, Disk
5: Finished, Disk
3: Finished, Memory
2: Finished, Memory
4: Finished, Memory

Try turning off internet.. It doesn't take them from the disk for some reason.

That is the quote that Firebase guy Mike McDonald told me: "The Github one has Cache-Control: max-age=300 while Firebase Storage doesn't have cache control set by default (you can set it when you upload the file, or change it by updating metadata), so I assume that's why KingFisher isn't caching it."

It is the correct behavior. Kingfisher has two layers of cache: disk and memory. The images loaded from disk will be kept in memory for later use and better performance. And once you switch you app to background or receive a memory warning, the memory cache will be swiped.

If this is not what you want, you could set the maxMemoryCost of ImageCache.defaultCache to a small number (like 1) to disable the memory cache.

I just want it not to load and use internet on every time I scroll or come back into the app. So I thought that the disk cache could solve it. I tried to change the macMemoryCost = 1 but it didn't change anything.

There is no internet request when the image come from either memory or disk. :)

Xcode debugger shows that there is :D
Have a look at this image.

But with the example app and the url those both are zero.

It might be the firebase request I guess?

The loading time is because of the downloadURLWithCompletion of FIRStorage.storage. As I understand, it will send request to Firebase and get the image url. It will take some time and Kingfisher could only begin its work after that.

I suggest to use some network inspector like Charles to see what happened indeed.

It's usually because you are not handling cell reusing correctly.

Since it's not an issue of Kingfisher's cache, I am closing it.

I realize this is closed, but to help @MaeseppTarvo and any others, this definitely appears to be an issue with Firebase's caching headers coming from their storage platform. I'm coming from Android, but had a similar issue with an image loading library not caching images, but upon further inspection of the headers realized for some reason that the max age of the file was 0. Trying an image hosted on Github (drag a file into an issue and copy the address) worked perfectly with the cache. Not sure why those default headers are there... =\

I know this is closed, but as @lustigdev said, you can and may want to set Cache-Control metadata when uploading images to Firebase Storage. The default setting (as 2018 March) is Private, max-age=0, thus no cache.

Search for metadata in the following official docs for more information how to upload files with customized metadata.

Here's a the list of metadata properties that you can set

Bro i am facing the same problem. Some images are cached and some are not. The images with extension .jpeg or .jpg are cached without any problem but when we are trying to cached image with extension .png they are not caching successfully. @onevcat @MaeseppTarvo

I have tried cache control but it still not working. Has anyone found a workable solution?

Was this page helpful?
0 / 5 - 0 ratings