AlamofireImage cache? - ios

I use AlamofireImage in conjunction with PromiseKit and Alamofire. I use promises to chain the download of x number of images since I want to do this synchronically. I can't seem to understand if ImageDownloader() caches automatically or if I have to add the image explicitly to the cache? The only examples I've seen so far are not using the ImageDownloader so I have a real hard time finding an answer to this.
If not - how do I add it the cache? I've tried using:
self.imageDownloader.imageCache?.addImage(image, withIdentifier: imageUrl)
But all it does is increase my memory usage for all eternity(i.e. adding the same image to the cache over and over)

I think that defining an AutoPurgingImageCache() and then using it while caching process need to solve your memory usage problem.
let photoCache = AutoPurgingImageCache(
memoryCapacity: 100 * 1024 * 1024,
preferredMemoryUsageAfterPurge: 60 * 1024 * 1024
)
You can change memoryCapacity, preferredMemoryUsageAfterPurge represent MB. To add any image in your cache, you can use like that:
photoCache.addImage(scaledImage, withIdentifier: urlString)
Also you can check this topic for more details and AlamofireImage GitHub page.

I was also using the same approach you did here. But according to the API reference the image should be added to cache automatically:
Each downloaded image is cached in the underlying NSURLCache as well as the in-memory image cache that supports image filters.
https://alamofire.github.io/AlamofireImage/Classes/ImageDownloader.html

Related

How to set image using Kingfisher when image url did not change but image changed

How to retrieve correct image using Kingfisher when image url did not change but image changed such as Twitter or GitHub.
SDWebImage has a option [SDWebImageRefreshCached] to solve the problem.
/**
* Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed.
* The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation.
* This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.
* If a cached image is refreshed, the completion block is called once with the cached image and again with the final image.
*
* Use this flag only if you can't make your URLs static with embedded cache busting parameter.
*/
SDWebImageRefreshCached = 1 << 4,
How to solve the problem in Kingfisher?
Kingfisher has forceRefresh option to force downloading image again skiping cache
imageView.kf.setImage(with: url, options: [.forceRefresh])
refer this github thread for more details

How to cache images only in disk using Kingfisher?

I am using Kingfisher library for downloading and caching images. I am facing some issues in the implementation:
Are the images cached in both memory and disk?
Is there any provision to cache images only on disk?
I have already read multiple posts regarding this, but couldn't find any solution.
Yes, Kingfisher caches images both in memory and on disk.
By default, the amount of RAM that will be used is not even limited, you have to set the value yourself:
ImageCache.default.maxMemoryCost = 1024 * 1024 * yourValue
where 1024 * 1024 * yourValue is the global cost in megapixels (I know this is weird, but it's not megabytes, it's megapixels, because images can have different bit depth, etc).
For example, in my tests, the maximum RAM used with a value of 1024 * 1024 * 500 fluctuates between 120MB and 300MB.
Incidentally, this is also how you tell Kingfisher to never use the RAM and only cache to disk:
ImageCache.default.maxMemoryCost = 1
This will force Kingfisher to only use the disk cache.
How to debug
The first thing is to check that you're setting the max value on the right cache. Maybe you did create a custom cache? My example is setting the value for the default cache, the one used if no other is defined.
You may also want to manually clear the memory cache and compare the RAM occupation before and after:
ImageCache.default.clearMemoryCache()
If you think that some big image is in the memory cache when it shouldn't be, you can verify with isImageCached:
if let result = ImageCache.default.isImageCached(forKey: imageLink) {
print(result.cached)
print(result.cacheType)
}
If anyone looking for answer for downloading images explicitly and caching the same without using imageView the sample code is:
ImageDownloader.default.downloadImage(with: imgUrl, retrieveImageTask: nil, options: [], progressBlock: nil) { (image, error, url, data) in
print("Downloaded Image: \(url)")
//cache image:
if let image = image, let url = url {
ImageCache.default.store(image, forKey: url.absoluteString)
}
}
reference: https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet
Swift 5.3, Xcode 12
https://stackoverflow.com/a/44354411/10579134 , the latest version of the following
ImageCache.default.memoryStorage.config.totalCostLimit = 1 //1 in bytes

Cache Image from Firebase

I have multiple cells that currently hold different photos from Firebase. Every time a user loads these images then scrolls, they are re-downloaded which eats up data fast. I find this concerning to any user who has a metered data plan. What could I do to solve this? Does Firebase offer any options to cache downloaded images?
This is how I am currently calling an image into a cell:
if let imageName = post["image"] as? String {
let imageRef = FIRStorage.storage().reference().child("images/\(imageName)")
imageRef.data(withMaxSize: 25 * 1024 * 1024, completion: { (data, error) -> Void in if error == nil {
let image = UIImage(data: data!)
cell.postImageView.image = image
Use Kingfisher to cache images. It's light and very easy to use. Just pass your url from firebase and it will automatically cache it.
let url = URL(string: "url_of_your_image")
imageView.kf.setImage(with: url)
You might use Alamofire too. It does not handle caching automatically though, but against Kingfisher, it has the ability to handle almost all kinds of networking needs.
PS: Yes I know that -generally- I do not need any networking capabilities if I'm using Firebase.
But, for example; since Firebase Database and Firestore cannot handle full-text search, you need to use third-party solutions, so, you might be in need of full-featured networking utility sometime.
Firebase already does cache the database locally, before it fetch real-time data from the server, so the problem is not as severe. But if you want to do better caching, use Glide, Glide caches images and you can specify time signatures so it re-fetches images only if they are updated.
This is super easy, but you do need to host your images in google cloud services, or aws, or anywhere even
ImageView imageView = (ImageView) findViewById(R.id.my_image_view);
Glide.with(this).load("url").into(imageView);

Alamofire Image: Fetch Image from AutoPurgingImageCache after af_setImageWithURL()

I use AlamofireImage in my project. I use someImageView.af_setImageWithURL(url) a lot.
However at some point I need to fetch an image manually from the imageCache, because I do not want to apply it to an UIImageView.
This brings up 2 problems:
I can access the image cache directly by creating one with let imageCache = AutoPurgingImageCache(). Is this the same cache as .af_setImageWithURL() uses (Singleton)? Or is this a new one? Which is worthless in my case, because I could not profit from the prefilled cache.
Given this is the same cache: How can I fetch the correct image? I thought about imageCache.imageForRequest(NSUrlRequest). Will this match on the images I downloaded before with .af_setImageWithURL() if the NSUrlRequest does use the same NSURL?
Question 1
That is not the same cache that UIImageView uses. If you need access the same cache, you can do so by UIImageView.af_sharedImageDownloader.imageCache.
Question 2
You will need to use the exact same NSURLRequest and the same ImageFilter. If you use the same NSURLRequest and the same ImageFilter, the image will be directly fetched from the cache if it exists. If you are using a different ImageFilter, then the original image will most likely be pulled from the NSURLCache, then the ImageFilter will be run over it, placed into the AutoPurgingImageCache and returned. If the NSURLRequest is different, the new image will need to be downloaded again.
This is answer to the comment: How to download multiple images, save them to cache and get from cache.
Following code will download images from array of URLs and cache them in-memory.
let imageDownloader = ImageDownloader()
var URLRequests = [URLRequestConvertible]()
URLRequests.append(NSURLRequest(URL: NSURL(string: "https://some/image/some.png")!))
As #cnoon mentioned, to set an image use myUIImageView.af_setImageWithURL(imageUrl)
This will load image from cache if present. If it doesn't exist, then network call will happen

SDWebImage change cacheMaxCacheAge

How I can change a static value cacheMaxCacheAge when I use SDWebImage? What way is good? I need to cache images for 5 minutes.
In SDWebImage 5.0+ maxCacheAge renamed to maxDiskAge. You can set maximum caching time in seconds like this.
Objetive-c
[SDImageCache sharedImageCache].config.maxDiskAge = 60 * 5; //5 minutes
Swift 4+
SDImageCache.shared.config.maxDiskAge = 60 * 5 //5 minutes
Reference link: https://github.com/SDWebImage/SDWebImage/wiki/5.0-Migration-guide
SDWebImage is providing maxCacheAge property to set the maximum caching time in seconds. We can use shared instance method to access this property.
[SDImageCache sharedImageCache].config.maxCacheAge = 60 * 5; //5 minutes
SWIFT 5+ , SDWebImage 5.13 +, tvOS15+ and maybe other platforms
I found out that simply setting new maxDiskAge property on tvOS 15+ is not enough when storing image with SDImageCache.shared.store , but you indeed need to manually delete the old cache with deleteOldFiles() function. It happens in my case, even though the performing of old cache deletion is allegedly done by library itself after terminating the app as specified in following SO post. This might be only the case for tvOS, but I am not entirely sure if the problem persists also on another platforms. Here is the solution that worked for me:
Deleting expired cache:
SDImageCache.shared.deleteOldFiles()
Setting max cache disk time in seconds:
SDImageCache.shared.config.maxDiskAge = 100000
Storing images to disk:
SDImageCache.shared.store(image, forKey: key, toDisk: true)

Resources