iOS Caching images with AFImageCache doesn't seem to work - ios

I'm trying to cache some images loaded from a URL by using AFImageCache, without success... I'm not sure If i'm doing anything wrong and would love your opinions if you've ever used this library.
I'm getting an image from a NSURLRequest, and then caching it as follows
UIImage *img; //This image is a png icon returned from the server
NSData *imgData = UIImagePNGRepresentation(img); // Always checking length to make sure its more than 0, and it is
[[AFImageCache sharedImageCache] cacheImageData: imgData
forURL: myURL
cacheName: #"IconCache"];
On the next run, i'll try to check if the image was cached, but will always get nil for some reason.
UIImage *myIcon = [[AFImageCache sharedImageCache] cachedImageForURL: myURL cacheName: #"IconCache"];
NSLog("%#", myIcon); // output (null)
Any ideas or thoughts about this?
Thanks.

What version of AFNetworking are you using? AFImageCache was removed from the public API a long time ago. If you want to do image cacheing, use NSURLCache, which caches all network responses on a system level. AFImageCache has always just been a temporary cache used to improve the performance of image loading in scroll views.

Related

SDWebImage refreshing

I am using the SDWebImage to load the image from URL. When image is updated on the server, then image is not updating in the image view even i have used options: .refreshCached. After updating image, URL remains same.
Can i directly change image in cache for this URL?
I googled for this question,i got that i can not change it in cache directly. If i can not change image in cache directly for specify URL, Is it possible to clear cached value for specific key (URL)?
self.imgView.sd_setImage(with: url, placeholderImage: #imageLiteral(resourceName: "Timg"), options: .refreshCached, completed: { (image: UIImage?, error: Error?, cacheType: SDImageCacheType, imageURL: URL?) in
if image != nil{
self.imgView.contentMode = .scaleAspectFill
}
})
Looks like the issue has already been raised in the official GitHub page of SDWebImage. Clearly the answer suggests that [[SDImageCache sharedImageCache] removeImageForKey:image_url fromDisk:YES]; is not doing fair enough job to clear the image.
Suggested answer says
[[SDImageCache sharedImageCache] clearMemory];
[[SDImageCache sharedImageCache] cleanDisk];
Now whether you need both the statements or not depends on how you have configured SDImageCache.
What do they mean ?
SDWebImage caches images extensively, it keeps the copy of cache in RAM as well as in HardDisk. When an image is requested, it hits the RAM Cache first to find the image, if not found then hits disk cache. And loads RAM cache from disk cache.
So clearing simply from disk will still leave image in Disk cache and when requested an image rather than downloading from web it rather loads from Disk and keeps a copy in RAM for future use. So it defeats the idea of deleting the image from cache.
I know its not the answer you are specifically looking for, but unfortunately am not aware of deleting specific image from SDImageCache.
[[SDImageCache sharedImageCache] removeImageForKey:image_url fromDisk:YES];

Is there a way to refresh the cache used by UIImage class?

In my iOS app I am using the +imageNamed: method to load an image (many times and in many different places in the code).
In one case the user might update (download) a new image.
When I try to load the new, it will show the old, due to caching.
From the "Is there a way to clear the cache used by UIImage class?" question, I saw that I have to use the -initWithContentsOfFile: method.
But this will not take advantage of the caching speedup that the +imageNamed: enjoys. All I want is to "tell" the cache that the file has changed, so it needs to "re-cache" it. And then keep using the +imageNamed: method with the new cached image.
In other words, I use the +imageNamed: method (say) 10 times, I change the image, I "tell" the cache, then I continue use the +imageNamed: method another (say) 10 times. If I change all the +imageNamed: to -initWithContentsOfFile: then I lose the caching advantage.
Is there a way/trick to do that?
There is no API for clearing the cache. If your app is not destined for the app store you could call the private method:
[UIImage _flushSharedImageCache];
However I wouldn't want this anywhere near production code.
Instead I would create a category on UIImage and add a method for returning the desired image from a filename. This name would be stored and then updated when your new image is downloaded. You will get the benefit of caching, without any hacky workarounds.
Depending on the complexity of your project, a simple find and replace shouldn't take too long.
Although I'm now questioning how your app is working currently, imageNamed only looks for files in your app's bundle, so won't work for images downloaded by the user.
You'll probably just have to figure out your own way of caching your images.
I'd suggest using a UIImage category with a static NSMutableDictionary that can hold your cached images. Then just use your custom caching method when initialising your UIImage.
For example:
#interface UIImage (UIImageCache)
+(UIImage*) cachedImageFile:(NSString*)imageFile;
+(void) resetCacheForImageFile:(NSString*)imageFile;
#end
#implementation UIImage (UIImageCache)
static NSMutableDictionary* cachedImages;
+(UIImage*) cachedImageFile:(NSString*)imageFile {
// Optional error checking
NSAssert1([[NSFileManager defaultManager] fileExistsAtPath:imageFile], #"Warning! The image file %# doesn't exist.", imageFile);
if (!cachedImages) cachedImages = [NSMutableDictionary dictionary];
UIImage* cachedImg = [cachedImages objectForKey:imageFile];
if (cachedImg) return cachedImg; // Image is cached, return it
else { // No cached image, create one
UIImage* img = [UIImage imageWithContentsOfFile:imageFile]; // iOS won't auto-cache the image.
[cachedImages setObject:img forKey:imageFile];
return img;
}
}
+(void) resetCacheForImageFile:(NSString*)imageFile {
[cachedImages removeObjectForKey:imageFile];
}
#end
Maybe I just got late to the party...but using
+ (UIImage *)imageWithContentsOfFile:(NSString *
I got rid of the cache issue.
Hope it helps!!

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

UIImages NSURLs and Threads

I am trying to build a nice function to access the network for images, if they are found on the web, I store them in a cache system I made.
If the image was already stored on the cache, I return it.
The function is called getImageFromCache and returns an image if it is in the cache, else, it would go to the network and fetch.
The code might look like this:
UIImageView* backgroundTiles = [[UIImageView alloc] initWithImage[self getImageFromCache:#"http://www.example.com/1.jpg"]];
Now, I am moving on to using threads because of big latencies due to network traffic. So I want images to show a temp image before I get the result from the web.
What I want to know is how can I keep track of so many images being accessed sequentially, being added to UIImageViews by this function (getImageFromCache).
Something just won't work there:
-(UIImage*)getImageFromCache:(NSString*)forURL{
__block NSError* error = nil;
__block NSData *imageData;
__block UIImage* tmpImage;
if(forURL==nil) return nil;
if(![self.imagesCache objectForKey:forURL])
{
// Setting a temporary image until we start getting results
tmpImage = [UIImage imageNamed:#"noimage.png"];
NSURL *imageURL = [NSURL URLWithString:forURL];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
imageData = [NSData dataWithContentsOfURL:imageURL options:NSDataReadingUncached error:&error];
if(imageData)
{
NSLog(#"Thread fetching image URL:%#",imageURL);
dispatch_async(dispatch_get_main_queue(), ^{
tmpImage = [UIImage imageWithData:imageData];
if(tmpImage)
{
[imagesCache setObject:tmpImage forKey:forURL];
}
else
// Couldn't build an image of this data, probably bad URL
[imagesCache setObject:[UIImage imageNamed:#"imageNotFound.png"] forKey:forURL];
});
}
else
// Couldn't build an image of this data, probably bad URL
[imagesCache setObject:[UIImage imageNamed:#"imageNotFound.png"] forKey:forURL];
});
}
else
return [imagesCache objectForKey:forURL];
return tmpImage;
}
This is not a direct answer to your question, but are you aware that there is no need to use GCD to download things asynchronously (on a background thread)? Just use NSURLConnection and its delegate methods. All your code will be on the main thread but the actual connection and downloading will happen in the background.
(And in fact I have written a class, MyDownloader, that takes care of all this for you:
http://www.apeth.com/iOSBook/ch37.html#_http_requests
Scroll down to the part about MyDownloader and its subclass MyImageDownloader, which is doing exactly the sort of thing you need done here. Moreover, note the subsequent code in that chapter showing how to use a notification when a download completes, prompting the table view that need these images to reload the row that contains the image view whose image has just arrived.)
its good your building it from scratch but if you want to save the all the work, there's a drop in Replacement SDWebImage Library with support for remote images coming from the web, and has all the functionality Like Temp Image, Asychronous Loading, Caching etc, you said you need
In your background thread, once the download has completed and you've saved the image to the cache, I'd suggest you post a notification using the NSNotificationCenter to let other parts of your app know that the cache has been updated.
This assumes that whichever part of the app manages the image views has registered its interest in those notification with the addObserverForName method. When it receives such a notification, it can then attempt to retrieve the images from the cache again and update its image views if appropriate.
Depending on the number of image views, you may want to pass through the image url in the notification in some way (e.g. in the userInfo dictionary), and then based on that decide which image views should be refreshed rather than refreshing them all.
I should add that I would also recommend getting rid of the inner dispatch_async call. There's no need for that, although you may need to add synchronisation to your cache object so it can be safely accessed from the main thread as well as the download thread.

Doesn't load picture from valid URL

UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]];
I use the code above and then check
if (image)
return image;
else
{
NSLog(#"no image on URL");
return nil;
}
But sometimes (very very seldom) I don't get an image from a valid url. The url is valid 100%.
Usually it takes nearly one second to load a picture, but when it can't load a picture the process takes much more time (20-200 seconds).
And then i get "no image on URL".
Is there a better way to get a picture from URL?
I'd rather get "no image on URL" in one second then waiting so long.
P.S. srry for my poor english
It is very rare that you want to use dataWithContentsOfURL:. It's a blocking call so requires a background thread. It's also inflexible and doesn't provide good error returns (which is the problem you're encountering).
See the URL Loading System Programming Guide. Generally you'll want to configure an asynchronous NSURLConnection for this kind of work. If you're doing a lot of network operations, you may want to consider a framework like MKNetworkKit or AFNetworking which handle a lot of the complexities for you.

Resources