Added RCTDataRequestHandler
Summary: public Added RCTDataRequestHandler, which is responsible for loading data URLs. This moves the logic for data URL handling out of RCTImageDownloader (no longer needed) and into the RCTNetwork library, where it makes more sense. This also means that it is now possible to load data URLs via XHR, and use them for purposes other than just images. Reviewed By: javache Differential Revision: D2540964 fb-gh-sync-id: 4f0418bd6b9186f047cc8297276bb970795af104
This commit is contained in:
parent
31f9a690f3
commit
1076f4a172
|
@ -24,6 +24,8 @@ var {
|
|||
ActivityIndicatorIOS
|
||||
} = React;
|
||||
|
||||
var base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACSR7JhAAADtUlEQVR4Ac3YA2Bj6QLH0XPT1Fzbtm29tW3btm3bfLZtv7e2ObZnms7d8Uw098tuetPzrxv8wiISrtVudrG2JXQZ4VOv+qUfmqCGGl1mqLhoA52oZlb0mrjsnhKpgeUNEs91Z0pd1kvihA3ULGVHiQO2narKSHKkEMulm9VgUyE60s1aWoMQUbpZOWE+kaqs4eLEjdIlZTcFZB0ndc1+lhB1lZrIuk5P2aib1NBpZaL+JaOGIt0ls47SKzLC7CqrlGF6RZ09HGoNy1lYl2aRSWL5GuzqWU1KafRdoRp0iOQEiDzgZPnG6DbldcomadViflnl/cL93tOoVbsOLVM2jylvdWjXolWX1hmfZbGR/wjypDjFLSZIRov09BgYmtUqPQPlQrPapecLgTIy0jMgPKtTeob2zWtrGH3xvjUkPCtNg/tm1rjwrMa+mdUkPd3hWbH0jArPGiU9ufCsNNWFZ40wpwn+62/66R2RUtoso1OB34tnLOcy7YB1fUdc9e0q3yru8PGM773vXsuZ5YIZX+5xmHwHGVvlrGPN6ZSiP1smOsMMde40wKv2VmwPPVXNut4sVpUreZiLBHi0qln/VQeI/LTMYXpsJtFiclUN+5HVZazim+Ky+7sAvxWnvjXrJFneVtLWLyPJu9K3cXLWeOlbMTlrIelbMDlrLenrjEQOtIF+fuI9xRp9ZBFp6+b6WT8RrxEpdK64BuvHgDk+vUy+b5hYk6zfyfs051gRoNO1usU12WWRWL73/MMEy9pMi9qIrR4ZpV16Rrvduxazmy1FSvuFXRkqTnE7m2kdb5U8xGjLw/spRr1uTov4uOgQE+0N/DvFrG/Jt7i/FzwxbA9kDanhf2w+t4V97G8lrT7wc08aA2QNUkuTfW/KimT01wdlfK4yEw030VfT0RtZbzjeMprNq8m8tnSTASrTLti64oBNdpmMQm0eEwvfPwRbUBywG5TzjPCsdwk3IeAXjQblLCoXnDVeoAz6SfJNk5TTzytCNZk/POtTSV40NwOFWzw86wNJRpubpXsn60NJFlHeqlYRbslqZm2jnEZ3qcSKgm0kTli3zZVS7y/iivZTweYXJ26Y+RTbV1zh3hYkgyFGSTKPfRVbRqWWVReaxYeSLarYv1Qqsmh1s95S7G+eEWK0f3jYKTbV6bOwepjfhtafsvUsqrQvrGC8YhmnO9cSCk3yuY984F1vesdHYhWJ5FvASlacshUsajFt2mUM9pqzvKGcyNJW0arTKN1GGGzQlH0tXwLDgQTurS8eIQAAAABJRU5ErkJggg==';
|
||||
|
||||
var ImageCapInsetsExample = require('./ImageCapInsetsExample');
|
||||
|
||||
var NetworkImageExample = React.createClass({
|
||||
|
@ -335,6 +337,18 @@ exports.examples = [
|
|||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Base64 image',
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
style={styles.base64}
|
||||
source={{uri: base64Icon, scale: 3}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Cap Insets',
|
||||
description:
|
||||
|
@ -399,4 +413,9 @@ var styles = StyleSheet.create({
|
|||
flex: 1,
|
||||
height: 200,
|
||||
},
|
||||
base64: {
|
||||
flex: 1,
|
||||
height: 50,
|
||||
resizeMode: 'contain',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -25,14 +25,14 @@ typedef RCTImageLoaderCancellationBlock (^RCTImageURLLoaderLoadImageURLHandler)(
|
|||
|
||||
@end
|
||||
|
||||
typedef BOOL (^RCTImageDecoderCanDecodeImageDataHandler)(NSData *imageData);
|
||||
typedef RCTImageLoaderCancellationBlock (^RCTImageDecoderDecodeImageDataHandler)(NSData *imageData, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler);
|
||||
typedef BOOL (^RCTImageDataDecoderCanDecodeImageDataHandler)(NSData *imageData);
|
||||
typedef RCTImageLoaderCancellationBlock (^RCTImageDataDecoderDecodeImageDataHandler)(NSData *imageData, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler);
|
||||
|
||||
@interface RCTConcreteImageDecoder : NSObject <RCTImageDecoder>
|
||||
@interface RCTConcreteImageDecoder : NSObject <RCTImageDataDecoder>
|
||||
|
||||
- (instancetype)initWithPriority:(float)priority
|
||||
canDecodeImageDataHandler:(RCTImageDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler
|
||||
decodeImageDataHandler:(RCTImageDecoderDecodeImageDataHandler)decodeImageDataHandler;
|
||||
canDecodeImageDataHandler:(RCTImageDataDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler
|
||||
decodeImageDataHandler:(RCTImageDataDecoderDecodeImageDataHandler)decodeImageDataHandler;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
return _loadImageURLHandler(imageURL, size, scale, resizeMode, progressHandler, completionHandler);
|
||||
}
|
||||
|
||||
- (float)imageLoaderPriority
|
||||
- (float)loaderPriority
|
||||
{
|
||||
return _priority;
|
||||
}
|
||||
|
@ -61,8 +61,8 @@
|
|||
|
||||
@implementation RCTConcreteImageDecoder
|
||||
{
|
||||
RCTImageDecoderCanDecodeImageDataHandler _canDecodeImageDataHandler;
|
||||
RCTImageDecoderDecodeImageDataHandler _decodeImageDataHandler;
|
||||
RCTImageDataDecoderCanDecodeImageDataHandler _canDecodeImageDataHandler;
|
||||
RCTImageDataDecoderDecodeImageDataHandler _decodeImageDataHandler;
|
||||
float _priority;
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@
|
|||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithPriority:(float)priority canDecodeImageDataHandler:(RCTImageDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler decodeImageDataHandler:(RCTImageDecoderDecodeImageDataHandler)decodeImageDataHandler
|
||||
- (instancetype)initWithPriority:(float)priority canDecodeImageDataHandler:(RCTImageDataDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler decodeImageDataHandler:(RCTImageDataDecoderDecodeImageDataHandler)decodeImageDataHandler
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_canDecodeImageDataHandler = [canDecodeImageDataHandler copy];
|
||||
|
@ -97,7 +97,7 @@
|
|||
return _decodeImageDataHandler(imageData, size, scale, resizeMode, completionHandler);
|
||||
}
|
||||
|
||||
- (float)imageDecoderPriority
|
||||
- (float)decoderPriority
|
||||
{
|
||||
return _priority;
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
|
|||
NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO];
|
||||
UIImage *image = [[UIImage alloc] initWithData:data];
|
||||
|
||||
id<RCTImageDecoder> decoder = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
|
||||
id<RCTImageDataDecoder> decoder = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
|
||||
return YES;
|
||||
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
|
||||
XCTAssertEqualObjects(imageData, data);
|
||||
|
@ -118,7 +118,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
|
|||
NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO];
|
||||
UIImage *image = [[UIImage alloc] initWithData:data];
|
||||
|
||||
id<RCTImageDecoder> decoder1 = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
|
||||
id<RCTImageDataDecoder> decoder1 = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
|
||||
return YES;
|
||||
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
|
||||
XCTAssertEqualObjects(imageData, data);
|
||||
|
@ -126,7 +126,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
|
|||
return nil;
|
||||
}];
|
||||
|
||||
id<RCTImageDecoder> decoder2 = [[RCTImageLoaderTestsDecoder2 alloc] initWithPriority:0.5 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
|
||||
id<RCTImageDataDecoder> decoder2 = [[RCTImageLoaderTestsDecoder2 alloc] initWithPriority:0.5 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
|
||||
return YES;
|
||||
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(__unused NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderCompletionBlock completionHandler) {
|
||||
XCTFail(@"Should not have used decoder2");
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
|
||||
#import "RCTImageLoader.h"
|
||||
|
||||
@interface RCTGIFImageDecoder : NSObject <RCTImageDecoder>
|
||||
@interface RCTGIFImageDecoder : NSObject <RCTImageDataDecoder>
|
||||
|
||||
@end
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
|
||||
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
|
||||
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; };
|
||||
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
@ -54,8 +53,6 @@
|
|||
354631661B69857700AA0B86 /* RCTImageEditingManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageEditingManager.h; sourceTree = "<group>"; };
|
||||
354631671B69857700AA0B86 /* RCTImageEditingManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageEditingManager.m; sourceTree = "<group>"; };
|
||||
58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
58B511891A9E6BD600147676 /* RCTImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageDownloader.h; sourceTree = "<group>"; };
|
||||
58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -76,8 +73,6 @@
|
|||
13EF7F7E1BC825B1003F47DD /* RCTXCAssetImageLoader.m */,
|
||||
1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */,
|
||||
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */,
|
||||
58B511891A9E6BD600147676 /* RCTImageDownloader.h */,
|
||||
58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */,
|
||||
354631661B69857700AA0B86 /* RCTImageEditingManager.h */,
|
||||
354631671B69857700AA0B86 /* RCTImageEditingManager.m */,
|
||||
143879361AAD32A300F088A5 /* RCTImageLoader.h */,
|
||||
|
@ -166,7 +161,6 @@
|
|||
files = (
|
||||
13EF7F0C1BC42D4E003F47DD /* RCTVirtualImageManager.m in Sources */,
|
||||
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */,
|
||||
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */,
|
||||
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
|
||||
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */,
|
||||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
|
||||
|
|
|
@ -1,178 +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 "RCTImageDownloader.h"
|
||||
|
||||
#import "RCTImageLoader.h"
|
||||
#import "RCTImageUtils.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTNetworking.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@implementation RCTImageDownloader
|
||||
{
|
||||
NSURLCache *_cache;
|
||||
dispatch_queue_t _processingQueue;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
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);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)canLoadImageURL:(NSURL *)requestURL
|
||||
{
|
||||
static NSSet *schemes = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
schemes = [[NSSet alloc] initWithObjects:@"http", @"https", @"file", @"data", nil];
|
||||
});
|
||||
return [schemes containsObject:requestURL.scheme.lowercaseString];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
progressHandler:(RCTImageLoaderProgressBlock)progressBlock
|
||||
completionHandler:(void (^)(NSError *error, NSData *data))completionBlock
|
||||
{
|
||||
if (![_bridge respondsToSelector:NSSelectorFromString(@"networking")]) {
|
||||
RCTLogError(@"You need to import the RCTNetworking library in order to download remote images.");
|
||||
return ^{};
|
||||
}
|
||||
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
RCTURLRequestCompletionBlock runBlocks = ^(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, ^{
|
||||
completionBlock(error, data);
|
||||
});
|
||||
};
|
||||
|
||||
NSURLRequest *request = [NSURLRequest requestWithURL:url];
|
||||
{
|
||||
NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
|
||||
if (cachedResponse) {
|
||||
runBlocks(cachedResponse.response, cachedResponse.data, nil);
|
||||
return ^{};
|
||||
}
|
||||
}
|
||||
|
||||
RCTDownloadTask *task = [_bridge.networking downloadTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
if (response && !error && [response.URL.scheme hasPrefix:@"http"]) {
|
||||
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;
|
||||
}
|
||||
[task start];
|
||||
return ^{ [task cancel]; };
|
||||
}
|
||||
|
||||
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
|
||||
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
||||
{
|
||||
if ([imageURL.scheme.lowercaseString hasPrefix:@"http"] ||
|
||||
[imageURL.scheme caseInsensitiveCompare:@"file"] == NSOrderedSame) {
|
||||
__block RCTImageLoaderCancellationBlock decodeCancel = nil;
|
||||
|
||||
// Add missing png extension
|
||||
if (imageURL.fileURL && imageURL.pathExtension.length == 0) {
|
||||
imageURL = [NSURL fileURLWithPath:[imageURL.path stringByAppendingPathExtension:@"png"]];
|
||||
}
|
||||
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
RCTImageLoaderCancellationBlock downloadCancel = [self downloadDataForURL:imageURL progressHandler:progressHandler completionHandler:^(NSError *error, NSData *imageData) {
|
||||
if (error) {
|
||||
completionHandler(error, nil);
|
||||
} else {
|
||||
decodeCancel = [weakSelf.bridge.imageLoader decodeImageData:imageData size:size scale:scale resizeMode:resizeMode completionBlock:completionHandler];
|
||||
}
|
||||
}];
|
||||
|
||||
return ^{
|
||||
downloadCancel();
|
||||
|
||||
if (decodeCancel) {
|
||||
decodeCancel();
|
||||
}
|
||||
};
|
||||
} else if ([imageURL.scheme caseInsensitiveCompare:@"data"] == NSOrderedSame) {
|
||||
__block BOOL cancelled = NO;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Normally -dataWithContentsOfURL: would be bad but this is a data URL.
|
||||
NSData *data = [NSData dataWithContentsOfURL:imageURL];
|
||||
|
||||
UIImage *image = [UIImage imageWithData:data scale:scale];
|
||||
if (image) {
|
||||
if (progressHandler) {
|
||||
progressHandler(1, 1);
|
||||
}
|
||||
if (completionHandler) {
|
||||
completionHandler(nil, image);
|
||||
}
|
||||
} else {
|
||||
if (completionHandler) {
|
||||
NSString *message = [NSString stringWithFormat:@"Invalid image data for URL: %@", imageURL];
|
||||
completionHandler(RCTErrorWithMessage(message), nil);
|
||||
}
|
||||
}
|
||||
});
|
||||
return ^{
|
||||
cancelled = YES;
|
||||
};
|
||||
} else {
|
||||
RCTLogError(@"Unexpected image schema %@", imageURL.scheme);
|
||||
return ^{};
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTBridge (RCTImageDownloader)
|
||||
|
||||
- (RCTImageDownloader *)imageDownloader
|
||||
{
|
||||
return self.modules[RCTBridgeModuleNameForClass([RCTImageDownloader class])];
|
||||
}
|
||||
|
||||
@end
|
|
@ -46,7 +46,8 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
|
|||
|
||||
/**
|
||||
* Finds an appropriate image decoder and passes the target size, scale and
|
||||
* resizeMode for optimal image decoding.
|
||||
* resizeMode for optimal image decoding. Can be called from any thread,
|
||||
* will call callback on an unspecified thread.
|
||||
*/
|
||||
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
|
||||
size:(CGSize)size
|
||||
|
@ -66,7 +67,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
|
|||
@end
|
||||
|
||||
/**
|
||||
* Provides the interface needed to register an image data loader. Image data
|
||||
* Provides the interface needed to register an image loader. Image data
|
||||
* loaders are also bridge modules, so should be registered using
|
||||
* RCT_EXPORT_MODULE().
|
||||
*/
|
||||
|
@ -85,18 +86,23 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
|
|||
* has finished. The method should also return a cancellation block, if
|
||||
* applicable.
|
||||
*/
|
||||
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
|
||||
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
|
||||
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* If more than one RCTImageURLLoader responds YES to `-canLoadImageURL:`
|
||||
* then `imageLoaderPriority` is used to determine which one to use. The handler
|
||||
* then `loaderPriority` is used to determine which one to use. The loader
|
||||
* with the highest priority will be selected. Default priority is zero. If
|
||||
* two or more valid handlers have the same priority, the selection order is
|
||||
* two or more valid loaders have the same priority, the selection order is
|
||||
* undefined.
|
||||
*/
|
||||
- (float)imageLoaderPriority;
|
||||
- (float)loaderPriority;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -104,7 +110,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
|
|||
* Provides the interface needed to register an image decoder. Image decoders
|
||||
* are also bridge modules, so should be registered using RCT_EXPORT_MODULE().
|
||||
*/
|
||||
@protocol RCTImageDecoder <RCTBridgeModule>
|
||||
@protocol RCTImageDataDecoder <RCTBridgeModule>
|
||||
|
||||
/**
|
||||
* Indicates whether this handler is capable of decoding the specified data.
|
||||
|
@ -118,17 +124,21 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
|
|||
* completionHandler when the decoding operation has finished. The method
|
||||
* should also return a cancellation block, if applicable.
|
||||
*/
|
||||
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
|
||||
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* If more than one RCTImageDecoder responds YES to `-canDecodeImageData:`
|
||||
* then `imageDecoderPriority` is used to determine which one to use. The
|
||||
* handler with the highest priority will be selected. Default priority is zero.
|
||||
* If two or more valid handlers have the same priority, the selection order is
|
||||
* If more than one RCTImageDataDecoder responds YES to `-canDecodeImageData:`
|
||||
* then `decoderPriority` is used to determine which one to use. The decoder
|
||||
* with the highest priority will be selected. Default priority is zero.
|
||||
* If two or more valid decoders have the same priority, the selection order is
|
||||
* undefined.
|
||||
*/
|
||||
- (float)imageDecoderPriority;
|
||||
- (float)decoderPriority;
|
||||
|
||||
@end
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTImageDownloader.h"
|
||||
#import "RCTImageUtils.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTNetworking.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSError *error, UIImage *image)
|
||||
|
@ -44,11 +44,131 @@ static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSEr
|
|||
@end
|
||||
|
||||
@implementation RCTImageLoader
|
||||
{
|
||||
NSArray *_loaders;
|
||||
NSArray *_decoders;
|
||||
NSURLCache *_cache;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
// Get image loaders and decoders
|
||||
NSMutableArray *loaders = [NSMutableArray array];
|
||||
NSMutableArray *decoders = [NSMutableArray array];
|
||||
for (id<RCTBridgeModule> module in bridge.modules.allValues) {
|
||||
if ([module conformsToProtocol:@protocol(RCTImageURLLoader)]) {
|
||||
[loaders addObject:module];
|
||||
}
|
||||
if ([module conformsToProtocol:@protocol(RCTImageDataDecoder)]) {
|
||||
[decoders addObject:module];
|
||||
}
|
||||
}
|
||||
|
||||
// Sort loaders in reverse priority order (highest priority first)
|
||||
[loaders sortUsingComparator:^NSComparisonResult(id<RCTImageURLLoader> a, id<RCTImageURLLoader> b) {
|
||||
float priorityA = [a respondsToSelector:@selector(loaderPriority)] ? [a loaderPriority] : 0;
|
||||
float priorityB = [b respondsToSelector:@selector(loaderPriority)] ? [b loaderPriority] : 0;
|
||||
if (priorityA > priorityB) {
|
||||
return NSOrderedAscending;
|
||||
} else if (priorityA < priorityB) {
|
||||
return NSOrderedDescending;
|
||||
} else {
|
||||
return NSOrderedSame;
|
||||
}
|
||||
}];
|
||||
|
||||
// Sort decoders in reverse priority order (highest priority first)
|
||||
[decoders sortUsingComparator:^NSComparisonResult(id<RCTImageDataDecoder> a, id<RCTImageDataDecoder> b) {
|
||||
float priorityA = [a respondsToSelector:@selector(decoderPriority)] ? [a decoderPriority] : 0;
|
||||
float priorityB = [b respondsToSelector:@selector(decoderPriority)] ? [b decoderPriority] : 0;
|
||||
if (priorityA > priorityB) {
|
||||
return NSOrderedAscending;
|
||||
} else if (priorityA < priorityB) {
|
||||
return NSOrderedDescending;
|
||||
} else {
|
||||
return NSOrderedSame;
|
||||
}
|
||||
}];
|
||||
|
||||
_bridge = bridge;
|
||||
_loaders = loaders;
|
||||
_decoders = decoders;
|
||||
_cache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 // 5MB
|
||||
diskCapacity:200 * 1024 * 1024 // 200MB
|
||||
diskPath:@"React/RCTImageDownloader"];
|
||||
}
|
||||
|
||||
- (id<RCTImageURLLoader>)imageURLLoaderForURL:(NSURL *)URL
|
||||
{
|
||||
if (RCT_DEBUG) {
|
||||
// Check for handler conflicts
|
||||
float previousPriority = 0;
|
||||
id<RCTImageURLLoader> previousLoader = nil;
|
||||
for (id<RCTImageURLLoader> loader in _loaders) {
|
||||
if ([loader canLoadImageURL:URL]) {
|
||||
float priority = [loader respondsToSelector:@selector(loaderPriority)] ? [loader loaderPriority] : 0;
|
||||
if (previousLoader) {
|
||||
if (priority == previousPriority) {
|
||||
RCTLogError(@"The RCTImageURLLoaders %@ and %@ both reported that"
|
||||
" they can load the URL %@, and have equal priority"
|
||||
" (%g). This could result in non-deterministic behavior.",
|
||||
loader, previousLoader, URL, priority);
|
||||
}
|
||||
} else {
|
||||
previousLoader = loader;
|
||||
previousPriority = priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normal code path
|
||||
for (id<RCTImageURLLoader> loader in _loaders) {
|
||||
if ([loader canLoadImageURL:URL]) {
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id<RCTImageDataDecoder>)imageDataDecoderForData:(NSData *)data
|
||||
{
|
||||
if (RCT_DEBUG) {
|
||||
// Check for handler conflicts
|
||||
float previousPriority = 0;
|
||||
id<RCTImageDataDecoder> previousDecoder = nil;
|
||||
for (id<RCTImageDataDecoder> decoder in _decoders) {
|
||||
if ([decoder canDecodeImageData:data]) {
|
||||
float priority = [decoder respondsToSelector:@selector(decoderPriority)] ? [decoder decoderPriority] : 0;
|
||||
if (previousDecoder) {
|
||||
if (priority == previousPriority) {
|
||||
RCTLogError(@"The RCTImageDataDecoders %@ and %@ both reported that"
|
||||
" they can decode the data <NSData %p; %tu bytes>, and"
|
||||
" have equal priority (%g). This could result in"
|
||||
" non-deterministic behavior.",
|
||||
decoder, previousDecoder, data, data.length, priority);
|
||||
}
|
||||
} else {
|
||||
previousDecoder = decoder;
|
||||
previousPriority = priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normal code path
|
||||
for (id<RCTImageDataDecoder> decoder in _decoders) {
|
||||
if ([decoder canDecodeImageData:data]) {
|
||||
return decoder;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
|
||||
callback:(RCTImageLoaderCompletionBlock)callback
|
||||
{
|
||||
|
@ -60,35 +180,6 @@ RCT_EXPORT_MODULE()
|
|||
completionBlock:callback];
|
||||
}
|
||||
|
||||
- (id<RCTImageURLLoader>)imageURLLoaderForRequest:(NSURL *)requestURL
|
||||
{
|
||||
NSMutableArray *handlers = [NSMutableArray array];
|
||||
for (id<RCTBridgeModule> module in _bridge.modules.allValues) {
|
||||
if ([module conformsToProtocol:@protocol(RCTImageURLLoader)]) {
|
||||
if ([(id<RCTImageURLLoader>)module canLoadImageURL:requestURL]) {
|
||||
[handlers addObject:module];
|
||||
}
|
||||
}
|
||||
}
|
||||
[handlers sortUsingComparator:^NSComparisonResult(id<RCTImageURLLoader> a, id<RCTImageURLLoader> b) {
|
||||
float priorityA = [a respondsToSelector:@selector(imageLoaderPriority)] ? [a imageLoaderPriority] : 0;
|
||||
float priorityB = [b respondsToSelector:@selector(imageLoaderPriority)] ? [b imageLoaderPriority] : 0;
|
||||
if (priorityA < priorityB) {
|
||||
return NSOrderedAscending;
|
||||
} else if (priorityA > priorityB) {
|
||||
return NSOrderedDescending;
|
||||
} else {
|
||||
RCTLogError(@"The RCTImageLoader %@ and %@ both reported that they can"
|
||||
" handle the load request %@, and have equal priority (%g)."
|
||||
" This could result in non-deterministic behavior.",
|
||||
a, b, requestURL, priorityA);
|
||||
|
||||
return NSOrderedSame;
|
||||
}
|
||||
}];
|
||||
return [handlers lastObject];
|
||||
}
|
||||
|
||||
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
|
@ -101,58 +192,125 @@ RCT_EXPORT_MODULE()
|
|||
return ^{};
|
||||
}
|
||||
|
||||
NSURL *requestURL = [RCTConvert NSURL:imageTag];
|
||||
id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForRequest:requestURL];
|
||||
if (!loadHandler) {
|
||||
RCTLogError(@"No suitable image URL loader found for %@", imageTag);
|
||||
// Ensure progress is dispatched on main thread
|
||||
RCTImageLoaderProgressBlock progressHandler = nil;
|
||||
if (progressBlock) {
|
||||
progressHandler = ^(int64_t progress, int64_t total) {
|
||||
if ([NSThread isMainThread]) {
|
||||
progressBlock(progress, total);
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
progressBlock(progress, total);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Ensure completion is dispatched on main thread
|
||||
RCTImageLoaderCompletionBlock completionHandler = ^(NSError *error, UIImage *image) {
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
||||
};
|
||||
|
||||
// Find suitable image URL loader
|
||||
NSURLRequest *request = [RCTConvert NSURLRequest:imageTag];
|
||||
id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForURL:request.URL];
|
||||
if (loadHandler) {
|
||||
return [loadHandler loadImageForURL:request.URL
|
||||
size:size
|
||||
scale:scale
|
||||
resizeMode:resizeMode
|
||||
progressHandler:progressHandler
|
||||
completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
// Check if networking module is available
|
||||
if (![_bridge respondsToSelector:@selector(networking)]) {
|
||||
RCTLogError(@"No suitable image URL loader found for %@. You may need to "
|
||||
" import the RCTNetworking library in order to load images.",
|
||||
imageTag);
|
||||
return ^{};
|
||||
}
|
||||
|
||||
return [loadHandler loadImageForURL:requestURL size:size scale:scale resizeMode:resizeMode progressHandler:^(int64_t progress, int64_t total) {
|
||||
if (!progressBlock) {
|
||||
return;
|
||||
}
|
||||
// Use networking module to load image
|
||||
if ([_bridge.networking canHandleRequest:request]) {
|
||||
|
||||
if ([NSThread isMainThread]) {
|
||||
progressBlock(progress, total);
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
progressBlock(progress, total);
|
||||
});
|
||||
}
|
||||
} completionHandler:^(NSError *error, UIImage *image) {
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
||||
}];
|
||||
}
|
||||
__weak RCTImageLoader *weakSelf = self;
|
||||
__block RCTImageLoaderCancellationBlock decodeCancel = nil;
|
||||
RCTURLRequestCompletionBlock processResponse =
|
||||
^(NSURLResponse *response, NSData *data, __unused NSError *error) {
|
||||
|
||||
- (id<RCTImageDecoder>)imageDecoderForRequest:(NSData *)imageData
|
||||
{
|
||||
NSMutableArray *handlers = [NSMutableArray array];
|
||||
for (id<RCTBridgeModule> module in _bridge.modules.allValues) {
|
||||
if ([module conformsToProtocol:@protocol(RCTImageDecoder)]) {
|
||||
if ([(id<RCTImageDecoder>)module canDecodeImageData:imageData]) {
|
||||
[handlers addObject:module];
|
||||
// Check for http errors
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
|
||||
if (statusCode != 200) {
|
||||
completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain
|
||||
code:statusCode
|
||||
userInfo:nil], nil);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[handlers sortUsingComparator:^NSComparisonResult(id<RCTImageDecoder> a, id<RCTImageDecoder> b) {
|
||||
float priorityA = [a respondsToSelector:@selector(imageDecoderPriority)] ? [a imageDecoderPriority] : 0;
|
||||
float priorityB = [b respondsToSelector:@selector(imageDecoderPriority)] ? [b imageDecoderPriority] : 0;
|
||||
if (priorityA < priorityB) {
|
||||
return NSOrderedAscending;
|
||||
} else if (priorityA > priorityB) {
|
||||
return NSOrderedDescending;
|
||||
} else {
|
||||
RCTLogError(@"The RCTImageDecoder %@ and %@ both reported that they can"
|
||||
" handle the decode request <NSData %p; %tu bytes>, and have"
|
||||
" equal priority (%g). This could result in"
|
||||
" non-deterministic behavior.",
|
||||
a, b, imageData, imageData.length, priorityA);
|
||||
|
||||
return NSOrderedSame;
|
||||
// Decode image
|
||||
decodeCancel = [weakSelf decodeImageData:data
|
||||
size:size
|
||||
scale:scale
|
||||
resizeMode:resizeMode
|
||||
completionBlock:completionHandler];
|
||||
};
|
||||
|
||||
// Check for cached response before reloading
|
||||
// TODO: move URL cache out of RCTImageLoader into its own module
|
||||
NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
|
||||
if (cachedResponse) {
|
||||
processResponse(cachedResponse.response, cachedResponse.data, nil);
|
||||
return ^{};
|
||||
}
|
||||
}];
|
||||
return [handlers lastObject];
|
||||
|
||||
// Add missing png extension
|
||||
if (request.URL.fileURL && request.URL.pathExtension.length == 0) {
|
||||
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
||||
mutableRequest.URL = [NSURL fileURLWithPath:[request.URL.path stringByAppendingPathExtension:@"png"]];
|
||||
request = mutableRequest;
|
||||
}
|
||||
|
||||
// Download image
|
||||
RCTNetworkTask *task = [_bridge.networking networkTaskWithRequest:request completionBlock:
|
||||
^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
if (error) {
|
||||
completionHandler(error, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the response
|
||||
// TODO: move URL cache out of RCTImageLoader into its own module
|
||||
RCTImageLoader *strongSelf = weakSelf;
|
||||
BOOL isHTTPRequest = [request.URL.scheme hasPrefix:@"http"];
|
||||
[strongSelf->_cache storeCachedResponse:
|
||||
[[NSCachedURLResponse alloc] initWithResponse:response
|
||||
data:data
|
||||
userInfo:nil
|
||||
storagePolicy:isHTTPRequest ? NSURLCacheStorageAllowed: NSURLCacheStorageAllowedInMemoryOnly]
|
||||
forRequest:request];
|
||||
|
||||
// Process image data
|
||||
processResponse(response, data, nil);
|
||||
|
||||
}];
|
||||
if (progressBlock) {
|
||||
task.downloadProgressBlock = progressBlock;
|
||||
}
|
||||
[task start];
|
||||
|
||||
return ^{
|
||||
[task cancel];
|
||||
if (decodeCancel) {
|
||||
decodeCancel();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
RCTLogError(@"No suitable image URL loader found for %@", imageTag);
|
||||
return ^{};
|
||||
}
|
||||
|
||||
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data
|
||||
|
@ -161,20 +319,22 @@ RCT_EXPORT_MODULE()
|
|||
resizeMode:(UIViewContentMode)resizeMode
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
|
||||
{
|
||||
id<RCTImageDecoder> imageDecoder = [self imageDecoderForRequest:data];
|
||||
id<RCTImageDataDecoder> imageDecoder = [self imageDataDecoderForData:data];
|
||||
if (imageDecoder) {
|
||||
return [imageDecoder decodeImageData:data size:size scale:scale resizeMode:resizeMode completionHandler:^(NSError *error, UIImage *image) {
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
||||
}];
|
||||
return [imageDecoder decodeImageData:data
|
||||
size:size
|
||||
scale:scale
|
||||
resizeMode:resizeMode
|
||||
completionHandler:completionBlock];
|
||||
} else {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
UIImage *image = [UIImage imageWithData:data scale:scale];
|
||||
if (image) {
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, nil, image);
|
||||
completionBlock(nil, image);
|
||||
} else {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Error decoding image data <NSData %p; %tu bytes>", data, data.length];
|
||||
NSError *finalError = RCTErrorWithMessage(errorMessage);
|
||||
RCTDispatchCallbackOnMainQueue(completionBlock, finalError, nil);
|
||||
completionBlock(finalError, nil);
|
||||
}
|
||||
});
|
||||
return ^{};
|
||||
|
@ -185,18 +345,20 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
||||
{
|
||||
id<RCTImageURLLoader> handler = [self imageURLLoaderForRequest:request.URL];
|
||||
|
||||
// RCTImageDownloader is an image plugin that uses the networking stack.
|
||||
// We don't want to route any network calls through the image downloader
|
||||
// as that would cause cyclical dependencies.
|
||||
return handler && ![handler isKindOfClass:[RCTImageDownloader class]];
|
||||
NSURL *requestURL = request.URL;
|
||||
for (id<RCTBridgeModule> module in _bridge.modules.allValues) {
|
||||
if ([module conformsToProtocol:@protocol(RCTImageURLLoader)] &&
|
||||
[(id<RCTImageURLLoader>)module canLoadImageURL:requestURL]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id)sendRequest:(NSURLRequest *)request withDelegate:(id<RCTURLRequestDelegate>)delegate
|
||||
{
|
||||
__block RCTImageLoaderCancellationBlock requestToken;
|
||||
requestToken = [self.bridge.imageLoader loadImageWithTag:request.URL.absoluteString callback:^(NSError *error, UIImage *image) {
|
||||
requestToken = [self loadImageWithTag:request.URL.absoluteString callback:^(NSError *error, UIImage *image) {
|
||||
if (error) {
|
||||
[delegate URLRequest:requestToken didCompleteWithError:error];
|
||||
return;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#import "RCTImageLoader.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTUIManager.h"
|
||||
|
||||
@implementation RCTShadowVirtualImage
|
||||
{
|
||||
|
@ -38,9 +39,11 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init)
|
|||
|
||||
__weak RCTShadowVirtualImage *weakSelf = self;
|
||||
[_bridge.imageLoader loadImageWithTag:imageTag size:CGSizeZero scale:scale resizeMode:UIViewContentModeScaleToFill progressBlock:nil completionBlock:^(NSError *error, UIImage *image) {
|
||||
RCTShadowVirtualImage *strongSelf = weakSelf;
|
||||
strongSelf->_image = image;
|
||||
[strongSelf dirtyText];
|
||||
dispatch_async(_bridge.uiManager.methodQueue, ^{
|
||||
RCTShadowVirtualImage *strongSelf = weakSelf;
|
||||
strongSelf->_image = image;
|
||||
[strongSelf dirtyText];
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,11 +20,6 @@ RCT_EXPORT_MODULE()
|
|||
return RCTIsXCAssetURL(requestURL);
|
||||
}
|
||||
|
||||
- (float)imageLoaderPriority
|
||||
{
|
||||
return 100; // higher priority than any ordinary file loader
|
||||
}
|
||||
|
||||
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
||||
{
|
||||
__block BOOL cancelled = NO;
|
||||
|
|
|
@ -7,15 +7,12 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTImageLoader.h"
|
||||
#import "RCTURLRequestHandler.h"
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
@interface RCTImageDownloader : NSObject <RCTImageURLLoader>
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTBridge (RCTImageDownloader)
|
||||
|
||||
@property (nonatomic, readonly) RCTImageDownloader *imageDownloader;
|
||||
/**
|
||||
* This is the default RCTURLRequestHandler implementation for data URL requests.
|
||||
*/
|
||||
@interface RCTDataRequestHandler : NSObject <RCTURLRequestHandler, RCTInvalidating>
|
||||
|
||||
@end
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* 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 "RCTDataRequestHandler.h"
|
||||
|
||||
@implementation RCTDataRequestHandler
|
||||
{
|
||||
NSOperationQueue *_queue;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
[_queue cancelAllOperations];
|
||||
_queue = nil;
|
||||
}
|
||||
|
||||
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
||||
{
|
||||
return [request.URL.scheme caseInsensitiveCompare:@"data"] == NSOrderedSame;
|
||||
}
|
||||
|
||||
- (NSOperation *)sendRequest:(NSURLRequest *)request
|
||||
withDelegate:(id<RCTURLRequestDelegate>)delegate
|
||||
{
|
||||
// Lazy setup
|
||||
if (!_queue) {
|
||||
_queue = [NSOperationQueue new];
|
||||
_queue.maxConcurrentOperationCount = 2;
|
||||
}
|
||||
|
||||
__block NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
|
||||
|
||||
// Get mime type
|
||||
NSRange firstSemicolon = [request.URL.resourceSpecifier rangeOfString:@";"];
|
||||
NSString *mimeType = firstSemicolon.length ? [request.URL.resourceSpecifier substringToIndex:firstSemicolon.location] : nil;
|
||||
|
||||
// Send response
|
||||
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL
|
||||
MIMEType:mimeType
|
||||
expectedContentLength:-1
|
||||
textEncodingName:nil];
|
||||
|
||||
[delegate URLRequest:op didReceiveResponse:response];
|
||||
|
||||
// Load data
|
||||
NSError *error;
|
||||
NSData *data = [NSData dataWithContentsOfURL:request.URL
|
||||
options:NSDataReadingMappedIfSafe
|
||||
error:&error];
|
||||
if (data) {
|
||||
[delegate URLRequest:op didReceiveData:data];
|
||||
}
|
||||
[delegate URLRequest:op didCompleteWithError:error];
|
||||
}];
|
||||
|
||||
[_queue addOperation:op];
|
||||
return op;
|
||||
}
|
||||
|
||||
- (void)cancelRequest:(NSOperation *)op
|
||||
{
|
||||
[op cancel];
|
||||
}
|
||||
|
||||
@end
|
|
@ -11,13 +11,13 @@
|
|||
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@implementation RCTFileRequestHandler
|
||||
{
|
||||
NSOperationQueue *_fileQueue;
|
||||
}
|
||||
|
||||
@synthesize methodQueue = _methodQueue;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (void)invalidate
|
||||
|
@ -28,7 +28,9 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
||||
{
|
||||
return [request.URL.scheme caseInsensitiveCompare:@"file"] == NSOrderedSame;
|
||||
return
|
||||
[request.URL.scheme caseInsensitiveCompare:@"file"] == NSOrderedSame
|
||||
&& !RCTIsXCAssetURL(request.URL);
|
||||
}
|
||||
|
||||
- (NSOperation *)sendRequest:(NSURLRequest *)request
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
134E969A1BCEB7F800AFFDA1 /* RCTDataRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */; settings = {ASSET_TAGS = (); }; };
|
||||
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */; };
|
||||
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */; };
|
||||
13D6D66A1B5FCF8200883BE9 /* RCTNetworkTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */; };
|
||||
13EF800E1BCBE015003F47DD /* RCTFileRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */; settings = {ASSET_TAGS = (); }; };
|
||||
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */; };
|
||||
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.m */; };
|
||||
|
@ -27,10 +28,12 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
134E96981BCEB7F800AFFDA1 /* RCTDataRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDataRequestHandler.h; sourceTree = "<group>"; };
|
||||
134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDataRequestHandler.m; sourceTree = "<group>"; };
|
||||
1372B7351AB03E7B00659ED6 /* RCTNetInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTNetInfo.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RCTNetInfo.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
13D6D6681B5FCF8200883BE9 /* RCTDownloadTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDownloadTask.h; sourceTree = "<group>"; };
|
||||
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDownloadTask.m; sourceTree = "<group>"; };
|
||||
13D6D6681B5FCF8200883BE9 /* RCTNetworkTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTNetworkTask.h; sourceTree = "<group>"; };
|
||||
13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTNetworkTask.m; sourceTree = "<group>"; };
|
||||
13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFileRequestHandler.h; sourceTree = "<group>"; };
|
||||
13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFileRequestHandler.m; sourceTree = "<group>"; };
|
||||
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTHTTPRequestHandler.h; sourceTree = "<group>"; };
|
||||
|
@ -54,12 +57,14 @@
|
|||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13D6D6681B5FCF8200883BE9 /* RCTDownloadTask.h */,
|
||||
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */,
|
||||
13D6D6681B5FCF8200883BE9 /* RCTNetworkTask.h */,
|
||||
13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */,
|
||||
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */,
|
||||
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */,
|
||||
13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */,
|
||||
13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */,
|
||||
134E96981BCEB7F800AFFDA1 /* RCTDataRequestHandler.h */,
|
||||
134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */,
|
||||
1372B7351AB03E7B00659ED6 /* RCTNetInfo.h */,
|
||||
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */,
|
||||
58B512061A9E6CE300147676 /* RCTNetworking.h */,
|
||||
|
@ -134,8 +139,9 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */,
|
||||
13D6D66A1B5FCF8200883BE9 /* RCTNetworkTask.m in Sources */,
|
||||
13EF800E1BCBE015003F47DD /* RCTFileRequestHandler.m in Sources */,
|
||||
134E969A1BCEB7F800AFFDA1 /* RCTDataRequestHandler.m in Sources */,
|
||||
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */,
|
||||
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */,
|
||||
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */,
|
||||
|
|
|
@ -18,7 +18,7 @@ typedef void (^RCTURLRequestIncrementalDataBlock)(NSData *data);
|
|||
typedef void (^RCTURLRequestProgressBlock)(int64_t progress, int64_t total);
|
||||
typedef void (^RCTURLRequestResponseBlock)(NSURLResponse *response);
|
||||
|
||||
@interface RCTDownloadTask : NSObject <RCTURLRequestDelegate>
|
||||
@interface RCTNetworkTask : NSObject <RCTURLRequestDelegate>
|
||||
|
||||
@property (nonatomic, readonly) NSURLRequest *request;
|
||||
@property (nonatomic, readonly) NSNumber *requestID;
|
|
@ -7,15 +7,15 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTDownloadTask.h"
|
||||
#import "RCTNetworkTask.h"
|
||||
|
||||
#import "RCTLog.h"
|
||||
|
||||
@implementation RCTDownloadTask
|
||||
@implementation RCTNetworkTask
|
||||
{
|
||||
NSMutableData *_data;
|
||||
id<RCTURLRequestHandler> _handler;
|
||||
RCTDownloadTask *_selfReference;
|
||||
RCTNetworkTask *_selfReference;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRequest:(NSURLRequest *)request
|
|
@ -10,12 +10,21 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTDownloadTask.h"
|
||||
#import "RCTNetworkTask.h"
|
||||
|
||||
@interface RCTNetworking : NSObject <RCTBridgeModule>
|
||||
|
||||
- (RCTDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
|
||||
completionBlock:(RCTURLRequestCompletionBlock)completionBlock;
|
||||
/**
|
||||
* Does a handler exist for the specified request?
|
||||
*/
|
||||
- (BOOL)canHandleRequest:(NSURLRequest *)request;
|
||||
|
||||
/**
|
||||
* Return an RCTNetworkTask for the specified request. This is useful for
|
||||
* invoking the React Native networking stack from within native code.
|
||||
*/
|
||||
- (RCTNetworkTask *)networkTaskWithRequest:(NSURLRequest *)request
|
||||
completionBlock:(RCTURLRequestCompletionBlock)completionBlock;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTDownloadTask.h"
|
||||
#import "RCTNetworkTask.h"
|
||||
#import "RCTURLRequestHandler.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTHTTPRequestHandler.h"
|
||||
|
@ -37,10 +37,10 @@ typedef RCTURLRequestCancellationBlock (^RCTHTTPQueryResult)(NSError *error, NSD
|
|||
|
||||
@implementation RCTHTTPFormDataHelper
|
||||
{
|
||||
NSMutableArray *parts;
|
||||
NSMutableData *multipartBody;
|
||||
NSMutableArray *_parts;
|
||||
NSMutableData *_multipartBody;
|
||||
RCTHTTPQueryResult _callback;
|
||||
NSString *boundary;
|
||||
NSString *_boundary;
|
||||
}
|
||||
|
||||
static NSString *RCTGenerateFormBoundary()
|
||||
|
@ -56,19 +56,19 @@ static NSString *RCTGenerateFormBoundary()
|
|||
return [[NSString alloc] initWithBytesNoCopy:bytes length:boundaryLength encoding:NSUTF8StringEncoding freeWhenDone:YES];
|
||||
}
|
||||
|
||||
- (RCTURLRequestCancellationBlock)process:(NSArray *)formData
|
||||
- (RCTURLRequestCancellationBlock)process:(NSDictionaryArray *)formData
|
||||
callback:(RCTHTTPQueryResult)callback
|
||||
{
|
||||
if (formData.count == 0) {
|
||||
return callback(nil, nil);
|
||||
}
|
||||
|
||||
parts = [formData mutableCopy];
|
||||
_parts = [formData mutableCopy];
|
||||
_callback = callback;
|
||||
multipartBody = [NSMutableData new];
|
||||
boundary = RCTGenerateFormBoundary();
|
||||
_multipartBody = [NSMutableData new];
|
||||
_boundary = RCTGenerateFormBoundary();
|
||||
|
||||
return [_networker processDataForHTTPQuery:parts[0] callback:^(NSError *error, NSDictionary *result) {
|
||||
return [_networker processDataForHTTPQuery:_parts[0] callback:^(NSError *error, NSDictionary *result) {
|
||||
return [self handleResult:result error:error];
|
||||
}];
|
||||
}
|
||||
|
@ -81,37 +81,37 @@ static NSString *RCTGenerateFormBoundary()
|
|||
}
|
||||
|
||||
// Start with boundary.
|
||||
[multipartBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
|
||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[_multipartBody appendData:[[NSString stringWithFormat:@"--%@\r\n", _boundary]
|
||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
// Print headers.
|
||||
NSMutableDictionary *headers = [parts[0][@"headers"] mutableCopy];
|
||||
NSMutableDictionary *headers = [_parts[0][@"headers"] mutableCopy];
|
||||
NSString *partContentType = result[@"contentType"];
|
||||
if (partContentType != nil) {
|
||||
headers[@"content-type"] = partContentType;
|
||||
}
|
||||
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey, NSString *parameterValue, BOOL *stop) {
|
||||
[multipartBody appendData:[[NSString stringWithFormat:@"%@: %@\r\n", parameterKey, parameterValue]
|
||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[_multipartBody appendData:[[NSString stringWithFormat:@"%@: %@\r\n", parameterKey, parameterValue]
|
||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
}];
|
||||
|
||||
// Add the body.
|
||||
[multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[multipartBody appendData:result[@"body"]];
|
||||
[multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[_multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[_multipartBody appendData:result[@"body"]];
|
||||
[_multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
[parts removeObjectAtIndex:0];
|
||||
if (parts.count) {
|
||||
return [_networker processDataForHTTPQuery:parts[0] callback:^(NSError *err, NSDictionary *res) {
|
||||
[_parts removeObjectAtIndex:0];
|
||||
if (_parts.count) {
|
||||
return [_networker processDataForHTTPQuery:_parts[0] callback:^(NSError *err, NSDictionary *res) {
|
||||
return [self handleResult:res error:err];
|
||||
}];
|
||||
}
|
||||
|
||||
// We've processed the last item. Finish and return.
|
||||
[multipartBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary]
|
||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=\"%@\"", boundary];
|
||||
return _callback(nil, @{@"body": multipartBody, @"contentType": contentType});
|
||||
[_multipartBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", _boundary]
|
||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=\"%@\"", _boundary];
|
||||
return _callback(nil, @{@"body": _multipartBody, @"contentType": contentType});
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -122,6 +122,7 @@ static NSString *RCTGenerateFormBoundary()
|
|||
@implementation RCTNetworking
|
||||
{
|
||||
NSMutableDictionary *_tasksByRequestID;
|
||||
NSArray *_handlers;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
@ -129,12 +130,65 @@ static NSString *RCTGenerateFormBoundary()
|
|||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)init
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_tasksByRequestID = [NSMutableDictionary new];
|
||||
// get handlers
|
||||
NSMutableArray *handlers = [NSMutableArray array];
|
||||
for (id<RCTBridgeModule> module in bridge.modules.allValues) {
|
||||
if ([module conformsToProtocol:@protocol(RCTURLRequestHandler)]) {
|
||||
[handlers addObject:module];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
|
||||
// Sort handlers in reverse priority order (highest priority first)
|
||||
[handlers sortUsingComparator:^NSComparisonResult(id<RCTURLRequestHandler> a, id<RCTURLRequestHandler> b) {
|
||||
float priorityA = [a respondsToSelector:@selector(handlerPriority)] ? [a handlerPriority] : 0;
|
||||
float priorityB = [b respondsToSelector:@selector(handlerPriority)] ? [b handlerPriority] : 0;
|
||||
if (priorityA > priorityB) {
|
||||
return NSOrderedAscending;
|
||||
} else if (priorityA < priorityB) {
|
||||
return NSOrderedDescending;
|
||||
} else {
|
||||
return NSOrderedSame;
|
||||
}
|
||||
}];
|
||||
|
||||
_bridge = bridge;
|
||||
_handlers = handlers;
|
||||
_tasksByRequestID = [NSMutableDictionary new];
|
||||
}
|
||||
|
||||
- (id<RCTURLRequestHandler>)handlerForRequest:(NSURLRequest *)request
|
||||
{
|
||||
if (RCT_DEBUG) {
|
||||
// Check for handler conflicts
|
||||
float previousPriority = 0;
|
||||
id<RCTURLRequestHandler> previousHandler = nil;
|
||||
for (id<RCTURLRequestHandler> handler in _handlers) {
|
||||
if ([handler canHandleRequest:request]) {
|
||||
float priority = [handler respondsToSelector:@selector(handlerPriority)] ? [handler handlerPriority] : 0;
|
||||
if (previousHandler) {
|
||||
if (priority == previousPriority) {
|
||||
RCTLogError(@"The RCTURLRequestHandlers %@ and %@ both reported that"
|
||||
" they can handle the request %@, and have equal priority"
|
||||
" (%g). This could result in non-deterministic behavior.",
|
||||
handler, previousHandler, request, priority);
|
||||
}
|
||||
} else {
|
||||
previousHandler = handler;
|
||||
previousPriority = priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normal code path
|
||||
for (id<RCTURLRequestHandler> handler in _handlers) {
|
||||
if ([handler canHandleRequest:request]) {
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (RCTURLRequestCancellationBlock)buildRequest:(NSDictionary *)query
|
||||
|
@ -169,37 +223,9 @@ RCT_EXPORT_MODULE()
|
|||
}];
|
||||
}
|
||||
|
||||
- (id<RCTURLRequestHandler>)handlerForRequest:(NSURLRequest *)request
|
||||
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
||||
{
|
||||
NSMutableArray *handlers = [NSMutableArray array];
|
||||
for (id<RCTBridgeModule> module in _bridge.modules.allValues) {
|
||||
if ([module conformsToProtocol:@protocol(RCTURLRequestHandler)]) {
|
||||
if ([(id<RCTURLRequestHandler>)module canHandleRequest:request]) {
|
||||
[handlers addObject:module];
|
||||
}
|
||||
}
|
||||
}
|
||||
[handlers sortUsingComparator:^NSComparisonResult(id<RCTURLRequestHandler> a, id<RCTURLRequestHandler> b) {
|
||||
float priorityA = [a respondsToSelector:@selector(handlerPriority)] ? [a handlerPriority] : 0;
|
||||
float priorityB = [b respondsToSelector:@selector(handlerPriority)] ? [b handlerPriority] : 0;
|
||||
if (priorityA < priorityB) {
|
||||
return NSOrderedAscending;
|
||||
} else if (priorityA > priorityB) {
|
||||
return NSOrderedDescending;
|
||||
} else {
|
||||
RCTLogError(@"The RCTURLRequestHandlers %@ and %@ both reported that"
|
||||
" they can handle the request %@, and have equal priority"
|
||||
" (%g). This could result in non-deterministic behavior.",
|
||||
a, b, request, priorityA);
|
||||
|
||||
return NSOrderedSame;
|
||||
}
|
||||
}];
|
||||
id<RCTURLRequestHandler> handler = handlers.lastObject;
|
||||
if (!handler) {
|
||||
RCTLogError(@"No suitable request handler found for %@", request.URL);
|
||||
}
|
||||
return handler;
|
||||
return [self handlerForRequest:request] != nil;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -234,13 +260,13 @@ RCT_EXPORT_MODULE()
|
|||
if (request) {
|
||||
|
||||
__block RCTURLRequestCancellationBlock cancellationBlock = nil;
|
||||
RCTDownloadTask *task = [self downloadTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
RCTNetworkTask *task = [self networkTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
cancellationBlock = callback(error, data ? @{@"body": data, @"contentType": RCTNullIfNil(response.MIMEType)} : nil);
|
||||
}];
|
||||
|
||||
[task start];
|
||||
|
||||
__weak RCTDownloadTask *weakTask = task;
|
||||
__weak RCTNetworkTask *weakTask = task;
|
||||
return ^{
|
||||
[weakTask cancel];
|
||||
if (cancellationBlock) {
|
||||
|
@ -259,7 +285,7 @@ RCT_EXPORT_MODULE()
|
|||
return callback(nil, nil);
|
||||
}
|
||||
|
||||
- (void)sendData:(NSData *)data forTask:(RCTDownloadTask *)task
|
||||
- (void)sendData:(NSData *)data forTask:(RCTNetworkTask *)task
|
||||
{
|
||||
if (data.length == 0) {
|
||||
return;
|
||||
|
@ -278,7 +304,7 @@ RCT_EXPORT_MODULE()
|
|||
if (!responseText && data.length) {
|
||||
|
||||
// We don't have an encoding, or the encoding is incorrect, so now we
|
||||
// try to guess (unfortunately, this feature is available of iOS 8+ only)
|
||||
// try to guess (unfortunately, this feature is available in iOS 8+ only)
|
||||
if ([NSString respondsToSelector:@selector(stringEncodingForData:
|
||||
encodingOptions:
|
||||
convertedString:
|
||||
|
@ -305,7 +331,7 @@ RCT_EXPORT_MODULE()
|
|||
incrementalUpdates:(BOOL)incrementalUpdates
|
||||
responseSender:(RCTResponseSenderBlock)responseSender
|
||||
{
|
||||
__block RCTDownloadTask *task;
|
||||
__block RCTNetworkTask *task;
|
||||
|
||||
RCTURLRequestProgressBlock uploadProgressBlock = ^(int64_t progress, int64_t total) {
|
||||
dispatch_async(_methodQueue, ^{
|
||||
|
@ -355,7 +381,7 @@ RCT_EXPORT_MODULE()
|
|||
});
|
||||
};
|
||||
|
||||
task = [self downloadTaskWithRequest:request completionBlock:completionBlock];
|
||||
task = [self networkTaskWithRequest:request completionBlock:completionBlock];
|
||||
task.incrementalDataBlock = incrementalDataBlock;
|
||||
task.responseBlock = responseBlock;
|
||||
task.uploadProgressBlock = uploadProgressBlock;
|
||||
|
@ -370,15 +396,16 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
#pragma mark - Public API
|
||||
|
||||
- (RCTDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
|
||||
- (RCTNetworkTask *)networkTaskWithRequest:(NSURLRequest *)request
|
||||
completionBlock:(RCTURLRequestCompletionBlock)completionBlock
|
||||
{
|
||||
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
|
||||
if (!handler) {
|
||||
RCTLogError(@"No suitable URL request handler found for %@", request.URL);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [[RCTDownloadTask alloc] initWithRequest:request
|
||||
return [[RCTNetworkTask alloc] initWithRequest:request
|
||||
handler:handler
|
||||
completionBlock:completionBlock];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue