Objective-C: скачать картинку и сохранить в кэше

thailand

Попробуем скачать картинку (достаточно простая задача) и немного рассмотрим механизм кэширования в iOS (есть нюансы).

iOS по умолчанию имеет кэширование и обычно он работает хорошо. Но в некоторых случаях вам возможно понадобится изменить стандартную логику кэширования, быть независимым от настроек кэширования на стороне сервера.

Шаг 1.

Как обычно создаем «Single View Application». Ставим язык Objective-C.

Шаг 2.

Задаем размер хранилищ для кэша в AppDelegate в методе didFinishLaunchingWithOptions:

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

Шаг 3.

Рисуем простой интерфейс.

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];
    
}

В результате мы получим:

Screen Shot 2017-04-22 at 09.07.59

Шаг 4.

Пишем логики загрузки изображения.

- (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";
    });
}

БОльшая часть кода скопирована из предыдущего поста. Имеет смысл заострить внимание на этой строке:

request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

Использую эту опцию я задаю команду, чтобы по возможности всегда использовалось изображение из кэша, каков его возраст не был. Вы можете посмотреть все опции в в официальной документации Apple. Обратите внимание, что на момент публикации данного поста (Апрель 2017) две опции не реализованы (вообще они остаются нереализованным уже несколько лет).

Комментарии в исходнике:

Screen Shot 2017-04-22 at 19.44.27

Из документации:

Screen Shot 2017-04-22 at 21.26.42

Также мы может задать свойство max-age на стороне клиента (обычно оно задается на стороне сервера). Для этого надо написать свой класс наследующийся от NSURLCache.

Шаг 5.

Финальный результат:

Screen Shot 2017-04-22 at 19.56.55

Проект на GitHub-е.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Картинка профиля Doszhan Kalibek

Doszhan Kalibek