From aa8dc7f8d9124194e3086aeaf6f6839615ed3057 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 20 Feb 2019 08:47:18 -0800 Subject: [PATCH] Set priority of download data tasks at the right time (#490) Currently the priority is set after a task has been scheduled and potentially sent off. It means the priority may not be picked up at all. This diff adds a new behavior that sets the priority right after the task is created. Also expose an API that allows the priority to be set by Texture. --- CHANGELOG.md | 1 + Source/Classes/PINRemoteImageDownloadQueue.h | 2 +- Source/Classes/PINRemoteImageDownloadQueue.m | 27 ++++++------ Source/Classes/PINRemoteImageDownloadTask.m | 11 ++--- Source/Classes/PINRemoteImageManager.h | 21 +++++++++- Source/Classes/PINRemoteImageManager.m | 43 ++++++++++++-------- Source/Classes/PINURLSessionManager.h | 6 ++- Source/Classes/PINURLSessionManager.m | 7 +++- 8 files changed, 75 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bf431af..2aa94b3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - [cleanup] Use NS_ERROR_ENUM to improve Swift import. [#440](https://github.com/pinterest/PINRemoteImage/pull/440) [Adlai-Holler](https://github.com/Adlai-Holler) - [fixed] Fixes nil session manager configuration. [#460](https://github.com/pinterest/PINRemoteImage/pull/460) [garrettmoon](https://github.com/garrettmoon) - [fixed] Fixes deprecated -defaultImageCache not being called if overridden. [479](https://github.com/pinterest/PINRemoteImage/pull/479) [nguyenhuy](https://github.com/nguyenhuy) +- [new] Add a new API that allows a priority to be set when a new download task is scheduled. [#490](https://github.com/pinterest/PINRemoteImage/pull/490) [nguyenhuy](https://github.com/nguyenhuy) ## 3.0.0 Beta 13 - [new] Support for webp and improved support for GIFs. [#411](https://github.com/pinterest/PINRemoteImage/pull/411) [garrettmoon](https://github.com/garrettmoon) diff --git a/Source/Classes/PINRemoteImageDownloadQueue.h b/Source/Classes/PINRemoteImageDownloadQueue.h index 6a13b6fb..574a2800 100644 --- a/Source/Classes/PINRemoteImageDownloadQueue.h +++ b/Source/Classes/PINRemoteImageDownloadQueue.h @@ -18,7 +18,7 @@ typedef void (^PINRemoteImageDownloadCompletion)(NSURLResponse * _Nullable respo @interface PINRemoteImageDownloadQueue : NSObject -@property (nonatomic, assign) NSUInteger maxNumberOfConcurrentDownloads; +@property (atomic, assign) NSUInteger maxNumberOfConcurrentDownloads; - (instancetype)init NS_UNAVAILABLE; + (PINRemoteImageDownloadQueue *)queueWithMaxConcurrentDownloads:(NSUInteger)maxNumberOfConcurrentDownloads; diff --git a/Source/Classes/PINRemoteImageDownloadQueue.m b/Source/Classes/PINRemoteImageDownloadQueue.m index e4aefd76..07e70f2e 100644 --- a/Source/Classes/PINRemoteImageDownloadQueue.m +++ b/Source/Classes/PINRemoteImageDownloadQueue.m @@ -36,7 +36,7 @@ - (PINRemoteImageDownloadQueue *)initWithMaxConcurrentDownloads:(NSUInteger)maxN { if (self = [super init]) { _maxNumberOfConcurrentDownloads = maxNumberOfConcurrentDownloads; - + _lock = [[PINRemoteLock alloc] initWithName:@"PINRemoteImageDownloadQueue Lock"]; _highPriorityQueuedOperations = [[NSMutableOrderedSet alloc] init]; _defaultPriorityQueuedOperations = [[NSMutableOrderedSet alloc] init]; @@ -68,19 +68,21 @@ - (NSURLSessionDataTask *)addDownloadWithSessionManager:(PINURLSessionManager *) priority:(PINRemoteImageManagerPriority)priority completionHandler:(PINRemoteImageDownloadCompletion)completionHandler { - NSURLSessionDataTask *dataTask = [sessionManager dataTaskWithRequest:request completionHandler:^(NSURLSessionTask *task, NSError *error) { - completionHandler(task.response, error); - [self lock]; - [self->_runningTasks removeObject:task]; - [self unlock]; - - [self scheduleDownloadsIfNeeded]; - }]; - + NSURLSessionDataTask *dataTask = [sessionManager dataTaskWithRequest:request + priority:priority + completionHandler:^(NSURLSessionTask *task, NSError *error) { + completionHandler(task.response, error); + [self lock]; + [self->_runningTasks removeObject:task]; + [self unlock]; + + [self scheduleDownloadsIfNeeded]; + }]; + [self setQueuePriority:priority forTask:dataTask addIfNecessary:YES]; - + [self scheduleDownloadsIfNeeded]; - + return dataTask; } @@ -105,7 +107,6 @@ - (void)scheduleDownloadsIfNeeded [queue removeObjectAtIndex:0]; [task resume]; - [_runningTasks addObject:task]; } [self unlock]; diff --git a/Source/Classes/PINRemoteImageDownloadTask.m b/Source/Classes/PINRemoteImageDownloadTask.m index 28df0294..47bcddef 100644 --- a/Source/Classes/PINRemoteImageDownloadTask.m +++ b/Source/Classes/PINRemoteImageDownloadTask.m @@ -139,9 +139,10 @@ - (void)setPriority:(PINRemoteImageManagerPriority)priority [super setPriority:priority]; if (@available(iOS 8.0, macOS 10.10, tvOS 9.0, watchOS 2.0, *)) { [self.lock lockWithBlock:^{ - if (self->_progressImage.dataTask) { - self->_progressImage.dataTask.priority = dataTaskPriorityWithImageManagerPriority(priority); - [self.manager.urlSessionTaskQueue setQueuePriority:priority forTask:self->_progressImage.dataTask]; + NSURLSessionDataTask *dataTask = self->_progressImage.dataTask; + if (dataTask) { + dataTask.priority = dataTaskPriorityWithImageManagerPriority(priority); + [self.manager.urlSessionTaskQueue setQueuePriority:priority forTask:dataTask]; } }]; } @@ -342,10 +343,6 @@ - (void)scheduleDownloadWithRequest:(NSURLRequest *)request } }]; }]]; - - if (@available(iOS 8.0, macOS 10.10, tvOS 9.0, watchOS 2.0, *)) { - self->_progressImage.dataTask.priority = dataTaskPriorityWithImageManagerPriority(priority); - } }]; } diff --git a/Source/Classes/PINRemoteImageManager.h b/Source/Classes/PINRemoteImageManager.h index 365a85a6..7726f7f0 100644 --- a/Source/Classes/PINRemoteImageManager.h +++ b/Source/Classes/PINRemoteImageManager.h @@ -188,7 +188,6 @@ typedef void(^PINRemoteImageManagerMetrics)(NSURL * __nonnull url, NSURLSession alternativeRepresentationProvider:(nullable id )alternateRepDelegate imageCache:(nullable id)imageCache NS_DESIGNATED_INITIALIZER; - /** Get the shared instance of PINRemoteImageManager @@ -456,6 +455,25 @@ typedef void(^PINRemoteImageManagerMetrics)(NSURL * __nonnull url, NSURLSession progressDownload:(nullable PINRemoteImageManagerProgressDownload)progressDownload completion:(nullable PINRemoteImageManagerImageCompletion)completion; +/** + Download or retrieve from cache the image found at the url and process it before calling completion. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). + + @param url NSURL where the image to download resides. + @param options PINRemoteImageManagerDownloadOptions options with which to fetch the image. + @param priority PINRemoteImageManagerPriority which indicates the priority of the download task. + @param progressImage PINRemoteImageManagerImageCompletion block which will be called to update progress of the image download. + @param progressDownload PINRemoteImageManagerDownloadProgress block which will be called to update progress in bytes of the image download. NOTE: For performance reasons, this block is not called on the main thread every time, if you need to update your UI ensure that you dispatch to the main thread first. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache or downloaded. + + @return An NSUUID which uniquely identifies this request. To be used for canceling requests and verifying that the callback is for the request you expect (see categories for example). + */ +- (nullable NSUUID *)downloadImageWithURL:(nonnull NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + priority:(PINRemoteImageManagerPriority)priority + progressImage:(nullable PINRemoteImageManagerImageCompletion)progressImage + progressDownload:(nullable PINRemoteImageManagerProgressDownload)progressDownload + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + /** Download or retrieve from cache the image found at the url and process it before calling completion. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). @@ -651,5 +669,4 @@ typedef void(^PINRemoteImageManagerMetrics)(NSURL * __nonnull url, NSURLSession @property (nonatomic, readonly) _Nonnull id (^_Nonnull retryStrategyCreationBlock)(void); - @end diff --git a/Source/Classes/PINRemoteImageManager.m b/Source/Classes/PINRemoteImageManager.m index 39e65045..b4dd3069 100644 --- a/Source/Classes/PINRemoteImageManager.m +++ b/Source/Classes/PINRemoteImageManager.m @@ -516,13 +516,9 @@ - (NSUUID *)downloadImageWithURL:(NSURL *)url { return [self downloadImageWithURL:url options:options - priority:PINRemoteImageManagerPriorityDefault - processorKey:nil - processor:nil progressImage:progressImage progressDownload:nil - completion:completion - inputUUID:nil]; + completion:completion]; } - (NSUUID *)downloadImageWithURL:(NSURL *)url @@ -532,13 +528,9 @@ - (NSUUID *)downloadImageWithURL:(NSURL *)url { return [self downloadImageWithURL:url options:options - priority:PINRemoteImageManagerPriorityDefault - processorKey:nil - processor:nil progressImage:nil progressDownload:progressDownload - completion:completion - inputUUID:nil]; + completion:completion]; } - (NSUUID *)downloadImageWithURL:(NSURL *)url @@ -550,6 +542,21 @@ - (NSUUID *)downloadImageWithURL:(NSURL *)url return [self downloadImageWithURL:url options:options priority:PINRemoteImageManagerPriorityDefault + progressImage:progressImage + progressDownload:progressDownload + completion:completion]; +} + +- (nullable NSUUID *)downloadImageWithURL:(nonnull NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + priority:(PINRemoteImageManagerPriority)priority + progressImage:(PINRemoteImageManagerImageCompletion)progressImage + progressDownload:(nullable PINRemoteImageManagerProgressDownload)progressDownload + completion:(nullable PINRemoteImageManagerImageCompletion)completion; +{ + return [self downloadImageWithURL:url + options:options + priority:priority processorKey:nil processor:nil progressImage:progressImage @@ -583,14 +590,14 @@ - (NSUUID *)downloadImageWithURL:(NSURL *)url completion:(PINRemoteImageManagerImageCompletion)completion { return [self downloadImageWithURL:url - options:options - priority:PINRemoteImageManagerPriorityDefault - processorKey:processorKey - processor:processor - progressImage:nil - progressDownload:progressDownload - completion:completion - inputUUID:nil]; + options:options + priority:PINRemoteImageManagerPriorityDefault + processorKey:processorKey + processor:processor + progressImage:nil + progressDownload:progressDownload + completion:completion + inputUUID:nil]; } - (NSUUID *)downloadImageWithURL:(NSURL *)url diff --git a/Source/Classes/PINURLSessionManager.h b/Source/Classes/PINURLSessionManager.h index 1ef452f1..f632b7b4 100644 --- a/Source/Classes/PINURLSessionManager.h +++ b/Source/Classes/PINURLSessionManager.h @@ -8,6 +8,8 @@ #import +#import "PINRemoteImageManager.h" + extern NSErrorDomain _Nonnull const PINURLErrorDomain; @protocol PINURLSessionManagerDelegate @@ -29,7 +31,9 @@ typedef void (^PINURLSessionDataTaskCompletion)(NSURLSessionTask * _Nonnull task - (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration; -- (nonnull NSURLSessionDataTask *)dataTaskWithRequest:(nonnull NSURLRequest *)request completionHandler:(nonnull PINURLSessionDataTaskCompletion)completionHandler; +- (nonnull NSURLSessionDataTask *)dataTaskWithRequest:(nonnull NSURLRequest *)request + priority:(PINRemoteImageManagerPriority)priority + completionHandler:(nonnull PINURLSessionDataTaskCompletion)completionHandler; - (void)invalidateSessionAndCancelTasks; diff --git a/Source/Classes/PINURLSessionManager.m b/Source/Classes/PINURLSessionManager.m index b82bdc33..13a6ea26 100644 --- a/Source/Classes/PINURLSessionManager.m +++ b/Source/Classes/PINURLSessionManager.m @@ -48,10 +48,15 @@ - (void)invalidateSessionAndCancelTasks [self unlock]; } -- (nonnull NSURLSessionDataTask *)dataTaskWithRequest:(nonnull NSURLRequest *)request completionHandler:(nonnull PINURLSessionDataTaskCompletion)completionHandler +- (nonnull NSURLSessionDataTask *)dataTaskWithRequest:(nonnull NSURLRequest *)request + priority:(PINRemoteImageManagerPriority)priority + completionHandler:(nonnull PINURLSessionDataTaskCompletion)completionHandler { [self lock]; NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request]; + if (@available(iOS 8.0, macOS 10.10, tvOS 9.0, watchOS 2.0, *)) { + dataTask.priority = dataTaskPriorityWithImageManagerPriority(priority); + } if (completionHandler) { [self.completions setObject:completionHandler forKey:@(dataTask.taskIdentifier)]; }