Objective-C: download image and save it in cache

thailand

Let’s download image (pretty simple task) and learn little more about caching mechanism of iOS (there are nuances).

iOS has default caching mechanism and it works good. But sometimes you may need more flexibility or may need to set settings independent from server headers.

Step 1.

As usually create “Single View Application”. Set language to Objective-C.

Step 2.

Set cache size in AppDelegate in function didFinishLaunchingWithOptions:

NSURLCache *urlCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
                                                         diskCapacity:20 * 1024 * 1024
                                                             diskPath:nil];
    [NSURLCache setSharedURLCache:urlCache];

Step 3.

Draw simple interface.

UILabel *imageCacheLabel;
UIImageView *imageView;

- (void)viewDidLoad {
    [super viewDidLoad];
    [self buildView];
}

- (void) buildView {
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    
    UIView *imageContainerView = [[UIView alloc] initWithFrame:CGRectMake(
                                                                   0,
                                                                   0,
                                                                   screenWidth,
                                                                   350)];
    imageContainerView.layer.borderColor = [UIColor grayColor].CGColor;
    imageContainerView.layer.borderWidth = 1.0f;
    [self.view addSubview:imageContainerView];
    
    imageCacheLabel = [[UILabel alloc] initWithFrame:CGRectMake(
                                                                    30,
                                                                    30,
                                                                    imageContainerView.frame.size.width - 60,
                                                                    20)];
    imageCacheLabel.text = @"Not loaded yet";
    imageCacheLabel.textAlignment = NSTextAlignmentCenter;
    [imageContainerView addSubview:imageCacheLabel];
    
    imageView = [[UIImageView alloc] initWithFrame:CGRectMake(
                                                              30,
                                                              imageCacheLabel.frame.origin.y + imageCacheLabel.frame.size.height + 10,
                                                              imageContainerView.frame.size.width - 60,
                                                              200)];
    imageView.backgroundColor = [UIColor grayColor];
    [imageContainerView addSubview:imageView];
    
    UIButton *loadButton = [[UIButton alloc] initWithFrame:CGRectMake(
                                                                        (screenWidth - 200)/2,
                                                                        imageView.frame.origin.y + imageView.frame.size.height + 20,
                                                                        200,
                                                                        30)];
    [loadButton setTitle:@"Load image" forState:UIControlStateNormal];
    loadButton.showsTouchWhenHighlighted = true;
    loadButton.backgroundColor = [UIColor grayColor];
    [loadButton addTarget:self action:@selector(loadImage:) forControlEvents:UIControlEventTouchUpInside];
    [imageContainerView addSubview:loadButton];
    
}

The result will be as follows:

Screen Shot 2017-04-22 at 09.07.59

Step 4.

Write logic of loading image.

- (void) loadImage:(UIButton *) sender {
    NSLog(@"loadImage");
    
    imageCacheLabel.text = @"Loading";
    
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:@"https://doszhan.com/wp-content/uploads/2017/04/thailand.jpg"]];
    request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
    
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                            completionHandler:
                                  ^(NSData *data, NSURLResponse *response, NSError *error) {
                                      if (data == nil) {
                                          [self printCannotLoad];
                                      } else {
                                          [self setImageData:data];
                                      }
                                  }];
    [task resume];
}

- (void) setImageData:(NSData *) data {
    dispatch_sync(dispatch_get_main_queue(), ^{
        imageView.image = [[UIImage alloc] initWithData:data];
        imageCacheLabel.text = @"Loaded";
    });
}

- (void) printCannotLoad {
    dispatch_sync(dispatch_get_main_queue(), ^{
        imageCacheLabel.text = @"Cannot load";
    });
}

The majority of code is copy pasted from the previous post. The most interesting part is in this line:

request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

Using such option of caching app will take out image from cache if it possible ignoring “age” of cached image. You can discover all options in official Apple documentation. Pay attention that at this moment (April 2017) two options are unimplemented (actually these options remain unimplemented several years).

Source code:

Screen Shot 2017-04-22 at 19.44.27

From documentation:

Screen Shot 2017-04-22 at 21.26.42

Also you can set max-age property on client side (usually this option set on server side). For that you need to write custom NSURLCache.

Step 5.

The result:

Screen Shot 2017-04-22 at 19.56.55

Project on GitHub.

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.

Profile photo of Doszhan Kalibek

Doszhan Kalibek