-1

Here is my code how to download image from URL and save it into document directory using AFNetworking.

Now, my question is if image is already downloaded from URL then image is loaded from cache instead of re-download it. I want to do this using AFNetworking. I know that the solution for this problem is inside #import "UIKit+AFNetworking/UIKit+AFNetworking.h"

If anyone have any idea of how to help, please help me solve my issue.

#import "ViewController.h"

#define URL @"https://upload.wikimedia.org/wikipedia/commons/e/ec/USA-NYC-American_Museum_of_Natural_History.JPG"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.progressBar.hidden = YES ;
    self.lblProgressStatus.hidden = YES;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)Action:(UIButton *)sender
{
    self.progressBar.hidden = NO ;
    self.lblProgressStatus.hidden = NO ;
    self.ActionDownload.enabled = NO ;

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

    NSURL *strURL = [NSURL URLWithString:URL];
    NSURLRequest *request = [NSURLRequest requestWithURL:strURL];

    NSProgress *progress;

    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:&progress destination:^NSURL *(NSURL *targetPath, NSURLResponse *response)
        {
                NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
                return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
        }
        completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error)
        {
                [self.progressBar setHidden:YES];
                self.lblProgressStatus.text = @"Download completed" ;
                NSLog(@"File downloaded to: %@", filePath);

                NSString * strTemp = [NSString stringWithFormat:@"%@", filePath];
                NSArray *components = [strTemp componentsSeparatedByString:@"/"];
                id obj = [components lastObject];
                NSLog(@"%@", obj);

            NSString *docPath = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];
            NSString *strFilePath = [NSString stringWithFormat:@"%@/%@",docPath, obj];

            BOOL fileExists=[[NSFileManager defaultManager] fileExistsAtPath:strFilePath];

            if (!fileExists)
            {
                NSLog(@"File Not Found");
            }
            else
            {
                UIImage * image = [UIImage imageWithContentsOfFile:strFilePath];
                self.imageView.image = image ;
            }
            [progress removeObserver:self forKeyPath:@"fractionCompleted" context:NULL];

        }];

    [self.progressBar setProgressWithDownloadProgressOfTask:downloadTask animated:YES];
    [downloadTask resume];

    [progress addObserver:self
               forKeyPath:NSStringFromSelector(@selector(fractionCompleted))                  options:NSKeyValueObservingOptionNew
                  context:NULL];

}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"fractionCompleted"])
    {
        NSProgress *progress = (NSProgress *)object;
        int temp = progress.fractionCompleted * 100 ;
       // NSLog(@"%d", temp);
       NSString * strTemp = @"%";

        dispatch_async(dispatch_get_main_queue(), ^{
            // Update the UI
            self.lblProgressStatus.text = [NSString stringWithFormat:@"%d %@", temp, strTemp];
        });
    }
    else
    {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

@end
Ben
  • 1,061
  • 10
  • 23
Dharani
  • 88
  • 15

3 Answers3

2

You can download the image using this method defined in UIImageView+AFNetworking:

[imageView setImageWithURL:[NSURL URLWithString:URL] placeholderImage:[UIImage imageNamed:@"placeholder-avatar"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
    if ([[extension lowercaseString] isEqualToString:@"png"]) { 
        [UIImagePNGRepresentation(image) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", imageName, @"png"]] options:NSAtomicWrite error:nil];
    } else if ([[extension lowercaseString] isEqualToString:@"jpg"] || [[extension lowercaseString] isEqualToString:@"jpeg"]) {
        [UIImageJPEGRepresentation(image, 1.0) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", imageName, @"jpg"]] options:NSAtomicWrite error:nil];
    }
} failure:NULL];

The success block will be called even if it gets the image from cache. Hope it helped!

aramusss
  • 2,391
  • 20
  • 30
  • sorry, but I am not getting you answer . – Dharani Aug 19 '15 at 10:33
  • You want to download the image (either from cache or from URL) and save it locally? Or I misunderstood you? You can use UIImageView+AFNetworking.h? – aramusss Aug 19 '15 at 10:36
  • By the above code my image is saved in document directory, now when it once downloaded, it fetch the image from cache, not againg going to download it. and I want to do this using #import "UIKit+AFNetworking/UIKit+AFNetworking.h" – Dharani Aug 19 '15 at 10:39
  • My code does what you want. If the image is cache'd, it will get the image from cache, if not, it will download it. Then, it saves the image as a file (you can remove the code inside the success block and just show the image, or whatever). It uses `UIKit+AFNetworking`... Specifically, `UIImageView+AFNetworking`... `UIKit+AFNetworking` is just a header... check UIKit+AFNetworking.h and you will understand. There is no .m... – aramusss Aug 19 '15 at 11:35
  • Then What is extension and lowerstring ? I am not getting it. – Dharani Aug 19 '15 at 11:38
1

It uses cache by default. To test, go to a url you have access to of an image, then delete the image, and load again, and you'll see it's cached :D The images sometimes are not cached if they're big images.

If you want to increase this cache size, put this in your app delegate:

[[NSURLCache sharedURLCache] setMemoryCapacity:(20*1024*1024)];
[[NSURLCache sharedURLCache] setDiskCapacity:(200*1024*1024)];

EDIT RE: comments:

If you're looking to only download images once to your documents path, then perhaps the best way to test if an image already exists and should be downloaded or not is a test you can create. E.g, if the last path component (the last part of an image file path) of an image exists already in your documents, don't download it, else download it.

EDIT: further comments

Inside UIKit+AFNetworking/UIImageView+AFNetworking.h

/** Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. By default, URL requests have a Accept header field value of "image / *", a cache policy of NSURLCacheStorageAllowed and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use setImageWithURLRequest:placeholderImage:success:failure: @param url The URL used for the image request. */

- (void)setImageWithURL:(NSURL *)url;

This looks exactly like what you're looking for

to use:

#import <AFNetworking/UIKit+AFNetworking.h> 

and use

NSURL *strURL = [NSURL URLWithString:@"http://www.example.com/image.jpg"];
[imageview setImageWithURL:strURL];
Bhavin Bhadani
  • 22,224
  • 10
  • 78
  • 108
Ben
  • 1,061
  • 10
  • 23
  • I think you are not getting my question, this code download the image every time I run my app. I want that if image is already in document directory then it should be loaded from document and not re-download it again. – Dharani Aug 19 '15 at 10:20
  • oh ok, sorry :D i thought you meant literally cache, this is not cache you're speaking of – Ben Aug 19 '15 at 10:20
  • updated answer Aarti but cache is not what you have described above – Ben Aug 19 '15 at 10:25
  • The way you are saying I know it and I had tried for it. But I want to do this using function of #import "UIKit+AFNetworking/UIKit+AFNetworking.h". You have any idea how to do it ? – Dharani Aug 19 '15 at 10:27
  • what method in the import in particular? why do you want to use this import so much? have you got some code that does it? link that if you do – Ben Aug 19 '15 at 10:30
  • I actually don't know the method, but I have to done this by implement #import "UIKit+AFNetworking/UIKit+AFNetworking.h" – Dharani Aug 19 '15 at 10:33
  • why? is this school project? how do you KNOW you need to use this? – Ben Aug 19 '15 at 10:33
  • you can think that, but now my requirement is that. – Dharani Aug 19 '15 at 10:35
  • i'm just trying to ascertain how you know you NEED to use this? sorry then Aarti, i am going to try help elsewhere, happy coding anyway – Ben Aug 19 '15 at 10:36
  • Yes, exactly. But I have no idea how to use it in my code ? – Dharani Aug 19 '15 at 11:30
  • can you write the code as I had coded above in my question ? So, that I get proper idea. – Dharani Aug 19 '15 at 11:41
  • i've edited the question. it can't be simpler than this. you just put that in your viewDidLoad, forget all your other code – Ben Aug 19 '15 at 11:58
  • works for me, just tested. make sure device is connected to internet – Ben Aug 19 '15 at 12:18
  • thank you for downvoting my totally working answer though Aarti, very mature – Ben Aug 19 '15 at 12:32
  • Your answer working, but not as per my requirement. Its loading image from URL. But I want faster fetching, which cant be achieved using this. – Dharani Aug 19 '15 at 12:53
  • This uses faster fetching, the method 'setImageWithURL' uses cache. If the image has been loaded with it, it will be saved and used later when you use this method. Saving images to your documents path is NOT caching an image. Did you read the information I provided with my answer about the method? – Ben Aug 19 '15 at 12:54
  • Ok, then how it will be stored and can used later ? – Dharani Aug 19 '15 at 12:56
  • It's cached, you can't retrieve it later without using the method setImageWithURL. That method use the cached image on any new imageView. That's what cache is. If you want to save an image, save it! – Ben Aug 19 '15 at 12:58
  • I have to go into a meeting now but hopefully i've helped understand a bit – Ben Aug 19 '15 at 13:03
  • my e-mail said you have a question aarti but it isn't here, you fix? – Ben Aug 21 '15 at 07:57
  • hi.. can you answer this question http://stackoverflow.com/questions/33802803/facebook-app-invites-notification-not-working-in-ios. Please help me If you can. – Aarti Oza Nov 19 '15 at 11:58
0

I recommend you to use this library https://github.com/rs/SDWebImage

So, you can do something like this:

- (void)loadImage:(NSURL *)url
{
    __block UIImage *image = [[SDImageCache sharedImageCache] queryDiskCacheForKey:[url absoluteString]];

    if(!image) {

        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        [request setTimeoutInterval: 30.0]; // Will timeout after 30 seconds
        [NSURLConnection sendAsynchronousRequest:request
                                           queue:[NSOperationQueue currentQueue]
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

                                   if (data != nil && error == nil) {

                                       image = [UIImage imageWithData:data];

                                       NSData *pngData = UIImagePNGRepresentation(image);
                                       [[SDImageCache sharedImageCache] storeImage:image imageData:pngData forKey:[url absoluteString] toDisk:YES];
                                   }
                                   else {
                                       // There was an error, alert the user
                                       NSLog(@"%s Error: %@", __func__, error);
                                   }
                               }];
    }
}
Pablo A.
  • 2,042
  • 1
  • 17
  • 27