mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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:
committed by
facebook-github-bot-4
parent
31f9a690f3
commit
1076f4a172
@@ -24,6 +24,8 @@ var {
|
|||||||
ActivityIndicatorIOS
|
ActivityIndicatorIOS
|
||||||
} = React;
|
} = 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 ImageCapInsetsExample = require('./ImageCapInsetsExample');
|
||||||
|
|
||||||
var NetworkImageExample = React.createClass({
|
var NetworkImageExample = React.createClass({
|
||||||
@@ -335,6 +337,18 @@ exports.examples = [
|
|||||||
},
|
},
|
||||||
platform: 'ios',
|
platform: 'ios',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Base64 image',
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
style={styles.base64}
|
||||||
|
source={{uri: base64Icon, scale: 3}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
platform: 'ios',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Cap Insets',
|
title: 'Cap Insets',
|
||||||
description:
|
description:
|
||||||
@@ -399,4 +413,9 @@ var styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
height: 200,
|
height: 200,
|
||||||
},
|
},
|
||||||
|
base64: {
|
||||||
|
flex: 1,
|
||||||
|
height: 50,
|
||||||
|
resizeMode: 'contain',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ typedef RCTImageLoaderCancellationBlock (^RCTImageURLLoaderLoadImageURLHandler)(
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
typedef BOOL (^RCTImageDecoderCanDecodeImageDataHandler)(NSData *imageData);
|
typedef BOOL (^RCTImageDataDecoderCanDecodeImageDataHandler)(NSData *imageData);
|
||||||
typedef RCTImageLoaderCancellationBlock (^RCTImageDecoderDecodeImageDataHandler)(NSData *imageData, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler);
|
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
|
- (instancetype)initWithPriority:(float)priority
|
||||||
canDecodeImageDataHandler:(RCTImageDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler
|
canDecodeImageDataHandler:(RCTImageDataDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler
|
||||||
decodeImageDataHandler:(RCTImageDecoderDecodeImageDataHandler)decodeImageDataHandler;
|
decodeImageDataHandler:(RCTImageDataDecoderDecodeImageDataHandler)decodeImageDataHandler;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
return _loadImageURLHandler(imageURL, size, scale, resizeMode, progressHandler, completionHandler);
|
return _loadImageURLHandler(imageURL, size, scale, resizeMode, progressHandler, completionHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)imageLoaderPriority
|
- (float)loaderPriority
|
||||||
{
|
{
|
||||||
return _priority;
|
return _priority;
|
||||||
}
|
}
|
||||||
@@ -61,8 +61,8 @@
|
|||||||
|
|
||||||
@implementation RCTConcreteImageDecoder
|
@implementation RCTConcreteImageDecoder
|
||||||
{
|
{
|
||||||
RCTImageDecoderCanDecodeImageDataHandler _canDecodeImageDataHandler;
|
RCTImageDataDecoderCanDecodeImageDataHandler _canDecodeImageDataHandler;
|
||||||
RCTImageDecoderDecodeImageDataHandler _decodeImageDataHandler;
|
RCTImageDataDecoderDecodeImageDataHandler _decodeImageDataHandler;
|
||||||
float _priority;
|
float _priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
return nil;
|
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])) {
|
if ((self = [super init])) {
|
||||||
_canDecodeImageDataHandler = [canDecodeImageDataHandler copy];
|
_canDecodeImageDataHandler = [canDecodeImageDataHandler copy];
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
return _decodeImageDataHandler(imageData, size, scale, resizeMode, completionHandler);
|
return _decodeImageDataHandler(imageData, size, scale, resizeMode, completionHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (float)imageDecoderPriority
|
- (float)decoderPriority
|
||||||
{
|
{
|
||||||
return _priority;
|
return _priority;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
|
|||||||
NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO];
|
NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO];
|
||||||
UIImage *image = [[UIImage alloc] initWithData:data];
|
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;
|
return YES;
|
||||||
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
|
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
|
||||||
XCTAssertEqualObjects(imageData, data);
|
XCTAssertEqualObjects(imageData, data);
|
||||||
@@ -118,7 +118,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
|
|||||||
NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO];
|
NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO];
|
||||||
UIImage *image = [[UIImage alloc] initWithData:data];
|
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;
|
return YES;
|
||||||
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
|
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
|
||||||
XCTAssertEqualObjects(imageData, data);
|
XCTAssertEqualObjects(imageData, data);
|
||||||
@@ -126,7 +126,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
|
|||||||
return nil;
|
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;
|
return YES;
|
||||||
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(__unused NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderCompletionBlock completionHandler) {
|
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(__unused NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderCompletionBlock completionHandler) {
|
||||||
XCTFail(@"Should not have used decoder2");
|
XCTFail(@"Should not have used decoder2");
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
|
|
||||||
#import "RCTImageLoader.h"
|
#import "RCTImageLoader.h"
|
||||||
|
|
||||||
@interface RCTGIFImageDecoder : NSObject <RCTImageDecoder>
|
@interface RCTGIFImageDecoder : NSObject <RCTImageDataDecoder>
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
|
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
|
||||||
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
|
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
|
||||||
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
@@ -54,8 +53,6 @@
|
|||||||
354631661B69857700AA0B86 /* RCTImageEditingManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageEditingManager.h; sourceTree = "<group>"; };
|
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>"; };
|
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; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -76,8 +73,6 @@
|
|||||||
13EF7F7E1BC825B1003F47DD /* RCTXCAssetImageLoader.m */,
|
13EF7F7E1BC825B1003F47DD /* RCTXCAssetImageLoader.m */,
|
||||||
1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */,
|
1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */,
|
||||||
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */,
|
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */,
|
||||||
58B511891A9E6BD600147676 /* RCTImageDownloader.h */,
|
|
||||||
58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */,
|
|
||||||
354631661B69857700AA0B86 /* RCTImageEditingManager.h */,
|
354631661B69857700AA0B86 /* RCTImageEditingManager.h */,
|
||||||
354631671B69857700AA0B86 /* RCTImageEditingManager.m */,
|
354631671B69857700AA0B86 /* RCTImageEditingManager.m */,
|
||||||
143879361AAD32A300F088A5 /* RCTImageLoader.h */,
|
143879361AAD32A300F088A5 /* RCTImageLoader.h */,
|
||||||
@@ -166,7 +161,6 @@
|
|||||||
files = (
|
files = (
|
||||||
13EF7F0C1BC42D4E003F47DD /* RCTVirtualImageManager.m in Sources */,
|
13EF7F0C1BC42D4E003F47DD /* RCTVirtualImageManager.m in Sources */,
|
||||||
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */,
|
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */,
|
||||||
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */,
|
|
||||||
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
|
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
|
||||||
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */,
|
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */,
|
||||||
143879381AAD32A300F088A5 /* RCTImageLoader.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
|
* 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
|
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
|
||||||
size:(CGSize)size
|
size:(CGSize)size
|
||||||
@@ -66,7 +67,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
|
|||||||
@end
|
@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
|
* loaders are also bridge modules, so should be registered using
|
||||||
* RCT_EXPORT_MODULE().
|
* RCT_EXPORT_MODULE().
|
||||||
*/
|
*/
|
||||||
@@ -85,18 +86,23 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
|
|||||||
* has finished. The method should also return a cancellation block, if
|
* has finished. The method should also return a cancellation block, if
|
||||||
* applicable.
|
* 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
|
@optional
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If more than one RCTImageURLLoader responds YES to `-canLoadImageURL:`
|
* 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
|
* 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.
|
* undefined.
|
||||||
*/
|
*/
|
||||||
- (float)imageLoaderPriority;
|
- (float)loaderPriority;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -104,7 +110,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
|
|||||||
* Provides the interface needed to register an image decoder. Image decoders
|
* Provides the interface needed to register an image decoder. Image decoders
|
||||||
* are also bridge modules, so should be registered using RCT_EXPORT_MODULE().
|
* 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.
|
* 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
|
* completionHandler when the decoding operation has finished. The method
|
||||||
* should also return a cancellation block, if applicable.
|
* 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
|
@optional
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If more than one RCTImageDecoder responds YES to `-canDecodeImageData:`
|
* If more than one RCTImageDataDecoder responds YES to `-canDecodeImageData:`
|
||||||
* then `imageDecoderPriority` is used to determine which one to use. The
|
* then `decoderPriority` is used to determine which one to use. The decoder
|
||||||
* handler with the highest priority will be selected. Default priority is zero.
|
* 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 two or more valid decoders have the same priority, the selection order is
|
||||||
* undefined.
|
* undefined.
|
||||||
*/
|
*/
|
||||||
- (float)imageDecoderPriority;
|
- (float)decoderPriority;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
|
|
||||||
#import "RCTConvert.h"
|
#import "RCTConvert.h"
|
||||||
#import "RCTDefines.h"
|
#import "RCTDefines.h"
|
||||||
#import "RCTImageDownloader.h"
|
|
||||||
#import "RCTImageUtils.h"
|
#import "RCTImageUtils.h"
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
|
#import "RCTNetworking.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSError *error, UIImage *image)
|
static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSError *error, UIImage *image)
|
||||||
@@ -44,11 +44,131 @@ static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSEr
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation RCTImageLoader
|
@implementation RCTImageLoader
|
||||||
|
{
|
||||||
|
NSArray *_loaders;
|
||||||
|
NSArray *_decoders;
|
||||||
|
NSURLCache *_cache;
|
||||||
|
}
|
||||||
|
|
||||||
@synthesize bridge = _bridge;
|
@synthesize bridge = _bridge;
|
||||||
|
|
||||||
RCT_EXPORT_MODULE()
|
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
|
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
|
||||||
callback:(RCTImageLoaderCompletionBlock)callback
|
callback:(RCTImageLoaderCompletionBlock)callback
|
||||||
{
|
{
|
||||||
@@ -60,35 +180,6 @@ RCT_EXPORT_MODULE()
|
|||||||
completionBlock:callback];
|
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
|
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
|
||||||
size:(CGSize)size
|
size:(CGSize)size
|
||||||
scale:(CGFloat)scale
|
scale:(CGFloat)scale
|
||||||
@@ -101,58 +192,125 @@ RCT_EXPORT_MODULE()
|
|||||||
return ^{};
|
return ^{};
|
||||||
}
|
}
|
||||||
|
|
||||||
NSURL *requestURL = [RCTConvert NSURL:imageTag];
|
// Ensure progress is dispatched on main thread
|
||||||
id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForRequest:requestURL];
|
RCTImageLoaderProgressBlock progressHandler = nil;
|
||||||
if (!loadHandler) {
|
if (progressBlock) {
|
||||||
RCTLogError(@"No suitable image URL loader found for %@", imageTag);
|
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 ^{};
|
||||||
}
|
}
|
||||||
|
|
||||||
return [loadHandler loadImageForURL:requestURL size:size scale:scale resizeMode:resizeMode progressHandler:^(int64_t progress, int64_t total) {
|
// Use networking module to load image
|
||||||
if (!progressBlock) {
|
if ([_bridge.networking canHandleRequest:request]) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([NSThread isMainThread]) {
|
__weak RCTImageLoader *weakSelf = self;
|
||||||
progressBlock(progress, total);
|
__block RCTImageLoaderCancellationBlock decodeCancel = nil;
|
||||||
} else {
|
RCTURLRequestCompletionBlock processResponse =
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
^(NSURLResponse *response, NSData *data, __unused NSError *error) {
|
||||||
progressBlock(progress, total);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} completionHandler:^(NSError *error, UIImage *image) {
|
|
||||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id<RCTImageDecoder>)imageDecoderForRequest:(NSData *)imageData
|
// Check for http errors
|
||||||
{
|
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||||
NSMutableArray *handlers = [NSMutableArray array];
|
NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
|
||||||
for (id<RCTBridgeModule> module in _bridge.modules.allValues) {
|
if (statusCode != 200) {
|
||||||
if ([module conformsToProtocol:@protocol(RCTImageDecoder)]) {
|
completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain
|
||||||
if ([(id<RCTImageDecoder>)module canDecodeImageData:imageData]) {
|
code:statusCode
|
||||||
[handlers addObject:module];
|
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
|
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data
|
||||||
@@ -161,20 +319,22 @@ RCT_EXPORT_MODULE()
|
|||||||
resizeMode:(UIViewContentMode)resizeMode
|
resizeMode:(UIViewContentMode)resizeMode
|
||||||
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
|
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
|
||||||
{
|
{
|
||||||
id<RCTImageDecoder> imageDecoder = [self imageDecoderForRequest:data];
|
id<RCTImageDataDecoder> imageDecoder = [self imageDataDecoderForData:data];
|
||||||
if (imageDecoder) {
|
if (imageDecoder) {
|
||||||
return [imageDecoder decodeImageData:data size:size scale:scale resizeMode:resizeMode completionHandler:^(NSError *error, UIImage *image) {
|
return [imageDecoder decodeImageData:data
|
||||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
size:size
|
||||||
}];
|
scale:scale
|
||||||
|
resizeMode:resizeMode
|
||||||
|
completionHandler:completionBlock];
|
||||||
} else {
|
} else {
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
UIImage *image = [UIImage imageWithData:data scale:scale];
|
UIImage *image = [UIImage imageWithData:data scale:scale];
|
||||||
if (image) {
|
if (image) {
|
||||||
RCTDispatchCallbackOnMainQueue(completionBlock, nil, image);
|
completionBlock(nil, image);
|
||||||
} else {
|
} else {
|
||||||
NSString *errorMessage = [NSString stringWithFormat:@"Error decoding image data <NSData %p; %tu bytes>", data, data.length];
|
NSString *errorMessage = [NSString stringWithFormat:@"Error decoding image data <NSData %p; %tu bytes>", data, data.length];
|
||||||
NSError *finalError = RCTErrorWithMessage(errorMessage);
|
NSError *finalError = RCTErrorWithMessage(errorMessage);
|
||||||
RCTDispatchCallbackOnMainQueue(completionBlock, finalError, nil);
|
completionBlock(finalError, nil);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return ^{};
|
return ^{};
|
||||||
@@ -185,18 +345,20 @@ RCT_EXPORT_MODULE()
|
|||||||
|
|
||||||
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
||||||
{
|
{
|
||||||
id<RCTImageURLLoader> handler = [self imageURLLoaderForRequest:request.URL];
|
NSURL *requestURL = request.URL;
|
||||||
|
for (id<RCTBridgeModule> module in _bridge.modules.allValues) {
|
||||||
// RCTImageDownloader is an image plugin that uses the networking stack.
|
if ([module conformsToProtocol:@protocol(RCTImageURLLoader)] &&
|
||||||
// We don't want to route any network calls through the image downloader
|
[(id<RCTImageURLLoader>)module canLoadImageURL:requestURL]) {
|
||||||
// as that would cause cyclical dependencies.
|
return YES;
|
||||||
return handler && ![handler isKindOfClass:[RCTImageDownloader class]];
|
}
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)sendRequest:(NSURLRequest *)request withDelegate:(id<RCTURLRequestDelegate>)delegate
|
- (id)sendRequest:(NSURLRequest *)request withDelegate:(id<RCTURLRequestDelegate>)delegate
|
||||||
{
|
{
|
||||||
__block RCTImageLoaderCancellationBlock requestToken;
|
__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) {
|
if (error) {
|
||||||
[delegate URLRequest:requestToken didCompleteWithError:error];
|
[delegate URLRequest:requestToken didCompleteWithError:error];
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#import "RCTImageLoader.h"
|
#import "RCTImageLoader.h"
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
#import "RCTConvert.h"
|
#import "RCTConvert.h"
|
||||||
|
#import "RCTUIManager.h"
|
||||||
|
|
||||||
@implementation RCTShadowVirtualImage
|
@implementation RCTShadowVirtualImage
|
||||||
{
|
{
|
||||||
@@ -38,9 +39,11 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init)
|
|||||||
|
|
||||||
__weak RCTShadowVirtualImage *weakSelf = self;
|
__weak RCTShadowVirtualImage *weakSelf = self;
|
||||||
[_bridge.imageLoader loadImageWithTag:imageTag size:CGSizeZero scale:scale resizeMode:UIViewContentModeScaleToFill progressBlock:nil completionBlock:^(NSError *error, UIImage *image) {
|
[_bridge.imageLoader loadImageWithTag:imageTag size:CGSizeZero scale:scale resizeMode:UIViewContentModeScaleToFill progressBlock:nil completionBlock:^(NSError *error, UIImage *image) {
|
||||||
RCTShadowVirtualImage *strongSelf = weakSelf;
|
dispatch_async(_bridge.uiManager.methodQueue, ^{
|
||||||
strongSelf->_image = image;
|
RCTShadowVirtualImage *strongSelf = weakSelf;
|
||||||
[strongSelf dirtyText];
|
strongSelf->_image = image;
|
||||||
|
[strongSelf dirtyText];
|
||||||
|
});
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,6 @@ RCT_EXPORT_MODULE()
|
|||||||
return RCTIsXCAssetURL(requestURL);
|
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
|
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
||||||
{
|
{
|
||||||
__block BOOL cancelled = NO;
|
__block BOOL cancelled = NO;
|
||||||
|
|||||||
@@ -7,15 +7,12 @@
|
|||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "RCTBridge.h"
|
#import "RCTURLRequestHandler.h"
|
||||||
#import "RCTImageLoader.h"
|
#import "RCTInvalidating.h"
|
||||||
|
|
||||||
@interface RCTImageDownloader : NSObject <RCTImageURLLoader>
|
/**
|
||||||
|
* This is the default RCTURLRequestHandler implementation for data URL requests.
|
||||||
@end
|
*/
|
||||||
|
@interface RCTDataRequestHandler : NSObject <RCTURLRequestHandler, RCTInvalidating>
|
||||||
@interface RCTBridge (RCTImageDownloader)
|
|
||||||
|
|
||||||
@property (nonatomic, readonly) RCTImageDownloader *imageDownloader;
|
|
||||||
|
|
||||||
@end
|
@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 <MobileCoreServices/MobileCoreServices.h>
|
||||||
|
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
@implementation RCTFileRequestHandler
|
@implementation RCTFileRequestHandler
|
||||||
{
|
{
|
||||||
NSOperationQueue *_fileQueue;
|
NSOperationQueue *_fileQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@synthesize methodQueue = _methodQueue;
|
|
||||||
|
|
||||||
RCT_EXPORT_MODULE()
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
- (void)invalidate
|
- (void)invalidate
|
||||||
@@ -28,7 +28,9 @@ RCT_EXPORT_MODULE()
|
|||||||
|
|
||||||
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
- (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
|
- (NSOperation *)sendRequest:(NSURLRequest *)request
|
||||||
|
|||||||
@@ -7,8 +7,9 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* 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 */; };
|
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 = (); }; };
|
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 */; };
|
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */; };
|
||||||
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.m */; };
|
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.m */; };
|
||||||
@@ -27,10 +28,12 @@
|
|||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference 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; };
|
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; };
|
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>"; };
|
13D6D6681B5FCF8200883BE9 /* RCTNetworkTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTNetworkTask.h; sourceTree = "<group>"; };
|
||||||
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDownloadTask.m; 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>"; };
|
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>"; };
|
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>"; };
|
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTHTTPRequestHandler.h; sourceTree = "<group>"; };
|
||||||
@@ -54,12 +57,14 @@
|
|||||||
58B511D21A9E6C8500147676 = {
|
58B511D21A9E6C8500147676 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
13D6D6681B5FCF8200883BE9 /* RCTDownloadTask.h */,
|
13D6D6681B5FCF8200883BE9 /* RCTNetworkTask.h */,
|
||||||
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */,
|
13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */,
|
||||||
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */,
|
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */,
|
||||||
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */,
|
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */,
|
||||||
13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */,
|
13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */,
|
||||||
13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */,
|
13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */,
|
||||||
|
134E96981BCEB7F800AFFDA1 /* RCTDataRequestHandler.h */,
|
||||||
|
134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */,
|
||||||
1372B7351AB03E7B00659ED6 /* RCTNetInfo.h */,
|
1372B7351AB03E7B00659ED6 /* RCTNetInfo.h */,
|
||||||
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */,
|
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */,
|
||||||
58B512061A9E6CE300147676 /* RCTNetworking.h */,
|
58B512061A9E6CE300147676 /* RCTNetworking.h */,
|
||||||
@@ -134,8 +139,9 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */,
|
13D6D66A1B5FCF8200883BE9 /* RCTNetworkTask.m in Sources */,
|
||||||
13EF800E1BCBE015003F47DD /* RCTFileRequestHandler.m in Sources */,
|
13EF800E1BCBE015003F47DD /* RCTFileRequestHandler.m in Sources */,
|
||||||
|
134E969A1BCEB7F800AFFDA1 /* RCTDataRequestHandler.m in Sources */,
|
||||||
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */,
|
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */,
|
||||||
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */,
|
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */,
|
||||||
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.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 (^RCTURLRequestProgressBlock)(int64_t progress, int64_t total);
|
||||||
typedef void (^RCTURLRequestResponseBlock)(NSURLResponse *response);
|
typedef void (^RCTURLRequestResponseBlock)(NSURLResponse *response);
|
||||||
|
|
||||||
@interface RCTDownloadTask : NSObject <RCTURLRequestDelegate>
|
@interface RCTNetworkTask : NSObject <RCTURLRequestDelegate>
|
||||||
|
|
||||||
@property (nonatomic, readonly) NSURLRequest *request;
|
@property (nonatomic, readonly) NSURLRequest *request;
|
||||||
@property (nonatomic, readonly) NSNumber *requestID;
|
@property (nonatomic, readonly) NSNumber *requestID;
|
||||||
@@ -7,15 +7,15 @@
|
|||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "RCTDownloadTask.h"
|
#import "RCTNetworkTask.h"
|
||||||
|
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
|
|
||||||
@implementation RCTDownloadTask
|
@implementation RCTNetworkTask
|
||||||
{
|
{
|
||||||
NSMutableData *_data;
|
NSMutableData *_data;
|
||||||
id<RCTURLRequestHandler> _handler;
|
id<RCTURLRequestHandler> _handler;
|
||||||
RCTDownloadTask *_selfReference;
|
RCTNetworkTask *_selfReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithRequest:(NSURLRequest *)request
|
- (instancetype)initWithRequest:(NSURLRequest *)request
|
||||||
@@ -10,12 +10,21 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
#import "RCTDownloadTask.h"
|
#import "RCTNetworkTask.h"
|
||||||
|
|
||||||
@interface RCTNetworking : NSObject <RCTBridgeModule>
|
@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
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
#import "RCTAssert.h"
|
#import "RCTAssert.h"
|
||||||
#import "RCTConvert.h"
|
#import "RCTConvert.h"
|
||||||
#import "RCTDownloadTask.h"
|
#import "RCTNetworkTask.h"
|
||||||
#import "RCTURLRequestHandler.h"
|
#import "RCTURLRequestHandler.h"
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTEventDispatcher.h"
|
||||||
#import "RCTHTTPRequestHandler.h"
|
#import "RCTHTTPRequestHandler.h"
|
||||||
@@ -37,10 +37,10 @@ typedef RCTURLRequestCancellationBlock (^RCTHTTPQueryResult)(NSError *error, NSD
|
|||||||
|
|
||||||
@implementation RCTHTTPFormDataHelper
|
@implementation RCTHTTPFormDataHelper
|
||||||
{
|
{
|
||||||
NSMutableArray *parts;
|
NSMutableArray *_parts;
|
||||||
NSMutableData *multipartBody;
|
NSMutableData *_multipartBody;
|
||||||
RCTHTTPQueryResult _callback;
|
RCTHTTPQueryResult _callback;
|
||||||
NSString *boundary;
|
NSString *_boundary;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NSString *RCTGenerateFormBoundary()
|
static NSString *RCTGenerateFormBoundary()
|
||||||
@@ -56,19 +56,19 @@ static NSString *RCTGenerateFormBoundary()
|
|||||||
return [[NSString alloc] initWithBytesNoCopy:bytes length:boundaryLength encoding:NSUTF8StringEncoding freeWhenDone:YES];
|
return [[NSString alloc] initWithBytesNoCopy:bytes length:boundaryLength encoding:NSUTF8StringEncoding freeWhenDone:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (RCTURLRequestCancellationBlock)process:(NSArray *)formData
|
- (RCTURLRequestCancellationBlock)process:(NSDictionaryArray *)formData
|
||||||
callback:(RCTHTTPQueryResult)callback
|
callback:(RCTHTTPQueryResult)callback
|
||||||
{
|
{
|
||||||
if (formData.count == 0) {
|
if (formData.count == 0) {
|
||||||
return callback(nil, nil);
|
return callback(nil, nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
parts = [formData mutableCopy];
|
_parts = [formData mutableCopy];
|
||||||
_callback = callback;
|
_callback = callback;
|
||||||
multipartBody = [NSMutableData new];
|
_multipartBody = [NSMutableData new];
|
||||||
boundary = RCTGenerateFormBoundary();
|
_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];
|
return [self handleResult:result error:error];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -81,37 +81,37 @@ static NSString *RCTGenerateFormBoundary()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start with boundary.
|
// Start with boundary.
|
||||||
[multipartBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
|
[_multipartBody appendData:[[NSString stringWithFormat:@"--%@\r\n", _boundary]
|
||||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
|
|
||||||
// Print headers.
|
// Print headers.
|
||||||
NSMutableDictionary *headers = [parts[0][@"headers"] mutableCopy];
|
NSMutableDictionary *headers = [_parts[0][@"headers"] mutableCopy];
|
||||||
NSString *partContentType = result[@"contentType"];
|
NSString *partContentType = result[@"contentType"];
|
||||||
if (partContentType != nil) {
|
if (partContentType != nil) {
|
||||||
headers[@"content-type"] = partContentType;
|
headers[@"content-type"] = partContentType;
|
||||||
}
|
}
|
||||||
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey, NSString *parameterValue, BOOL *stop) {
|
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey, NSString *parameterValue, BOOL *stop) {
|
||||||
[multipartBody appendData:[[NSString stringWithFormat:@"%@: %@\r\n", parameterKey, parameterValue]
|
[_multipartBody appendData:[[NSString stringWithFormat:@"%@: %@\r\n", parameterKey, parameterValue]
|
||||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// Add the body.
|
// Add the body.
|
||||||
[multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
[_multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
[multipartBody appendData:result[@"body"]];
|
[_multipartBody appendData:result[@"body"]];
|
||||||
[multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
[_multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
|
|
||||||
[parts removeObjectAtIndex:0];
|
[_parts removeObjectAtIndex:0];
|
||||||
if (parts.count) {
|
if (_parts.count) {
|
||||||
return [_networker processDataForHTTPQuery:parts[0] callback:^(NSError *err, NSDictionary *res) {
|
return [_networker processDataForHTTPQuery:_parts[0] callback:^(NSError *err, NSDictionary *res) {
|
||||||
return [self handleResult:res error:err];
|
return [self handleResult:res error:err];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
// We've processed the last item. Finish and return.
|
// We've processed the last item. Finish and return.
|
||||||
[multipartBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary]
|
[_multipartBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", _boundary]
|
||||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=\"%@\"", boundary];
|
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=\"%@\"", _boundary];
|
||||||
return _callback(nil, @{@"body": multipartBody, @"contentType": contentType});
|
return _callback(nil, @{@"body": _multipartBody, @"contentType": contentType});
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -122,6 +122,7 @@ static NSString *RCTGenerateFormBoundary()
|
|||||||
@implementation RCTNetworking
|
@implementation RCTNetworking
|
||||||
{
|
{
|
||||||
NSMutableDictionary *_tasksByRequestID;
|
NSMutableDictionary *_tasksByRequestID;
|
||||||
|
NSArray *_handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@synthesize bridge = _bridge;
|
@synthesize bridge = _bridge;
|
||||||
@@ -129,12 +130,65 @@ static NSString *RCTGenerateFormBoundary()
|
|||||||
|
|
||||||
RCT_EXPORT_MODULE()
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
- (instancetype)init
|
- (void)setBridge:(RCTBridge *)bridge
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
// get handlers
|
||||||
_tasksByRequestID = [NSMutableDictionary new];
|
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
|
- (RCTURLRequestCancellationBlock)buildRequest:(NSDictionary *)query
|
||||||
@@ -169,37 +223,9 @@ RCT_EXPORT_MODULE()
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id<RCTURLRequestHandler>)handlerForRequest:(NSURLRequest *)request
|
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
||||||
{
|
{
|
||||||
NSMutableArray *handlers = [NSMutableArray array];
|
return [self handlerForRequest:request] != nil;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -234,13 +260,13 @@ RCT_EXPORT_MODULE()
|
|||||||
if (request) {
|
if (request) {
|
||||||
|
|
||||||
__block RCTURLRequestCancellationBlock cancellationBlock = nil;
|
__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);
|
cancellationBlock = callback(error, data ? @{@"body": data, @"contentType": RCTNullIfNil(response.MIMEType)} : nil);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[task start];
|
[task start];
|
||||||
|
|
||||||
__weak RCTDownloadTask *weakTask = task;
|
__weak RCTNetworkTask *weakTask = task;
|
||||||
return ^{
|
return ^{
|
||||||
[weakTask cancel];
|
[weakTask cancel];
|
||||||
if (cancellationBlock) {
|
if (cancellationBlock) {
|
||||||
@@ -259,7 +285,7 @@ RCT_EXPORT_MODULE()
|
|||||||
return callback(nil, nil);
|
return callback(nil, nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)sendData:(NSData *)data forTask:(RCTDownloadTask *)task
|
- (void)sendData:(NSData *)data forTask:(RCTNetworkTask *)task
|
||||||
{
|
{
|
||||||
if (data.length == 0) {
|
if (data.length == 0) {
|
||||||
return;
|
return;
|
||||||
@@ -278,7 +304,7 @@ RCT_EXPORT_MODULE()
|
|||||||
if (!responseText && data.length) {
|
if (!responseText && data.length) {
|
||||||
|
|
||||||
// We don't have an encoding, or the encoding is incorrect, so now we
|
// 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:
|
if ([NSString respondsToSelector:@selector(stringEncodingForData:
|
||||||
encodingOptions:
|
encodingOptions:
|
||||||
convertedString:
|
convertedString:
|
||||||
@@ -305,7 +331,7 @@ RCT_EXPORT_MODULE()
|
|||||||
incrementalUpdates:(BOOL)incrementalUpdates
|
incrementalUpdates:(BOOL)incrementalUpdates
|
||||||
responseSender:(RCTResponseSenderBlock)responseSender
|
responseSender:(RCTResponseSenderBlock)responseSender
|
||||||
{
|
{
|
||||||
__block RCTDownloadTask *task;
|
__block RCTNetworkTask *task;
|
||||||
|
|
||||||
RCTURLRequestProgressBlock uploadProgressBlock = ^(int64_t progress, int64_t total) {
|
RCTURLRequestProgressBlock uploadProgressBlock = ^(int64_t progress, int64_t total) {
|
||||||
dispatch_async(_methodQueue, ^{
|
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.incrementalDataBlock = incrementalDataBlock;
|
||||||
task.responseBlock = responseBlock;
|
task.responseBlock = responseBlock;
|
||||||
task.uploadProgressBlock = uploadProgressBlock;
|
task.uploadProgressBlock = uploadProgressBlock;
|
||||||
@@ -370,15 +396,16 @@ RCT_EXPORT_MODULE()
|
|||||||
|
|
||||||
#pragma mark - Public API
|
#pragma mark - Public API
|
||||||
|
|
||||||
- (RCTDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
|
- (RCTNetworkTask *)networkTaskWithRequest:(NSURLRequest *)request
|
||||||
completionBlock:(RCTURLRequestCompletionBlock)completionBlock
|
completionBlock:(RCTURLRequestCompletionBlock)completionBlock
|
||||||
{
|
{
|
||||||
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
|
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
|
RCTLogError(@"No suitable URL request handler found for %@", request.URL);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [[RCTDownloadTask alloc] initWithRequest:request
|
return [[RCTNetworkTask alloc] initWithRequest:request
|
||||||
handler:handler
|
handler:handler
|
||||||
completionBlock:completionBlock];
|
completionBlock:completionBlock];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user