Alamofire Image: Fetch Image from AutoPurgingImageCache after af_setImageWithURL() - ios

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

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

Caching downloaded images: FileManager / CachePolicy / URLCache / NSCache?

I'm need to implement the common scenario of having a table view with N cells where each of those cells needs to download an image to be displayed within it.
The service protocol to call to download the images could be either HTTP or HTTPS.
I am using an URLSessionDownloadTask this way:
func downloadImage(urlStr: String, completion: #escaping (UIImage?, Error?) -> Void) {
let url = URL(string: urlStr)
let request = URLRequest(url: url!)
let task = session.downloadTask(with: request, completionHandler: {
(fileUrl, response, error) in
// Call 'completion' depending on result
})
task.resume()
}
Where session is an URLSession with default configuration and associated operation queue:
self.session = URLSession(configuration: configuration, delegate: nil, delegateQueue: self.operationQueue)
So, what I want is to avoid to download an image that was already downloaded. And I would like them to have some expiration time.
I've read some articles and posts and I'm not completely clear about the differences between the options I found:
A. Using FileManager to actually store the image as a file, and removing it after checking the expiration time.
B. Setting the cachePolicy property of URLRequest.
C. Using URLCache
D. Using NSCache
About A:
What is actually the difference between storing the image as file and using a cache? Could have the file storage offer any benefit that a cache does not? Those images are not user-related, I can download them from a server when needed.
About B:
I read Apple's documentation about that, but I don't fully understand if for my scenario I should use NSURLRequestUseProtocolCachePolicy.
How does this option actually work? It is enough to set the policy and then you don't have to care about anything else? How does the URLRequest now that the image is asked for download has been already downloaded and cached?
About C:
How does it should be correctly implemented? Could anybody provide me an example/tutorial in case this is the best approach? What about expiration date?
About D:
I found an example I understood, but would it be a good approach having the previous options? What about expiration date also here?
In summary: which of the options would be the most efficient and appropriate for my scenario, and why?
From what I inferred about your question "what I want is to avoid to download an image that was already downloaded. And I would like them to have some expiration time."
To avoid images from downloading again, you can implement the following use case where, you store the images in the NSCache using the urls of the image itself.
This would be something like as discussed in the link.
For the expiration time case, if you want to remove all images at a particular expiration time, then just make a check for that scenario and empty the cache.
For the case where you want to remove the individual images, based on their expiration time, you can check the response from the server for the expiration key, and again remove cache in case the limit has been breached.

Refresh cached image in AlamofireImage?

Is it possible to automatically remove cached image and download newer if image was updated on server? I have tried AlamofireImage and STXImageCache but they both download an image only once and do not update it.
I try like that:
private let downloader = ImageDownloader()
func downloadImage(path: String?, completion: #escaping (UIImage?) -> ()) {
guard let path = path else { return }
let urlRequest = URLRequest(url: URL(string: path)!)
downloader.download(urlRequest) { response in
completion(response.result.value)
}
}
Manual update is not so good also, because if I don't know if an image was updated on server I have to forget about image caching at all.
Looks like none of available pods are able to refresh cached images, or any other cached content, when the content is changed on server. It is technically possible due to Content-Control technology, but apple does not seem to provide this possibility https://developer.apple.com/documentation/foundation/nsurlrequest.cachepolicy/1414422-reloadrevalidatingcachedata
reloadRevalidatingCacheData
Specifies that the existing cache data may
be used provided the origin source confirms its validity, otherwise
the URL is loaded from the origin source.
This constant is unimplemented and shouldn’t be used.
Your best move would be to ask server developer to generate unique URL addresses to any image that will change(avatar, backgrounds, icons etc), or if server developer is not available you should just get rid of the caching in places where there is such a problem and use casual download.
In any case you can use my gist for downloading images cached and not cached here https://gist.github.com/sam-moshenko/562ec61431c4a0ebeb68899b4d1b4d26 (Just don't forget to install pod 'AlamofireImage')

AlamofireImage cache?

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

ios fast Image cache with server

I am trying to use Path's FastImageCache library to handle photos in my app. The sample they provide simply reads the images from disk. Does anyone know how I might modify it to read from a url? In the section about providing source images to the cache they have
- (void)imageCache:(FICImageCache *)imageCache wantsSourceImageForEntity:(id<FICEntity>)entity withFormatName:(NSString *)formatName completionBlock:(FICImageRequestCompletionBlock)completionBlock {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Fetch the desired source image by making a network request
NSURL *requestURL = [entity sourceImageURLWithFormatName:formatName];
UIImage *sourceImage = [self _sourceImageForURL:requestURL];
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(sourceImage);
});
});
}
Has anyone used this api before and know how to get the source from the server to pass to the cache? Another example that still uses hard disk is
- (void)imageCache:(FICImageCache *)imageCache wantsSourceImageForEntity:(id<FICEntity>)entity withFormatName:(NSString *)formatName completionBlock:(FICImageRequestCompletionBlock)completionBlock {
// Images typically come from the Internet rather than from the app bundle directly, so this would be the place to fire off a network request to download the image.
// For the purposes of this demo app, we'll just access images stored locally on disk.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *sourceImage = [(FICDPhoto *)entity sourceImage];
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(sourceImage);
});
});
}
I worked on Fast Image Cache while I was at Path. The critical portion of Fast Image Cache is that it is the absolute fastest way to go from image data on disk to being rendered by Core Animation. No decoding happens, none of the image data is kept in memory by your app, and no image copies occur.
That said, the responsibility is yours to figure out how to download the images. There's nothing inherently special about downloading images. You can use NSURLConnection or one of many popular networking libraries (like AFNetworking) to actually download the image data from your server. Once you have that image data, you can call the relevant completion block for Fast Image Cache to have it optimize it for future rendering.
If you're looking for a simple way to download an image and display it when it's finished, then use something like SDWebImage. It's great for simple cases like that. If you are running into performance bottlenecks—especially with scrolling—as a result of your app needing to display tons of images quickly, then Fast Image Cache is perfect for you.
Your Approach Seems a Lot Like Lazy Loading Images from the URL, I had to do this once I had Used the following Library to do it, It dosent stores the Images in the disk, but uses cached Images..the below is its link..
https://github.com/nicklockwood/AsyncImageView
I added the networking logic to our fork > https://github.com/DZNS/FastImageCache#dezine-zync-additions-to-the-class
It utilizes NSURLSessionDownloadTasks, has a couple of configuration options (optional). All you need to do is create a new instance of DZFICNetworkController and set it as the delegate for FICImageCache's sharedCache instance object. It'll take care of downloading images with reference to the sourceImageURLWithFormatName: method on your objects conforming to <FICEntity>.
As I assume you'd use this in a UITableView or UICollectionView, calling cancelImageRetrievalForEntity:withFormatName: on the imageCache will cancel the download operation (if it's still in-flight or hasn't started).

Resources