AFNetworking framework : how to cache image - objective-c

-(void)setImageWithURL:(NSURL *)url,
the method in AFNetworking described: "If the image is cached locally, the image is set immediately", but it does not make me to set cache directory, so where it cache the image in? Or is it really cache the image?

AFNetworking takes advantage of the caching functionality already provided by NSURLCache and any of its subclasses.
Use Pete Steinberger's fork of SDURLCache which provides disk caching, which is not otherwise implemented by NSURLCache on iOS.
Note: as of iOS5, this is no longer needed. NSURLCache now properly caches objects, as long as the Cache-Control header is set.
You may find it useful to set ignoreMemoryOnlyStoragePolicy to YES for the SDURLCache instance, which will ensure that images will be cached to disk more consistently for offline use (which, as reported in the SDURLCache README, iOS 5 supports, but only for http, so it still remains a good option if you want to support iOS 4 or https)
Refer AFNetworking-FAQ - Does AFNetworking have any caching mechanisms built-in? and also AFURLCache - disk cache for iOS links.

Related

ImageResizer DiskCache+AzureReader strange behaviour

I'm using ImageResizer + Diskcache plugin and I'm finding issues to get cache working properly. Either the images are cached forever (regardless how many times I upload a new image) or changing some settings I get the old image in some browsers/computers and the new one in others.
This is what I have now in my web.config:
<add name="AzureReader2" connectionString="blahblahblah" endpoint="http://blahblahblah.blob.core.windows.net/" prefix="~/" redirectToBlobIfUnmodified="false" requireImageExtension="false" checkForModifiedFiles="true" cacheMetadata="true"/>
and:
<diskcache dir="~/imagecache" autoclean="true" hashModifiedDate="true" subfolders="8192" asyncWrites="true" asyncBufferSize="10485760" cacheAccessTimeout="15000" logging="true" />
Not sure if is something I can achieve using the existing parameters. My goal is to invalidate the cache preferably when the new image has been uploaded without having to change the query string serving the image to get the new one.
I was thinking:
Maybe having a blob storage trigger that when a replacement image has
been uploaded, fires a webhook that deletes the cache for that image?
Or a web request to my imageresizer app to preload the new image in
cache so it replaces the old cached image???
I've seen some posts about using IVirtualFileWithModifiedDate but from what I understand that would have a big performance impact? Is probably 5% of our images request that will have someone uploading an image and expecting it to see it right away since most of the images barely change but it's really frustrating if the image doesn't show the new one not even a day after they have uploaded it!
If I can use IVirtualFileWithModifiedDate to invalidate the cache when the image has changed and not in every image request? Would that be possible?
I get the old image in some browsers/computers and the new one in others.
That different browsers are displaying different versions indicates that either browser caching or proxy/CDN caching is at fault.
ImageResizer's DiskCache hashes the modified date, so it is always as correct as the storage provider.
Regarding your expectations around server-side invalidation:
You're using checkForModifiedFiles="true" cacheMetadata="true", which means that Azure is queried for the latest modified date, but that metadata is cached with a sliding expiration window of 1 hour. I.e, if a URL hasn't been accessed in 1 hour, the next request will cause the modified date to be checked. See StandardMetadataCache.
You can change this behavior by implementing IMetadataCache yourself and assigning that cache to the .MetadataCache member of the storage provider you're using.

Use data from NSURLCache

I have setup an NSURLCache and set the sharedcache to the initialized cache.
I can see the cache size growing when I log the size in the NSURLConnection delegates.
For some reason when requesting the same data, a request is made to the server again, and the log of the cache size shows the log staying the same size.
This indicates to me that the cache is working correctly, but for some reason the URL Loader is not pulling from the cache.
I use the default cache policy on the requests and have double checked the headers in the response.
After reading around on here I see other people mentioning the same issue and setting the memory capacity and disk capacity to the same value solves their issue, but it does not work for me. New requests are being made every time rather than pulling from the cache.
Would it be wise to instead check the cache for the data before making the request? I dont see the point of the cachingPolicy parameter to the NSURLRequest method.
well HTTP also lets the server tell you how to cache - it can disallow caching (and also set a max. cache time)
that may influence SDURLCache
anyways, also look at UIWebView and NSURLCache have a troubled relationship
there it is said that UIWebView doesnt work

How may delegate method from one protocol prevent execution of another one from another protocol?

Specifically, connectionDidFinishDownloading:destinationURL: from <NSURLConnectionDownloadDelegate> prevented execution of connection:DidReceiveData: from <NSURLConnectionDataDelegate>.
How could it be?
I was mistyped in naming method, and then just auto completed typo with Xcode, received connectionDidFinishDownloading:destinationURL: instead of connectionDidFinishLoading.
Then for a several days I could not realize why connection:DidReceiveData: wasn't called at all. So I've repaired everything, and now asking you: why is that?
Thx for responses.
It's a feature that is not well documented. NSURLConnection has two different delegate protocols: NSURLConnectionDataDelegate and NSURLConnectionDownloadDelegate.
NSURLConnectionDataDelegate: delegate methods used for loading data to memory. These delegate methods are all optional.
NSURLConnectionDownloadDelegate: delegate methods used to perform resource downloads directly to a disk file. All the methods are optional with the exception of connectionDidFinishDownloading:destinationURL: which must be implemented in order to inform the delegate of the location of the finished download.
As you see, if you implemented connectionDidFinishDownloading:destinationURL: in your delegate. That will inform NSURLConnection you want to download the data to a disk file other than to memory as NSData. The target disk file is located in the applications cache directory and is guaranteed to exist for the duration of the delegate callback. The implication is that the delegate should copy or move the download to a more persistent location.
So if you eliminate connectionDidFinishDownloading:destinationURL: from your delegate class implementation, connection:DidReceiveData: will get called instead.
How ever this is not the last story. When you implement connectionDidFinishDownloading:destinationURL: and then wanna access the downloaded file through destinationURL. Unfortunately the file isn't there. Gotcha, the delegate callback is for Newsstand apps. If you are not developing a newsstand app while you specify in the Info.plist, please keep away from NSURLConnectionDownloadDelegate. Apple confirmed this is a bug from iOS 5 to iOS 7. Still not fixed yet:)

ALAsset invalid after Camera Roll changes?

I write some photos to the photo library using UIImageWriteToSavedPhotosAlbum() and at the same time I display the contents of this asset group (ALAssetsGroupSavedPhotos) using enumerateAssetsUsingBlock: and friends. Sometimes the assets returned by enumerating the group become sort of “invalid”, meaning that the defaultRepresentation call returns nil, although the asset is still in memory.
I noticed that this seems to happen after the photo library gets modified by the UIImageWriteToSavedPhotosAlbum() call. Is this a documented behaviour? How can I prevent it? Reloading the assets is not a feasible option, as the user might already be somewhere deeper in the UI working with the asset.
this is an unfortunate, but documented behavior. For reference:
"ALAssetsLibraryChangedNotification Sent when the contents of the
assets library have changed from under the app that is using the data.
When you receive this notification, you should discard any cached
information and query the assets library again. You should consider
invalid any ALAsset, ALAssetsGroup, or ALAssetRepresentation objects
you are referencing after finishing processing the notification."
So what you have to do is to register an observer for ALAssetsLibraryChangedNotification. (And there is a bug in regarding this notification on iOS 5.X, see Open Radar.)
When you receive the notification you have to reenumerate all groups and assets. There is at the moment no other way. This is very unfortunate from a GUI perspective and we can only hope Apple improves this mechanism in the future.
Cheers,
Hendrik

How to configure the cache when using AFNetworking's setImageWithURL

I'm using setImageWithURL to download some images in my app. Is it possible:
to specify how long this image must be held in cache (eg 1 week)?
to specify how big the the maximum total size of the cache (eg 200
MB)
to see what is in the image cache?
to clear the cache?
The documentation is not really clear on these points..
The UIImageView category uses an internal, ephemeral cache for high performance in things like UITableView. For longer-term cache, use the system-level cacheing system--namely Peter Steinberger's fork of SDURLCache, a subclass of NSURLCache.
Set it up with the following code in the application delegate applicationDidFinishLaunching:withOptions::
SDURLCache *URLCache = [[SDURLCache alloc] initWithMemoryCapacity:1024*1024*2 diskCapacity:1024*1024*20 diskPath:[SDURLCache defaultCachePath]];
[URLCache setIgnoreMemoryOnlyStoragePolicy:YES];
[NSURLCache setSharedURLCache:URLCache];
[URLCache release];