Skip to content

Commit 17d4d13

Browse files
authored
Exposes a new option in ASImageDownloaderProtocol to retry image downloads (#1948)
* Exposes a new option in ASImageDownloaderProtocol to retry image downloads At the moment the ASBasicImageDownloader does not automatically retry image downloads if the remote host is unreachable. On the contrary the ASPINRemoteImageDownloader automatically retries. Retrying is something that ultimately clients need to be able to control, for example to fail fast to an alternative image rather than keep retrying for more than one minute while not displaying any image. This change exposes a new option in the ASImageDownloaderProtocol to retry image downloads. It also uses this new option in both ASNetworkImageNode and also ASMultiplexImageNode, setting it to YES to preserve the current behaviour. * Fixes a failing test in ASMultiplexImageNodeTests * Fixes ScreenNode.m ScreenNode.m is implementing ASImageDownloaderProtocol and needs to be fixed to reflect changes in the latter.
1 parent b4a4e2c commit 17d4d13

12 files changed

+46
-11
lines changed

Source/ASMultiplexImageNode.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) {
130130
*/
131131
@property (nonatomic) BOOL shouldRenderProgressImages;
132132

133+
/**
134+
* Specifies whether the underlying image downloader should attempt to retry downloading the image if the remote
135+
* host is unreachable. It will have no effect if the downloader does not support retrying. The default is YES.
136+
*/
137+
@property BOOL shouldRetryImageDownload;
138+
133139
/**
134140
* @abstract The image manager that this image node should use when requesting images from the Photos framework. If this is `nil` (the default), then `PHImageManager.defaultManager` is used.
135141

Source/ASMultiplexImageNode.mm

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,14 @@ - (instancetype)initWithCache:(id<ASImageCacheProtocol>)cache downloader:(id<ASI
176176

177177
_downloaderFlags.downloaderImplementsSetProgress = [downloader respondsToSelector:@selector(setProgressImageBlock:callbackQueue:withDownloadIdentifier:)];
178178
_downloaderFlags.downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)];
179-
_downloaderFlags.downloaderImplementsDownloadWithPriority = [downloader respondsToSelector:@selector(downloadImageWithURL:priority:callbackQueue:downloadProgress:completion:)];
179+
_downloaderFlags.downloaderImplementsDownloadWithPriority = [downloader respondsToSelector:@selector(downloadImageWithURL:shouldRetry:priority:callbackQueue:downloadProgress:completion:)];
180180

181181
_cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)];
182182

183183
_shouldRenderProgressImages = YES;
184184

185185
self.shouldBypassEnsureDisplay = YES;
186+
self.shouldRetryImageDownload = YES;
186187

187188
return self;
188189
}
@@ -862,6 +863,7 @@ - (void)_downloadImageWithIdentifier:(id)imageIdentifier URL:(NSURL *)imageURL c
862863
*/
863864
ASImageDownloaderPriority priority = ASImageDownloaderPriorityWithInterfaceState(strongSelf.interfaceState);
864865
downloadIdentifier = [strongSelf->_downloader downloadImageWithURL:imageURL
866+
shouldRetry:[self shouldRetryImageDownload]
865867
priority:priority
866868
callbackQueue:callbackQueue
867869
downloadProgress:downloadProgressBlock
@@ -877,6 +879,7 @@ - (void)_downloadImageWithIdentifier:(id)imageIdentifier URL:(NSURL *)imageURL c
877879
and their requests are put into the same pool.
878880
*/
879881
downloadIdentifier = [strongSelf->_downloader downloadImageWithURL:imageURL
882+
shouldRetry:[self shouldRetryImageDownload]
880883
callbackQueue:callbackQueue
881884
downloadProgress:downloadProgressBlock
882885
completion:completion];

Source/ASNetworkImageNode.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ NS_ASSUME_NONNULL_BEGIN
116116
*/
117117
@property BOOL shouldRenderProgressImages;
118118

119+
/**
120+
* Specifies whether the underlying image downloader should attempt to retry downloading the image if the remote
121+
* host is unreachable. It will have no effect if the downloader does not support retrying. The default is YES.
122+
*/
123+
@property BOOL shouldRetryImageDownload;
124+
119125
/**
120126
* The image quality of the current image.
121127
*

Source/ASNetworkImageNode.mm

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,15 @@ - (instancetype)initWithCache:(id<ASImageCacheProtocol>)cache downloader:(id<ASI
9696
_networkImageNodeFlags.downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)];
9797
_networkImageNodeFlags.downloaderImplementsAnimatedImage = [downloader respondsToSelector:@selector(animatedImageWithData:)];
9898
_networkImageNodeFlags.downloaderImplementsCancelWithResume = [downloader respondsToSelector:@selector(cancelImageDownloadWithResumePossibilityForIdentifier:)];
99-
_networkImageNodeFlags.downloaderImplementsDownloadWithPriority = [downloader respondsToSelector:@selector(downloadImageWithURL:priority:callbackQueue:downloadProgress:completion:)];
99+
_networkImageNodeFlags.downloaderImplementsDownloadWithPriority = [downloader respondsToSelector:@selector(downloadImageWithURL:shouldRetry:priority:callbackQueue:downloadProgress:completion:)];
100100

101101
_networkImageNodeFlags.cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)];
102102
_networkImageNodeFlags.cacheSupportsSynchronousFetch = [cache respondsToSelector:@selector(synchronouslyFetchedCachedImageWithURL:)];
103103

104104
_networkImageNodeFlags.shouldCacheImage = YES;
105105
_networkImageNodeFlags.shouldRenderProgressImages = YES;
106106
self.shouldBypassEnsureDisplay = YES;
107+
self.shouldRetryImageDownload = YES;
107108

108109
return self;
109110
}
@@ -656,8 +657,8 @@ - (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> ima
656657
ASImageDownloaderPriority priority = ASImageDownloaderPriorityWithInterfaceState(interfaceState);
657658

658659
downloadIdentifier = [self->_downloader downloadImageWithURL:url
659-
660-
priority:priority
660+
shouldRetry:[self shouldRetryImageDownload]
661+
priority:priority
661662
callbackQueue:callbackQueue
662663
downloadProgress:downloadProgress
663664
completion:completion];
@@ -672,6 +673,7 @@ - (void)_downloadImageWithCompletion:(void (^)(id <ASImageContainerProtocol> ima
672673
and their requests are put into the same pool.
673674
*/
674675
downloadIdentifier = [self->_downloader downloadImageWithURL:url
676+
shouldRetry:[self shouldRetryImageDownload]
675677
callbackQueue:callbackQueue
676678
downloadProgress:downloadProgress
677679
completion:completion];

Source/Details/ASBasicImageDownloader.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
2020
* A shared image downloader which can be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes.
2121
* The userInfo provided by this downloader is `nil`.
2222
*
23-
* This is a very basic image downloader. It does not support caching, progressive downloading and likely
23+
* This is a very basic image downloader. It does not support caching, retrying, progressive downloading and likely
2424
* isn't something you should use in production. If you'd like something production ready, see @c ASPINRemoteImageDownloader
2525
*
2626
* @note It is strongly recommended you include PINRemoteImage and use @c ASPINRemoteImageDownloader instead.

Source/Details/ASBasicImageDownloader.mm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,18 +253,21 @@ - (instancetype)_init
253253
#pragma mark ASImageDownloaderProtocol.
254254

255255
- (nullable id)downloadImageWithURL:(NSURL *)URL
256+
shouldRetry:(BOOL)shouldRetry
256257
callbackQueue:(dispatch_queue_t)callbackQueue
257258
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
258259
completion:(ASImageDownloaderCompletion)completion
259260
{
260261
return [self downloadImageWithURL:URL
262+
shouldRetry:shouldRetry
261263
priority:ASImageDownloaderPriorityImminent // maps to default priority
262264
callbackQueue:callbackQueue
263265
downloadProgress:downloadProgress
264266
completion:completion];
265267
}
266268

267269
- (nullable id)downloadImageWithURL:(NSURL *)URL
270+
shouldRetry:(BOOL)shouldRetry
268271
priority:(ASImageDownloaderPriority)priority
269272
callbackQueue:(dispatch_queue_t)callbackQueue
270273
downloadProgress:(ASImageDownloaderProgress)downloadProgress

Source/Details/ASImageProtocols.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
9292
/**
9393
@abstract Downloads an image with the given URL.
9494
@param URL The URL of the image to download.
95+
@param shouldRetry Whether to attempt to retry downloading if the remote host is currently unreachable.
9596
@param callbackQueue The queue to call `downloadProgressBlock` and `completion` on.
9697
@param downloadProgress The block to be invoked when the download of `URL` progresses.
9798
@param completion The block to be invoked when the download has completed, or has failed.
@@ -100,6 +101,7 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
100101
retain the identifier if you wish to use it later.
101102
*/
102103
- (nullable id)downloadImageWithURL:(NSURL *)URL
104+
shouldRetry:(BOOL)shouldRetry
103105
callbackQueue:(dispatch_queue_t)callbackQueue
104106
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
105107
completion:(ASImageDownloaderCompletion)completion;
@@ -117,6 +119,7 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
117119
/**
118120
@abstract Downloads an image with the given URL.
119121
@param URL The URL of the image to download.
122+
@param shouldRetry Whether to attempt to retry downloading if the remote host is currently unreachable.
120123
@param priority The priority at which the image should be downloaded.
121124
@param callbackQueue The queue to call `downloadProgressBlock` and `completion` on.
122125
@param downloadProgress The block to be invoked when the download of `URL` progresses.
@@ -127,6 +130,7 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
127130
retain the identifier if you wish to use it later.
128131
*/
129132
- (nullable id)downloadImageWithURL:(NSURL *)URL
133+
shouldRetry:(BOOL)shouldRetry
130134
priority:(ASImageDownloaderPriority)priority
131135
callbackQueue:(dispatch_queue_t)callbackQueue
132136
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress

Source/Details/ASPINRemoteImageDownloader.mm

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,18 +255,21 @@ - (void)clearFetchedImageFromCacheWithURL:(NSURL *)URL
255255
}
256256

257257
- (nullable id)downloadImageWithURL:(NSURL *)URL
258+
shouldRetry:(BOOL)shouldRetry
258259
callbackQueue:(dispatch_queue_t)callbackQueue
259260
downloadProgress:(ASImageDownloaderProgress)downloadProgress
260261
completion:(ASImageDownloaderCompletion)completion;
261262
{
262263
return [self downloadImageWithURL:URL
264+
shouldRetry:shouldRetry
263265
priority:ASImageDownloaderPriorityImminent // maps to default priority
264266
callbackQueue:callbackQueue
265267
downloadProgress:downloadProgress
266268
completion:completion];
267269
}
268270

269271
- (nullable id)downloadImageWithURL:(NSURL *)URL
272+
shouldRetry:(BOOL)shouldRetry
270273
priority:(ASImageDownloaderPriority)priority
271274
callbackQueue:(dispatch_queue_t)callbackQueue
272275
downloadProgress:(ASImageDownloaderProgress)downloadProgress
@@ -301,8 +304,13 @@ - (nullable id)downloadImageWithURL:(NSURL *)URL
301304
// extra downloads isn't worth the effort of rechecking caches every single time. In order to provide
302305
// feedback to the consumer about whether images are cached, we can't simply make the cache a no-op and
303306
// check the cache as part of this download.
307+
PINRemoteImageManagerDownloadOptions options = PINRemoteImageManagerDownloadOptionsSkipDecode | PINRemoteImageManagerDownloadOptionsIgnoreCache;
308+
if (!shouldRetry) {
309+
options |= PINRemoteImageManagerDownloadOptionsSkipRetry;
310+
}
311+
304312
return [[self sharedPINRemoteImageManager] downloadImageWithURL:URL
305-
options:PINRemoteImageManagerDownloadOptionsSkipDecode | PINRemoteImageManagerDownloadOptionsIgnoreCache
313+
options:options
306314
priority:pi_priority
307315
progressImage:nil
308316
progressDownload:progressDownload

Tests/ASBasicImageDownloaderTests.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ - (void)testAsynchronouslyDownloadTheSameURLTwice
2828
subdirectory:@"TestResources"];
2929

3030
[downloader downloadImageWithURL:URL
31+
shouldRetry:YES
3132
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
3233
downloadProgress:nil
3334
completion:^(id<ASImageContainerProtocol> _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) {
3435
[firstExpectation fulfill];
3536
}];
3637

3738
[downloader downloadImageWithURL:URL
39+
shouldRetry:YES
3840
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
3941
downloadProgress:nil
4042
completion:^(id<ASImageContainerProtocol> _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) {

Tests/ASMultiplexImageNodeTests.mm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,14 +222,14 @@ - (void)testUncachedDownload
222222

223223
// Mock a 50%-progress URL download.
224224
const CGFloat mockedProgress = 0.5;
225-
OCMExpect([mockDownloader downloadImageWithURL:[self _testImageURL] priority:ASImageDownloaderPriorityPreload callbackQueue:OCMOCK_ANY downloadProgress:[OCMArg isNotNil] completion:[OCMArg isNotNil]])
225+
OCMExpect([mockDownloader downloadImageWithURL:[self _testImageURL] shouldRetry:YES priority:ASImageDownloaderPriorityPreload callbackQueue:OCMOCK_ANY downloadProgress:[OCMArg isNotNil] completion:[OCMArg isNotNil]])
226226
.andDo(^(NSInvocation *inv){
227227
// Simulate progress.
228-
ASImageDownloaderProgress progressBlock = [inv as_argumentAtIndexAsObject:5];
228+
ASImageDownloaderProgress progressBlock = [inv as_argumentAtIndexAsObject:6];
229229
progressBlock(mockedProgress);
230230

231231
// Simulate completion.
232-
ASImageDownloaderCompletion completionBlock = [inv as_argumentAtIndexAsObject:6];
232+
ASImageDownloaderCompletion completionBlock = [inv as_argumentAtIndexAsObject:7];
233233
completionBlock([self _testImage], nil, nil, nil);
234234
});
235235

0 commit comments

Comments
 (0)