[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.
This commit is contained in:
Alexsander Akers 2015-07-10 09:11:31 -01:00
parent 8e70c7f003
commit 72b50dc32d
4 changed files with 79 additions and 48 deletions

View File

@ -14,13 +14,8 @@ typedef void (^RCTDataProgressBlock)(int64_t written, int64_t total);
@interface RCTDownloadTaskWrapper : NSObject <NSURLSessionDownloadDelegate>
@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<NSURLSessionDownloadDelegate> delegate;
@end

View File

@ -10,6 +10,42 @@
#import "RCTDownloadTaskWrapper.h"
#import <objc/runtime.h>
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;
{
if (downloadTask.rct_progressBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
if (self.progressBlock != nil) {
self.progressBlock(totalBytesWritten, totalBytesExpectedToWrite);
}
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);
});
}
}

View File

@ -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,7 +24,7 @@ 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
- (RCTImageDownloadCancellationBlock)downloadDataForURL:(NSURL *)url
progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTDataDownloadBlock)block;
@ -33,7 +34,7 @@ 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
- (RCTImageDownloadCancellationBlock)downloadImageForURL:(NSURL *)url
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
@ -46,6 +47,6 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
* same image, only the request that relates to the token passed will be
* cancelled.
*/
- (void)cancelDownload:(id)downloadToken;
- (void)cancelDownload:(RCTImageDownloadCancellationBlock)downloadToken;
@end

View File

@ -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,14 +121,14 @@ 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
- (RCTImageDownloadCancellationBlock)downloadImageForURL:(NSURL *)url
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
@ -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();
}
}