Authored by Steven Chan

New option SDWebImageRefreshCached

Even if the image is cached, fetch the URL again anyway. When set,
NSURLCache is enabled in the downloader via the new option
SDWebImageDownloaderEnableNSURLCache.

NSURLCache will handle the protocol caching while SDWebImage remains
useful for offline images.

This option helps deal with images changing behind the same request URL,
e.g. Facebook graph api profile pics where the request URL
https://graph.facebook.com/[userid]/picture returns a redirect to the
actual profile image.

If a cached image exists, the completion block is called once with the
cached image and again with the final image.
@@ -13,7 +13,8 @@ @@ -13,7 +13,8 @@
13 typedef enum 13 typedef enum
14 { 14 {
15 SDWebImageDownloaderLowPriority = 1 << 0, 15 SDWebImageDownloaderLowPriority = 1 << 0,
16 - SDWebImageDownloaderProgressiveDownload = 1 << 1 16 + SDWebImageDownloaderProgressiveDownload = 1 << 1,
  17 + SDWebImageDownloaderEnableNSURLCache = 1 << 2
17 } SDWebImageDownloaderOptions; 18 } SDWebImageDownloaderOptions;
18 19
19 typedef enum 20 typedef enum
@@ -120,7 +120,7 @@ static NSString *const kCompletedCallbackKey = @"completed"; @@ -120,7 +120,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
120 [self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^ 120 [self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^
121 { 121 {
122 // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests 122 // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests
123 - NSMutableURLRequest *request = [NSMutableURLRequest.alloc initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15]; 123 + NSMutableURLRequest *request = [NSMutableURLRequest.alloc initWithURL:url cachePolicy:(options & SDWebImageDownloaderEnableNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:15];
124 request.HTTPShouldHandleCookies = NO; 124 request.HTTPShouldHandleCookies = NO;
125 request.HTTPShouldUsePipelining = YES; 125 request.HTTPShouldUsePipelining = YES;
126 request.allHTTPHeaderFields = wself.HTTPHeaders; 126 request.allHTTPHeaderFields = wself.HTTPHeaders;
@@ -31,7 +31,14 @@ typedef enum @@ -31,7 +31,14 @@ typedef enum
31 * This flag enables progressive download, the image is displayed progressively during download as a browser would do. 31 * This flag enables progressive download, the image is displayed progressively during download as a browser would do.
32 * By default, the image is only displayed once completely downloaded. 32 * By default, the image is only displayed once completely downloaded.
33 */ 33 */
34 - SDWebImageProgressiveDownload = 1 << 3 34 + SDWebImageProgressiveDownload = 1 << 3,
  35 + /**
  36 + * Even if the image is cached, fetch the URL again anyway. When set, NSURLCache is enabled in the downloader.
  37 + * NSURLCache will handle the protocol caching logic while SDWebImage remains useful for offline images.
  38 + * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.
  39 + * If a cached image exists, the completion block is called once with the cached image and again with the final image.
  40 + */
  41 + SDWebImageRefreshCached = 1 << 4
35 } SDWebImageOptions; 42 } SDWebImageOptions;
36 43
37 typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType); 44 typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType);
@@ -97,16 +97,21 @@ @@ -97,16 +97,21 @@
97 if (image) 97 if (image)
98 { 98 {
99 completedBlock(image, nil, cacheType, YES); 99 completedBlock(image, nil, cacheType, YES);
100 - @synchronized(self.runningOperations)  
101 - {  
102 - [self.runningOperations removeObject:operation]; 100 + if (!(options & SDWebImageRefreshCached)) {
  101 + @synchronized(self.runningOperations)
  102 + {
  103 + [self.runningOperations removeObject:operation];
  104 + }
103 } 105 }
104 } 106 }
105 - else if (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]) 107 +
  108 + if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]))
106 { 109 {
  110 + // download if no image or requested to refresh anyway, and download allowed by delegate
107 SDWebImageDownloaderOptions downloaderOptions = 0; 111 SDWebImageDownloaderOptions downloaderOptions = 0;
108 if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority; 112 if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
109 if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload; 113 if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
  114 + if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderEnableNSURLCache;
110 __block id<SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) 115 __block id<SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished)
111 { 116 {
112 if (weakOperation.cancelled) 117 if (weakOperation.cancelled)
@@ -167,7 +172,7 @@ @@ -167,7 +172,7 @@
167 }]; 172 }];
168 operation.cancelBlock = ^{[subOperation cancel];}; 173 operation.cancelBlock = ^{[subOperation cancel];};
169 } 174 }
170 - else 175 + else if (!image)
171 { 176 {
172 // Image not in cache and download disallowed by delegate 177 // Image not in cache and download disallowed by delegate
173 completedBlock(nil, nil, SDImageCacheTypeNone, YES); 178 completedBlock(nil, nil, SDImageCacheTypeNone, YES);