From 72b50dc32d3d13e5fd5634aeafde8e74ea1d6969 Mon Sep 17 00:00:00 2001 From: Alexsander Akers Date: Fri, 10 Jul 2015 09:11:31 -0100 Subject: [PATCH] [React Native] Update image downloader Summary: Change `RCTImageDownloader` so it stores the `RCTDownloadTaskWrapper` for reuse. Modify `RCTDownloadTaskWrapper` to use associated objects to store the completion/progress blocks. --- Libraries/Image/RCTDownloadTaskWrapper.h | 5 -- Libraries/Image/RCTDownloadTaskWrapper.m | 64 ++++++++++++++++++------ Libraries/Image/RCTImageDownloader.h | 23 +++++---- Libraries/Image/RCTImageDownloader.m | 35 ++++++------- 4 files changed, 79 insertions(+), 48 deletions(-) diff --git a/Libraries/Image/RCTDownloadTaskWrapper.h b/Libraries/Image/RCTDownloadTaskWrapper.h index e1660218c..41337fac7 100644 --- a/Libraries/Image/RCTDownloadTaskWrapper.h +++ b/Libraries/Image/RCTDownloadTaskWrapper.h @@ -14,13 +14,8 @@ typedef void (^RCTDataProgressBlock)(int64_t written, int64_t total); @interface RCTDownloadTaskWrapper : NSObject -@property (copy, nonatomic) RCTDataCompletionBlock completionBlock; -@property (copy, nonatomic) RCTDataProgressBlock progressBlock; - - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration delegateQueue:(NSOperationQueue *)delegateQueue; - (NSURLSessionDownloadTask *)downloadData:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock completionBlock:(RCTDataCompletionBlock)completionBlock; -@property (nonatomic, assign) id delegate; - @end diff --git a/Libraries/Image/RCTDownloadTaskWrapper.m b/Libraries/Image/RCTDownloadTaskWrapper.m index 0d5f467e4..21c5f6d7a 100644 --- a/Libraries/Image/RCTDownloadTaskWrapper.m +++ b/Libraries/Image/RCTDownloadTaskWrapper.m @@ -10,6 +10,42 @@ #import "RCTDownloadTaskWrapper.h" +#import + +static void *const RCTDownloadTaskWrapperCompletionBlockKey = (void *)&RCTDownloadTaskWrapperCompletionBlockKey; +static void *const RCTDownloadTaskWrapperProgressBlockKey = (void *)&RCTDownloadTaskWrapperProgressBlockKey; + +@interface NSURLSessionTask (RCTDownloadTaskWrapper) + +@property (nonatomic, copy, setter=rct_setCompletionBlock:) RCTDataCompletionBlock rct_completionBlock; +@property (nonatomic, copy, setter=rct_setProgressBlock:) RCTDataProgressBlock rct_progressBlock; + +@end + +@implementation NSURLSessionTask (RCTDownloadTaskWrapper) + +- (RCTDataCompletionBlock)rct_completionBlock +{ + return objc_getAssociatedObject(self, RCTDownloadTaskWrapperCompletionBlockKey); +} + +- (void)rct_setCompletionBlock:(RCTDataCompletionBlock)completionBlock +{ + objc_setAssociatedObject(self, RCTDownloadTaskWrapperCompletionBlockKey, completionBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (RCTDataProgressBlock)rct_progressBlock +{ + return objc_getAssociatedObject(self, RCTDownloadTaskWrapperProgressBlockKey); +} + +- (void)rct_setProgressBlock:(RCTDataProgressBlock)progressBlock +{ + objc_setAssociatedObject(self, RCTDownloadTaskWrapperProgressBlockKey, progressBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +@end + @implementation RCTDownloadTaskWrapper { NSURLSession *_URLSession; @@ -26,10 +62,10 @@ - (NSURLSessionDownloadTask *)downloadData:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock completionBlock:(RCTDataCompletionBlock)completionBlock { - self.completionBlock = completionBlock; - self.progressBlock = progressBlock; - NSURLSessionDownloadTask *task = [_URLSession downloadTaskWithURL:url completionHandler:nil]; + task.rct_completionBlock = completionBlock; + task.rct_progressBlock = progressBlock; + [task resume]; return task; } @@ -38,30 +74,28 @@ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { - if (self.completionBlock) { + if (downloadTask.rct_completionBlock) { NSData *data = [NSData dataWithContentsOfURL:location]; dispatch_async(dispatch_get_main_queue(), ^{ - self.completionBlock(downloadTask.response, data, nil); + downloadTask.rct_completionBlock(downloadTask.response, data, nil); }); } } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)didWriteData totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite; { - dispatch_async(dispatch_get_main_queue(), ^{ - if (self.progressBlock != nil) { - self.progressBlock(totalBytesWritten, totalBytesExpectedToWrite); - } - }); + if (downloadTask.rct_progressBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + downloadTask.rct_progressBlock(totalBytesWritten, totalBytesExpectedToWrite); + }); + } } -- (void)URLSession:(NSURLSession *)session - task:(NSURLSessionTask *)task -didCompleteWithError:(NSError *)error +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { - if (error && self.completionBlock) { + if (error && task.rct_completionBlock) { dispatch_async(dispatch_get_main_queue(), ^{ - self.completionBlock(NULL, NULL, error); + task.rct_completionBlock(nil, nil, error); }); } } diff --git a/Libraries/Image/RCTImageDownloader.h b/Libraries/Image/RCTImageDownloader.h index 182913830..44b2b7369 100644 --- a/Libraries/Image/RCTImageDownloader.h +++ b/Libraries/Image/RCTImageDownloader.h @@ -13,6 +13,7 @@ typedef void (^RCTDataDownloadBlock)(NSData *data, NSError *error); typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error); +typedef void (^RCTImageDownloadCancellationBlock)(void); @interface RCTImageDownloader : NSObject @@ -23,9 +24,9 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error); * will not be executed on the same thread you called the method from, nor on * the main thread. Returns a token that can be used to cancel the download. */ -- (id)downloadDataForURL:(NSURL *)url - progressBlock:(RCTDataProgressBlock)progressBlock - block:(RCTDataDownloadBlock)block; +- (RCTImageDownloadCancellationBlock)downloadDataForURL:(NSURL *)url + progressBlock:(RCTDataProgressBlock)progressBlock + block:(RCTDataDownloadBlock)block; /** * Downloads an image and decompresses it a the size specified. The compressed @@ -33,19 +34,19 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error); * will not be executed on the same thread you called the method from, nor on * the main thread. Returns a token that can be used to cancel the download. */ -- (id)downloadImageForURL:(NSURL *)url - size:(CGSize)size - scale:(CGFloat)scale - resizeMode:(UIViewContentMode)resizeMode - backgroundColor:(UIColor *)backgroundColor - progressBlock:(RCTDataProgressBlock)progressBlock - block:(RCTImageDownloadBlock)block; +- (RCTImageDownloadCancellationBlock)downloadImageForURL:(NSURL *)url + size:(CGSize)size + scale:(CGFloat)scale + resizeMode:(UIViewContentMode)resizeMode + backgroundColor:(UIColor *)backgroundColor + progressBlock:(RCTDataProgressBlock)progressBlock + block:(RCTImageDownloadBlock)block; /** * Cancel an in-flight download. If multiple requets have been made for the * same image, only the request that relates to the token passed will be * cancelled. */ -- (void)cancelDownload:(id)downloadToken; +- (void)cancelDownload:(RCTImageDownloadCancellationBlock)downloadToken; @end diff --git a/Libraries/Image/RCTImageDownloader.m b/Libraries/Image/RCTImageDownloader.m index e45c41242..fc519c4f2 100644 --- a/Libraries/Image/RCTImageDownloader.m +++ b/Libraries/Image/RCTImageDownloader.m @@ -23,6 +23,7 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode); NSURLCache *_cache; dispatch_queue_t _processingQueue; NSMutableDictionary *_pendingBlocks; + RCTDownloadTaskWrapper *_downloadTaskWrapper; } + (RCTImageDownloader *)sharedInstance @@ -41,22 +42,22 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode); _cache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 diskCapacity:200 * 1024 * 1024 diskPath:@"React/RCTImageDownloader"]; _processingQueue = dispatch_queue_create("com.facebook.React.DownloadProcessingQueue", DISPATCH_QUEUE_SERIAL); _pendingBlocks = [[NSMutableDictionary alloc] init]; + + NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; + _downloadTaskWrapper = [[RCTDownloadTaskWrapper alloc] initWithSessionConfiguration:config delegateQueue:nil]; } return self; } -- (id)_downloadDataForURL:(NSURL *)url progressBlock:progressBlock block:(RCTCachedDataDownloadBlock)block +- (RCTImageDownloadCancellationBlock)_downloadDataForURL:(NSURL *)url progressBlock:progressBlock block:(RCTCachedDataDownloadBlock)block { - NSString *cacheKey = url.absoluteString; + NSString *const cacheKey = url.absoluteString; __block BOOL cancelled = NO; __block NSURLSessionDownloadTask *task = nil; - NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; - RCTDownloadTaskWrapper *downloadTaskWrapper = [[RCTDownloadTaskWrapper alloc] initWithSessionConfiguration:config delegateQueue:nil]; - - dispatch_block_t cancel = ^{ + RCTImageDownloadCancellationBlock cancel = ^{ cancelled = YES; dispatch_async(_processingQueue, ^{ @@ -90,7 +91,7 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode); }; NSURLRequest *request = [NSURLRequest requestWithURL:url]; - task = [downloadTaskWrapper downloadData:url progressBlock:progressBlock completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) { + task = [_downloadTaskWrapper downloadData:url progressBlock:progressBlock completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) { if (!cancelled) { runBlocks(NO, data, error); } @@ -120,20 +121,20 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode); return [cancel copy]; } -- (id)downloadDataForURL:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock block:(RCTDataDownloadBlock)block +- (RCTImageDownloadCancellationBlock)downloadDataForURL:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock block:(RCTDataDownloadBlock)block { return [self _downloadDataForURL:url progressBlock:progressBlock block:^(BOOL cached, NSData *data, NSError *error) { block(data, error); }]; } -- (id)downloadImageForURL:(NSURL *)url - size:(CGSize)size - scale:(CGFloat)scale - resizeMode:(UIViewContentMode)resizeMode - backgroundColor:(UIColor *)backgroundColor - progressBlock:(RCTDataProgressBlock)progressBlock - block:(RCTImageDownloadBlock)block +- (RCTImageDownloadCancellationBlock)downloadImageForURL:(NSURL *)url + size:(CGSize)size + scale:(CGFloat)scale + resizeMode:(UIViewContentMode)resizeMode + backgroundColor:(UIColor *)backgroundColor + progressBlock:(RCTDataProgressBlock)progressBlock + block:(RCTImageDownloadBlock)block { return [self downloadDataForURL:url progressBlock:progressBlock block:^(NSData *data, NSError *error) { if (!data || error) { @@ -182,10 +183,10 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode); }]; } -- (void)cancelDownload:(id)downloadToken +- (void)cancelDownload:(RCTImageDownloadCancellationBlock)downloadToken { if (downloadToken) { - ((dispatch_block_t)downloadToken)(); + downloadToken(); } }