Refactored RCTImageDownloader to use RCTNetworking instead of a separate download system
This commit is contained in:
parent
3cff9be3d1
commit
1d852624fd
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef void (^RCTDataCompletionBlock)(NSURLResponse *response, NSData *data, NSError *error);
|
||||
typedef void (^RCTDataProgressBlock)(int64_t written, int64_t total);
|
||||
|
||||
@interface RCTDownloadTaskWrapper : NSObject <NSURLSessionDownloadDelegate>
|
||||
|
||||
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration delegateQueue:(NSOperationQueue *)delegateQueue;
|
||||
|
||||
- (NSURLSessionDownloadTask *)downloadData:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock completionBlock:(RCTDataCompletionBlock)completionBlock;
|
||||
|
||||
@end
|
|
@ -1,98 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
|
||||
#import "RCTDownloadTaskWrapper.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface NSObject (RCTDownloadTaskWrapper)
|
||||
|
||||
@property (nonatomic, copy) RCTDataCompletionBlock reactCompletionBlock;
|
||||
@property (nonatomic, copy) RCTDataProgressBlock reactProgressBlock;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSObject (RCTDownloadTaskWrapper)
|
||||
|
||||
- (RCTDataCompletionBlock)reactCompletionBlock
|
||||
{
|
||||
return objc_getAssociatedObject(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setReactCompletionBlock:(RCTDataCompletionBlock)completionBlock
|
||||
{
|
||||
objc_setAssociatedObject(self, @selector(reactCompletionBlock), completionBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||||
}
|
||||
|
||||
- (RCTDataProgressBlock)reactProgressBlock
|
||||
{
|
||||
return objc_getAssociatedObject(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setReactProgressBlock:(RCTDataProgressBlock)progressBlock
|
||||
{
|
||||
objc_setAssociatedObject(self, @selector(reactProgressBlock), progressBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTDownloadTaskWrapper
|
||||
{
|
||||
NSURLSession *_URLSession;
|
||||
}
|
||||
|
||||
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration delegateQueue:(NSOperationQueue *)delegateQueue
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_URLSession = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSURLSessionDownloadTask *)downloadData:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock completionBlock:(RCTDataCompletionBlock)completionBlock
|
||||
{
|
||||
NSURLSessionDownloadTask *task = [_URLSession downloadTaskWithURL:url];
|
||||
task.reactCompletionBlock = completionBlock;
|
||||
task.reactProgressBlock = progressBlock;
|
||||
return task;
|
||||
}
|
||||
|
||||
#pragma mark - NSURLSessionTaskDelegate methods
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
|
||||
{
|
||||
if (downloadTask.reactCompletionBlock) {
|
||||
NSData *data = [NSData dataWithContentsOfURL:location];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
downloadTask.reactCompletionBlock(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.reactProgressBlock) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
downloadTask.reactProgressBlock(totalBytesWritten, totalBytesExpectedToWrite);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
||||
{
|
||||
if (error && task.reactCompletionBlock) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
task.reactCompletionBlock(nil, nil, error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -7,7 +7,6 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
03559E7F1B064DAF00730281 /* RCTDownloadTaskWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */; };
|
||||
1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTImageView.m */; };
|
||||
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */; };
|
||||
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; };
|
||||
|
@ -33,8 +32,6 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
03559E7D1B064D3A00730281 /* RCTDownloadTaskWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTDownloadTaskWrapper.h; sourceTree = "<group>"; };
|
||||
03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDownloadTaskWrapper.m; sourceTree = "<group>"; };
|
||||
1304D5A71AA8C4A30002E2BE /* RCTImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageView.h; sourceTree = "<group>"; };
|
||||
1304D5A81AA8C4A30002E2BE /* RCTImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageView.m; sourceTree = "<group>"; };
|
||||
1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageViewManager.h; sourceTree = "<group>"; };
|
||||
|
@ -74,8 +71,6 @@
|
|||
children = (
|
||||
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */,
|
||||
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */,
|
||||
03559E7D1B064D3A00730281 /* RCTDownloadTaskWrapper.h */,
|
||||
03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */,
|
||||
1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */,
|
||||
1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */,
|
||||
58B511891A9E6BD600147676 /* RCTImageDownloader.h */,
|
||||
|
@ -172,7 +167,6 @@
|
|||
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */,
|
||||
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
|
||||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
|
||||
03559E7F1B064DAF00730281 /* RCTDownloadTaskWrapper.m in Sources */,
|
||||
1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */,
|
||||
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */,
|
||||
);
|
||||
|
@ -275,6 +269,7 @@
|
|||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
"$(SRCROOT)/../Network/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -295,6 +290,7 @@
|
|||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
"$(SRCROOT)/../Network/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
|
|
@ -9,24 +9,10 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTDownloadTaskWrapper.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTImageLoader.h"
|
||||
|
||||
typedef void (^RCTDataDownloadBlock)(NSData *data, NSError *error);
|
||||
typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
|
||||
typedef void (^RCTImageDownloadCancellationBlock)(void);
|
||||
|
||||
@interface RCTImageDownloader : NSObject
|
||||
|
||||
+ (RCTImageDownloader *)sharedInstance;
|
||||
|
||||
/**
|
||||
* Downloads a block of raw data and returns it. Note that the callback block
|
||||
* 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.
|
||||
*/
|
||||
- (RCTImageDownloadCancellationBlock)downloadDataForURL:(NSURL *)url
|
||||
progressBlock:(RCTDataProgressBlock)progressBlock
|
||||
block:(RCTDataDownloadBlock)block;
|
||||
@interface RCTImageDownloader : NSObject <RCTBridgeModule>
|
||||
|
||||
/**
|
||||
* Downloads an image and decompresses it a the size specified. The compressed
|
||||
|
@ -34,13 +20,17 @@ typedef void (^RCTImageDownloadCancellationBlock)(void);
|
|||
* 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.
|
||||
*/
|
||||
- (RCTImageDownloadCancellationBlock)downloadImageForURL:(NSURL *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
tintColor:(UIColor *)tintColor
|
||||
backgroundColor:(UIColor *)backgroundColor
|
||||
progressBlock:(RCTDataProgressBlock)progressBlock
|
||||
block:(RCTImageDownloadBlock)block;
|
||||
- (RCTImageLoaderCancellationBlock)downloadImageForURL:(NSURL *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTBridge (RCTImageDownloader)
|
||||
|
||||
@property (nonatomic, readonly) RCTImageDownloader *imageDownloader;
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,14 +9,12 @@
|
|||
|
||||
#import "RCTImageDownloader.h"
|
||||
|
||||
#import "RCTDownloadTaskWrapper.h"
|
||||
#import "RCTGIFImage.h"
|
||||
#import "RCTImageUtils.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTNetworking.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSURLResponse *response,
|
||||
NSData *data, NSError *error);
|
||||
|
||||
CGSize RCTTargetSizeForClipRect(CGRect);
|
||||
CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
|
||||
|
||||
|
@ -24,10 +22,12 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
|
|||
{
|
||||
NSURLCache *_cache;
|
||||
dispatch_queue_t _processingQueue;
|
||||
NSMutableDictionary *_pendingBlocks;
|
||||
RCTDownloadTaskWrapper *_downloadTaskWrapper;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
+ (RCTImageDownloader *)sharedInstance
|
||||
{
|
||||
static RCTImageDownloader *sharedInstance;
|
||||
|
@ -43,122 +43,88 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
|
|||
if ((self = [super init])) {
|
||||
_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;
|
||||
}
|
||||
|
||||
- (RCTImageDownloadCancellationBlock)_downloadDataForURL:(NSURL *)url
|
||||
progressBlock:progressBlock
|
||||
block:(RCTCachedDataDownloadBlock)block
|
||||
/**
|
||||
* Downloads a block of raw data and returns it. Note that the callback block
|
||||
* 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.
|
||||
*/
|
||||
- (RCTImageLoaderCancellationBlock)downloadDataForURL:(NSURL *)url
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
|
||||
{
|
||||
NSString *const cacheKey = url.absoluteString;
|
||||
if (![_bridge respondsToSelector:NSSelectorFromString(@"networking")]) {
|
||||
RCTLogError(@"You need to import the RCTNetworking library in order to download remote images.");
|
||||
return ^{};
|
||||
}
|
||||
|
||||
__block BOOL cancelled = NO;
|
||||
__block NSURLSessionDownloadTask *task = nil;
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
RCTURLRequestCompletionBlock runBlocks = ^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
|
||||
RCTImageDownloadCancellationBlock cancel = ^{
|
||||
cancelled = YES;
|
||||
if (!error && [response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
if (httpResponse.statusCode != 200) {
|
||||
data = nil;
|
||||
error = [[NSError alloc] initWithDomain:NSURLErrorDomain
|
||||
code:httpResponse.statusCode
|
||||
userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
dispatch_async(_processingQueue, ^{
|
||||
NSMutableArray *pendingBlocks = self->_pendingBlocks[cacheKey];
|
||||
[pendingBlocks removeObject:block];
|
||||
completionBlock(error, data);
|
||||
});
|
||||
|
||||
if (task) {
|
||||
[task cancel];
|
||||
task = nil;
|
||||
}
|
||||
};
|
||||
|
||||
dispatch_async(_processingQueue, ^{
|
||||
NSMutableArray *pendingBlocks = _pendingBlocks[cacheKey];
|
||||
if (pendingBlocks) {
|
||||
[pendingBlocks addObject:block];
|
||||
} else {
|
||||
_pendingBlocks[cacheKey] = [NSMutableArray arrayWithObject:block];
|
||||
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
RCTCachedDataDownloadBlock runBlocks = ^(BOOL cached, NSURLResponse *response, NSData *data, NSError *error) {
|
||||
|
||||
if (!error && [response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
if (httpResponse.statusCode != 200) {
|
||||
data = nil;
|
||||
error = [[NSError alloc] initWithDomain:NSURLErrorDomain
|
||||
code:httpResponse.statusCode
|
||||
userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
dispatch_async(_processingQueue, ^{
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
NSArray *blocks = strongSelf->_pendingBlocks[cacheKey];
|
||||
[strongSelf->_pendingBlocks removeObjectForKey:cacheKey];
|
||||
for (RCTCachedDataDownloadBlock downloadBlock in blocks) {
|
||||
downloadBlock(cached, response, data, error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
NSURLRequest *request = [NSURLRequest requestWithURL:url];
|
||||
task = [_downloadTaskWrapper downloadData:url progressBlock:progressBlock completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
|
||||
if (!cancelled) {
|
||||
runBlocks(NO, response, data, error);
|
||||
}
|
||||
|
||||
if (response && !error) {
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
|
||||
[strongSelf->_cache storeCachedResponse:cachedResponse forRequest:request];
|
||||
}
|
||||
task = nil;
|
||||
}];
|
||||
|
||||
NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cachedResponse) {
|
||||
runBlocks(YES, cachedResponse.response, cachedResponse.data, nil);
|
||||
} else {
|
||||
[task resume];
|
||||
}
|
||||
NSURLRequest *request = [NSURLRequest requestWithURL:url];
|
||||
{
|
||||
NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
|
||||
if (cachedResponse) {
|
||||
runBlocks(cachedResponse.response, cachedResponse.data, nil);
|
||||
return ^{};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return [cancel copy];
|
||||
}
|
||||
|
||||
- (RCTImageDownloadCancellationBlock)downloadDataForURL:(NSURL *)url
|
||||
progressBlock:(RCTDataProgressBlock)progressBlock
|
||||
block:(RCTDataDownloadBlock)block
|
||||
{
|
||||
return [self _downloadDataForURL:url progressBlock:progressBlock block:^(BOOL cached, NSURLResponse *response, NSData *data, NSError *error) {
|
||||
block(data, error);
|
||||
RCTDownloadTask *task = [_bridge.networking downloadTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
if (response && !error) {
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
|
||||
[strongSelf->_cache storeCachedResponse:cachedResponse forRequest:request];
|
||||
}
|
||||
runBlocks(response, data, error);
|
||||
}];
|
||||
if (progressBlock) {
|
||||
task.downloadProgressBlock = progressBlock;
|
||||
}
|
||||
return ^{ [task cancel]; };
|
||||
}
|
||||
|
||||
- (RCTImageDownloadCancellationBlock)downloadImageForURL:(NSURL *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
tintColor:(UIColor *)tintColor
|
||||
backgroundColor:(UIColor *)backgroundColor
|
||||
progressBlock:(RCTDataProgressBlock)progressBlock
|
||||
block:(RCTImageDownloadBlock)block
|
||||
- (RCTImageLoaderCancellationBlock)downloadImageForURL:(NSURL *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
|
||||
{
|
||||
scale = scale ?: RCTScreenScale();
|
||||
|
||||
return [self downloadDataForURL:url progressBlock:progressBlock block:^(NSData *data, NSError *error) {
|
||||
return [self downloadDataForURL:url progressBlock:progressBlock completionBlock:^(NSError *error, id data) {
|
||||
|
||||
if (!data || error) {
|
||||
block(nil, error);
|
||||
completionBlock(error, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if ([url.path.lowercaseString hasSuffix:@".gif"]) {
|
||||
id image = RCTGIFImageWithData(data);
|
||||
if (!image && !error) {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Unable to load GIF image: %@", url];
|
||||
error = RCTErrorWithMessage(errorMessage);
|
||||
}
|
||||
completionBlock(error, image);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -169,35 +135,25 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
|
|||
CGRect imageRect = RCTClipRect(image.size, scale, size, scale, resizeMode);
|
||||
CGSize destSize = RCTTargetSizeForClipRect(imageRect);
|
||||
|
||||
// Opacity optimizations
|
||||
UIColor *blendColor = nil;
|
||||
BOOL opaque = !RCTImageHasAlpha(image.CGImage);
|
||||
if (!opaque && backgroundColor) {
|
||||
CGFloat alpha;
|
||||
[backgroundColor getRed:NULL green:NULL blue:NULL alpha:&alpha];
|
||||
if (alpha > 0.999) { // no benefit to blending if background is translucent
|
||||
opaque = YES;
|
||||
blendColor = backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
// Decompress image at required size
|
||||
BOOL opaque = !RCTImageHasAlpha(image.CGImage);
|
||||
UIGraphicsBeginImageContextWithOptions(destSize, opaque, scale);
|
||||
if (blendColor) {
|
||||
[blendColor setFill];
|
||||
UIRectFill((CGRect){CGPointZero, destSize});
|
||||
}
|
||||
if (tintColor) {
|
||||
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
[tintColor setFill];
|
||||
}
|
||||
[image drawInRect:imageRect];
|
||||
image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
||||
block(image, nil);
|
||||
completionBlock(nil, image);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTBridge (RCTImageDownloader)
|
||||
|
||||
- (RCTImageDownloader *)imageDownloader
|
||||
{
|
||||
return self.modules[RCTBridgeModuleNameForClass([RCTImageDownloader class])];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
@class ALAssetsLibrary;
|
||||
|
||||
typedef void (^RCTImageLoaderProgressBlock)(int64_t written, int64_t total);
|
||||
typedef void (^RCTImageLoaderProgressBlock)(int64_t progress, int64_t total);
|
||||
typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, id image /* UIImage or CAAnimation */);
|
||||
typedef void (^RCTImageLoaderCancellationBlock)(void);
|
||||
|
||||
|
@ -34,8 +34,8 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
|
|||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progress
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)completion;
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock;
|
||||
|
||||
/**
|
||||
* Is the specified image tag an asset library image?
|
||||
|
|
|
@ -123,8 +123,8 @@ static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation,
|
|||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progress
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)completion
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
|
||||
{
|
||||
if ([imageTag hasPrefix:@"assets-library://"]) {
|
||||
[[self assetsLibrary] assetForURL:[NSURL URLWithString:imageTag] resultBlock:^(ALAsset *asset) {
|
||||
|
@ -151,18 +151,18 @@ static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation,
|
|||
image = RCTScaledImageForAsset(representation, size, scale, resizeMode, &error);
|
||||
}
|
||||
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, image);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageTag];
|
||||
NSError *error = RCTErrorWithMessage(errorText);
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, nil);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, nil);
|
||||
}
|
||||
} failureBlock:^(NSError *loadError) {
|
||||
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageTag, loadError];
|
||||
NSError *error = RCTErrorWithMessage(errorText);
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, nil);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, nil);
|
||||
}];
|
||||
return ^{};
|
||||
} else if ([imageTag hasPrefix:@"ph://"]) {
|
||||
|
@ -175,7 +175,7 @@ static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation,
|
|||
if (results.count == 0) {
|
||||
NSString *errorText = [NSString stringWithFormat:@"Failed to fetch PHAsset with local identifier %@ with no error message.", phAssetID];
|
||||
NSError *error = RCTErrorWithMessage(errorText);
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, nil);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, nil);
|
||||
return ^{};
|
||||
}
|
||||
|
||||
|
@ -200,11 +200,11 @@ static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation,
|
|||
}
|
||||
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:targetSize contentMode:contentMode options:imageOptions resultHandler:^(UIImage *result, NSDictionary *info) {
|
||||
if (result) {
|
||||
RCTDispatchCallbackOnMainQueue(completion, nil, result);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, nil, result);
|
||||
} else {
|
||||
NSString *errorText = [NSString stringWithFormat:@"Failed to load PHAsset with local identifier %@ with no error message.", phAssetID];
|
||||
NSError *error = RCTErrorWithMessage(errorText);
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, nil);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, nil);
|
||||
return;
|
||||
}
|
||||
}];
|
||||
|
@ -213,52 +213,41 @@ static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation,
|
|||
NSURL *url = [NSURL URLWithString:imageTag];
|
||||
if (!url) {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Invalid URL: %@", imageTag];
|
||||
RCTDispatchCallbackOnMainQueue(completion, RCTErrorWithMessage(errorMessage), nil);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, RCTErrorWithMessage(errorMessage), nil);
|
||||
return ^{};
|
||||
}
|
||||
if ([imageTag.lowercaseString hasSuffix:@".gif"]) {
|
||||
return [[RCTImageDownloader sharedInstance] downloadDataForURL:url progressBlock:progress block:^(NSData *data, NSError *error) {
|
||||
id image = RCTGIFImageWithFileURL([RCTConvert NSURL:imageTag]);
|
||||
if (!image && !error) {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Unable to load GIF image: %@", imageTag];
|
||||
error = RCTErrorWithMessage(errorMessage);
|
||||
}
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, image);
|
||||
}];
|
||||
} else {
|
||||
return [[RCTImageDownloader sharedInstance] downloadImageForURL:url size:size scale:scale resizeMode:resizeMode tintColor:nil backgroundColor:nil progressBlock:progress block:^(UIImage *image, NSError *error) {
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, image);
|
||||
}];
|
||||
}
|
||||
return [_bridge.imageDownloader downloadImageForURL:url size:size scale:scale resizeMode:resizeMode progressBlock:progressBlock completionBlock:^(NSError *error, id image) {
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
||||
}];
|
||||
} else if ([imageTag hasPrefix:@"rct-image-store://"]) {
|
||||
[_bridge.imageStoreManager getImageForTag:imageTag withBlock:^(UIImage *image) {
|
||||
if (image) {
|
||||
RCTDispatchCallbackOnMainQueue(completion, nil, image);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, nil, image);
|
||||
} else {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Unable to load image from image store: %@", imageTag];
|
||||
NSError *error = RCTErrorWithMessage(errorMessage);
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, nil);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, nil);
|
||||
}
|
||||
}];
|
||||
return ^{};
|
||||
} else if ([imageTag.lowercaseString hasSuffix:@".gif"]) {
|
||||
id image = RCTGIFImageWithFileURL([RCTConvert NSURL:imageTag]);
|
||||
if (image) {
|
||||
RCTDispatchCallbackOnMainQueue(completion, nil, image);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, nil, image);
|
||||
} else {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Unable to load GIF image: %@", imageTag];
|
||||
NSError *error = RCTErrorWithMessage(errorMessage);
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, nil);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, nil);
|
||||
}
|
||||
return ^{};
|
||||
} else {
|
||||
UIImage *image = [RCTConvert UIImage:imageTag];
|
||||
if (image) {
|
||||
RCTDispatchCallbackOnMainQueue(completion, nil, image);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, nil, image);
|
||||
} else {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Unrecognized tag protocol: %@", imageTag];
|
||||
NSError *error = RCTErrorWithMessage(errorMessage);
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, nil);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, nil);
|
||||
}
|
||||
return ^{};
|
||||
}
|
||||
|
|
|
@ -110,11 +110,11 @@ RCT_NOT_IMPLEMENTED(-init)
|
|||
|
||||
RCTImageLoaderProgressBlock progressHandler = nil;
|
||||
if (_onProgress) {
|
||||
progressHandler = ^(int64_t loaded, int64_t total) {
|
||||
progressHandler = ^(int64_t loaded, int64_t total) {
|
||||
NSDictionary *event = @{
|
||||
@"target": self.reactTag,
|
||||
@"loaded": @(loaded),
|
||||
@"total": @(total),
|
||||
@"loaded": @((double)loaded),
|
||||
@"total": @((double)total),
|
||||
};
|
||||
[_bridge.eventDispatcher sendInputEventWithName:@"progress" body:event];
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
typedef void (^RCTURLRequestCompletionBlock)(NSURLResponse *response, NSData *data, NSError *error);
|
||||
typedef void (^RCTURLRequestCancellationBlock)(void);
|
||||
typedef void (^RCTURLRequestIncrementalDataBlock)(NSData *data);
|
||||
typedef void (^RCTURLRequestProgressBlock)(double progress, double total);
|
||||
typedef void (^RCTURLRequestProgressBlock)(int64_t progress, int64_t total);
|
||||
typedef void (^RCTURLRequestResponseBlock)(NSURLResponse *response);
|
||||
|
||||
@interface RCTDownloadTask : NSObject <RCTURLRequestDelegate>
|
||||
|
|
|
@ -9,8 +9,18 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTDownloadTask.h"
|
||||
|
||||
@interface RCTNetworking : NSObject <RCTBridgeModule>
|
||||
|
||||
- (RCTDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
|
||||
completionBlock:(RCTURLRequestCompletionBlock)completionBlock;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTBridge (RCTNetworking)
|
||||
|
||||
@property (nonatomic, readonly) RCTNetworking *networking;
|
||||
|
||||
@end
|
||||
|
|
|
@ -233,13 +233,8 @@ RCT_EXPORT_MODULE()
|
|||
NSURLRequest *request = [RCTConvert NSURLRequest:query[@"uri"]];
|
||||
if (request) {
|
||||
|
||||
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
|
||||
if (!handler) {
|
||||
return callback(nil, nil);
|
||||
}
|
||||
|
||||
__block RCTURLRequestCancellationBlock cancellationBlock = nil;
|
||||
RCTDownloadTask *task = [[RCTDownloadTask alloc] initWithRequest:request handler:handler completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
RCTDownloadTask *task = [self downloadTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
cancellationBlock = callback(error, data ? @{@"body": data, @"contentType": RCTNullIfNil(response.MIMEType)} : nil);
|
||||
}];
|
||||
|
||||
|
@ -291,16 +286,11 @@ RCT_EXPORT_MODULE()
|
|||
incrementalUpdates:(BOOL)incrementalUpdates
|
||||
responseSender:(RCTResponseSenderBlock)responseSender
|
||||
{
|
||||
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
__block RCTDownloadTask *task;
|
||||
|
||||
RCTURLRequestProgressBlock uploadProgressBlock = ^(double progress, double total) {
|
||||
RCTURLRequestProgressBlock uploadProgressBlock = ^(int64_t progress, int64_t total) {
|
||||
dispatch_async(_methodQueue, ^{
|
||||
NSArray *responseJSON = @[task.requestID, @(progress), @(total)];
|
||||
NSArray *responseJSON = @[task.requestID, @((double)progress), @((double)total)];
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"didSendNetworkData" body:responseJSON];
|
||||
});
|
||||
};
|
||||
|
@ -345,10 +335,7 @@ RCT_EXPORT_MODULE()
|
|||
});
|
||||
};
|
||||
|
||||
task = [[RCTDownloadTask alloc] initWithRequest:request
|
||||
handler:handler
|
||||
completionBlock:completionBlock];
|
||||
|
||||
task = [self downloadTaskWithRequest:request completionBlock:completionBlock];
|
||||
task.incrementalDataBlock = incrementalDataBlock;
|
||||
task.responseBlock = responseBlock;
|
||||
task.uploadProgressBlock = uploadProgressBlock;
|
||||
|
@ -359,6 +346,21 @@ RCT_EXPORT_MODULE()
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark - Public API
|
||||
|
||||
- (RCTDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
|
||||
completionBlock:(RCTURLRequestCompletionBlock)completionBlock
|
||||
{
|
||||
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
|
||||
if (!handler) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [[RCTDownloadTask alloc] initWithRequest:request
|
||||
handler:handler
|
||||
completionBlock:completionBlock];
|
||||
}
|
||||
|
||||
#pragma mark - JS API
|
||||
|
||||
RCT_EXPORT_METHOD(sendRequest:(NSDictionary *)query
|
||||
|
@ -383,3 +385,12 @@ RCT_EXPORT_METHOD(cancelRequest:(NSNumber *)requestID)
|
|||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTBridge (RCTNetworking)
|
||||
|
||||
- (RCTNetworking *)networking
|
||||
{
|
||||
return self.modules[RCTBridgeModuleNameForClass([RCTNetworking class])];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue