NSURLCache not used by NSURLConnection / AFNetworkingOperation - ios

When I start a request with the NSURLRequestReturnCacheDataElseLoad policy I expect to get the result from the NSURLCache if any, no matter how old it is. However, the system always tries to reach the server the request points to and returns an error if the server doesn't answer.
I use the UIImageView category of AFNetworking for the request.
NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60.0];
__weak __typeof(self) weakSelf = self;
NSLog(#"[%#] %#", request.URL, [[NSURLCache sharedURLCache] cachedResponseForRequest:request]); // this returns an instance of NSCachedURLResponse!
[self setImageWithURLRequest:request placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
__typeof(weakSelf) self = weakSelf;
self.image = image;
} failure:NULL];
This will not set the image even if asking the NSURLCache directly will return a valid NSCachedURLResponse.
The app is running on iOS6 only, so there should be no problems with on-disk cache as far as I know?!
Any help is appreciated! Thanks!

This is a know issue. Please refer this discussion on AFNetworking github page for a workaround.

Related

NSURLCache does not work when response header value for transfer-encoding is chunked

I found an issue with (possibly) NSURLCache today while inspecting request and response headers in Charles Proxy. The issue is a little perplexing, but I'm able to repro it consistently:
In a nutshell, the issue has to do with caching of NSURLRequests using iOS's native NSURLCache with the default policy. It turns out that the request is not cached whenever the response has the header transfer-encoding: chunked. But if the response header is content-length: xxx instead, caching works fine. Specifically, it seems that when the response is chunked, NSURLCache doesn't save the eTag and also neglects appending the if-none-match header to subsequent requests to the same url, and consequently, caching fails (as it should), i.e. a 200 is returned instead of a 304.
I'm testing on the iOS8.2 simulator. Even if you don't have a solution, I'd love to hear if you've experienced the same issue. I've found at least one similar report), and here's a related thread posted by my back-end engineer.
It should work if you manually add the response data to the cache. I've got an image loading class where I want to make sure everything is cached, so I do something like this:
- (void)getImageWithURL:(NSURL *)url onCompletion:(void (^)(UIImage *image, NSError *error))completion {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
UIImage *cachedImage = [self cachedImageForURLRequest:request];
if (cachedImage) {
NSLog(#"Got image from cache.");
completion(cachedImage, nil);
return;
}
[[[NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Manually cache the response.
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
[[NSURLCache sharedURLCache] storeCachedResponse:cachedResponse forRequest:request];
NSLog(#"Got a fresh image.");
completion([UIImage imageWithData:data], error);
}] resume];
}
- (UIImage *)cachedImageForURLRequest:(NSURLRequest *)urlRequest {
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:urlRequest];
return [UIImage imageWithData:cachedResponse.data];
}

UITableView with image in cell stucks

I am developing an app which use UITableView,which contains cell having UIImageView of size 320 X 200.
This images comes from the web url and store to my app folder.Then i am showing this image to user.
For that i am using "UIImageView+AFNetworking.h" class's below method.
- (void)setImageWithURL:(NSString *)url
placeholderImageName:(NSString *)placeholderImage
saveFilePath:(NSString *) filePath
IndexPath:(NSIndexPath *)indexPath
success:(void (^)(NSIndexPath *indexPath, UIImage *image))success
failure:(void (^)(NSIndexPath *indexPath, NSError *error))failure {
NSLog(#"DOWNLOAD IMG URL :: %#", url);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
[request setHTTPShouldHandleCookies:NO];
[request setHTTPShouldUsePipelining:YES];
[request addValue:#"image/*" forHTTPHeaderField:#"Accept"];
[self setImageWithURLRequest:request
placeholderImage:placeholderImage == nil? nil : [UIImage imageNamed:placeholderImage]
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
[Storage SaveImage:image WithFileName:filePath];
if (success)
success(indexPath, image);
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
if (failure)
failure(indexPath, error);
}
];
}
when my all images downloaded to my local folder after that my tableview stucks while showing images.
It might be possible that the downloading happens in sync with the main thread and is blocking it until the download has finished.
I use SDWebImage in my projects for this. It work's really really well.
EDIT:
OK, AFNetworking is doing all the work asynchronously and also caches all the images. See the documentation.
If you don't need your success and failure blocks, you could go away with this.
- (void)setImageWithURL:(NSString *)url
placeholderImageName:(NSString *)placeholderImage {
NSURL *url = [NSURL URLWithString:[url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
UIImage *pImage = placeholderImage == nil? nil : [UIImage imageNamed:placeholderImage];
[self setImageWithURL:url placeholderImage:pImage];
}
Or maybe the image is extremely large (that's independent of the UIImageView size).
Can we see your code?
Do you load them manualy from the file system? Probably is that, because you do that in the main thread.
Try this in the code that loads the image:
NSLog(#"Is this the main thread: %d", [NSThread isMainThread]);
If you see 1, that's the problem. You can solve it using GCD (remember that once the image is loaded, move it to the main thread)
Use NSOperationQueue for downloading image. While adding new item in NSOperationQueue, first check if it already added or not.
After NSOperation complete, that image will save into local.
Always load image from locally. If image not present in local then display some place holder.
Hope it helps.
Regards,
Punita

Retain cycle with AFNetworking

I'm writing a gallery with images which can be loaded by url with AFNetworking.
In Init method of the ImageView object I call a function that send a request. Here:
- (void) loadWithUrl:(NSURL *)url
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:TimeOut];
[request setHTTPShouldHandleCookies:NO];
[request setHTTPShouldUsePipelining:YES];
__weak AOWImageView *safeSelf = self;
m_operation = [AFImageRequestOperation imageRequestOperationWithRequest:request
imageProcessingBlock:nil
success:^
(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image)
{
[safeSelf setImage:image];
}
failure:^
(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error)
{
[safeSelf setNoImageLabelOpaque];
}];
[m_operation start];
}
If the ImageView is outside the visible part of the screen - (void) dealloc is called. I cancel operation loading image in this method so: [m_operation cancel];. I guess that the operations are not canceled because the memory is increasing and isn't released.
I think that there is retain cycle. I want to understand how to write it right. Thanks.
I don't see a retain cycle in the code snippet you posted, so if you are leaking memory, it may be due to another part of your app's code...
Regarding "best practices" for AFNetworking and loading images-
AFNetworking has a built in category on UIImageView that has convenience methods for setting an an image view's image via a URL.
See UIImageView+AFNetworking, specifically setImageWithURL: method and related. This also has the advantage of keeping a cache (so you don't have to fetch images again if requested multiple times), which AFAIK, doesn't appear to done by AFImageRequestOperation.

Memory leak in AFNetworking Cache images in IOS

I just moved from ASIHTTP to AFNetworking library. I am also using SDURLCache library from Olivier Poitrey and Peter Steinberg. I want to cache all the images that I gonna use in my application. For these, I tried this:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
for(NSURLRequest *imageRequest in imageRequestArray){
AFURLConnectionOperation *operation2 = [[[AFURLConnectionOperation alloc] initWithRequest:imageRequest] autorelease];
[queue addOperation: operation2];
[operation2 waitUntilFinished];
}
And when I need to show images, I am doing this for each image:
NSURL *url = [NSURL URLWithString:[cat imagePath]];
NSURLRequest *imageRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30.0];
AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:imageRequest
imageProcessingBlock:nil
cacheName:#"nscache"
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){
//NSData *responseData = [request responseData];
imageView.alpha = 0.0;
[imageView setImage:image forState:UIControlStateNormal];
[UIView beginAnimations:#"ToggleViews" context:nil];
[UIView setAnimationDuration:1.0];
imageView.alpha = 1.0;
[UIView commitAnimations];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){}
];
[operation start];
After a while, application gives memory warning, then shuts down. For that, I did the following:
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
[[NSURLCache sharedURLCache] removeAllCachedResponses];
NSLog(#"Memory Warning...");
}
It clears the cache(what I don't what) but after a while, application closes again.
What should I do for that?
The problem is likely caused because ARC doesn't automatically get set up on queues or on background threads. I had the same issue and solved it with the following article:
https://agilewarrior.wordpress.com/2012/04/09/memory-leaks-with-arc/

AFNetworking+UIImageView placeholder image shows up, but not URL image

I'm trying to use the AFNetworking UIImageView call to load images from a URL as shown below:
[self.image setImageWithURL:[NSURL URLWithString:feed.imageURL] placeholderImage: [UIImage imageNamed:#"logo"]];
The placeholder image always shows up, but the actual image from "feed.imageURL" never does. I've verified that the URL is actually correct. I even hardcoded it to make sure, and still nothing.
My basic app setup is a tab controller...and in viewDidLoad, I call a method "fetchFeed" which performs the HTTP request to gather my JSON data.
My request block looks like:
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
[self parseDictionary:JSON];
isLoading = NO;
[self.tableView reloadData];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Error: %#", error);
[self showNetworkError];
isLoading = NO;
[self.tableView reloadData];
}];
operation.acceptableContentTypes = [NSSet setWithObjects:#"application/json", #"text/json", #"text/javascript", #"text/html", nil];
[queue addOperation:operation];
Turns out the server I was requesting the image from was sending content-type "image/jpg" and by default AFNetworking does not support this file type.
I changed the class method in AFImageRequestOperation to look like:
+ (NSSet *)defaultAcceptableContentTypes {
return [NSSet setWithObjects:#"image/tiff", #"image/jpeg", #"image/gif", #"image/png", #"image/ico", #"image/x-icon" #"image/bmp", #"image/x-bmp", #"image/x-xbitmap", #"image/x-win-bitmap", #"image/jpg", nil];
}
and it fixed my problem.
You can manage to accept what content-type you want with this library simply changing the request like this:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:yourURL];
[request addValue:#"image/*" forHTTPHeaderField:#"Accept"];
And call the AFNetworking method:
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
}];
This way you will be able to override the content-type without changing the library.
AFNetworking doesn't support image/jpg MIME TYPE by default.
You can support it without modifying the AFNetworking Library
[AFImageRequestOperation addAcceptableContentType:#"image/jpg"];
All operations that manipulate the UI must be performed on the main thread. So you may need to use 'performSelectorOnMainThread:' when reloading your tableview data in the completion block.
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO]
I had a similar problem but it turned out that I was passing a URL which contained spaces in it. When I properly encoded the URL using stringByAddingPercentEscapesUsingEncoding: the images now load.

Resources