diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m
index e67edb0b4..8de60a199 100644
--- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m
+++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m
@@ -49,7 +49,8 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader]; } launchOptions:nil];
- [bridge.imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) {
+ NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://facebook.github.io/react/img/logo_og.png"]];
+ [bridge.imageLoader loadImageWithURLRequest:urlRequest size:CGSizeMake(100, 100) scale:1.0 clipped:YES resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) {
XCTAssertEqual(progress, 1);
XCTAssertEqual(total, 1);
} completionBlock:^(NSError *loadError, id loadedImage) {
@@ -79,7 +80,8 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader1, loader2]; } launchOptions:nil];
- [bridge.imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) {
+ NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://facebook.github.io/react/img/logo_og.png"]];
+ [bridge.imageLoader loadImageWithURLRequest:urlRequest size:CGSizeMake(100, 100) scale:1.0 clipped:YES resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) {
XCTAssertEqual(progress, 1);
XCTAssertEqual(total, 1);
} completionBlock:^(NSError *loadError, id loadedImage) {
@@ -103,7 +105,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder]; } launchOptions:nil];
- RCTImageLoaderCancellationBlock cancelBlock = [bridge.imageLoader decodeImageDataWithoutClipping:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:RCTResizeModeStretch completionBlock:^(NSError *decodeError, id decodedImage) {
+ RCTImageLoaderCancellationBlock cancelBlock = [bridge.imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 clipped:NO resizeMode:RCTResizeModeStretch completionBlock:^(NSError *decodeError, id decodedImage) {
XCTAssertEqualObjects(decodedImage, image);
XCTAssertNil(decodeError);
}];
@@ -132,7 +134,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder1, decoder2]; } launchOptions:nil];
- RCTImageLoaderCancellationBlock cancelBlock = [bridge.imageLoader decodeImageDataWithoutClipping:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:RCTResizeModeStretch completionBlock:^(NSError *decodeError, id decodedImage) {
+ RCTImageLoaderCancellationBlock cancelBlock = [bridge.imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 clipped:NO resizeMode:RCTResizeModeStretch completionBlock:^(NSError *decodeError, id decodedImage) {
XCTAssertEqualObjects(decodedImage, image);
XCTAssertNil(decodeError);
}];
diff --git a/Libraries/CameraRoll/RCTCameraRollManager.m b/Libraries/CameraRoll/RCTCameraRollManager.m
index d060224cc..7d9e82aa2 100644
--- a/Libraries/CameraRoll/RCTCameraRollManager.m
+++ b/Libraries/CameraRoll/RCTCameraRollManager.m
@@ -82,11 +82,12 @@ RCT_EXPORT_MODULE()
NSString *const RCTErrorUnableToLoad = @"E_UNABLE_TO_LOAD";
NSString *const RCTErrorUnableToSave = @"E_UNABLE_TO_SAVE";
-RCT_EXPORT_METHOD(saveImageWithTag:(NSString *)imageTag
+RCT_EXPORT_METHOD(saveImageWithTag:(NSURLRequest *)imageRequest
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
{
- [_bridge.imageLoader loadImageWithTag:imageTag callback:^(NSError *loadError, UIImage *loadedImage) {
+ [_bridge.imageLoader loadImageWithURLRequest:imageRequest
+ callback:^(NSError *loadError, UIImage *loadedImage) {
if (loadError) {
reject(RCTErrorUnableToLoad, nil, loadError);
return;
diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js
index b2f4718f7..7afec06e8 100644
--- a/Libraries/Image/Image.ios.js
+++ b/Libraries/Image/Image.ios.js
@@ -13,6 +13,7 @@
var EdgeInsetsPropType = require('EdgeInsetsPropType');
var ImageResizeMode = require('ImageResizeMode');
+var ImageSourcePropType = require('ImageSourcePropType');
var ImageStylePropTypes = require('ImageStylePropTypes');
var NativeMethodsMixin = require('NativeMethodsMixin');
var NativeModules = require('NativeModules');
@@ -60,24 +61,32 @@ var Image = React.createClass({
propTypes: {
style: StyleSheetPropType(ImageStylePropTypes),
/**
- * `uri` is a string representing the resource identifier for the image, which
- * could be an http address, a local file path, or the name of a static image
- * resource (which should be wrapped in the `require('./path/to/image.png')` function).
+ * The image source (either a remote URL or a local file resource).
*/
- source: PropTypes.oneOfType([
- PropTypes.shape({
- uri: PropTypes.string,
- }),
- // Opaque type returned by require('./image.jpg')
- PropTypes.number,
- ]),
+ source: ImageSourcePropType,
/**
* A static image to display while loading the image source.
* @platform ios
*/
defaultSource: PropTypes.oneOfType([
PropTypes.shape({
+ /**
+ * `uri` is a string representing the resource identifier for the image, which
+ * should be either a local file path or the name of a static image resource
+ * (which should be wrapped in the `require('./path/to/image.png')` function).
+ */
uri: PropTypes.string,
+ /**
+ * `width` and `height` can be specified if known at build time, in which case
+ * these will be used to set the default `` component dimensions.
+ */
+ width: PropTypes.number,
+ height: PropTypes.number,
+ /**
+ * `scale` is used to indicate the scale factor of the image. Defaults to 1.0 if
+ * unspecified, meaning that one image pixel equates to one display point / DIP.
+ */
+ scale: PropTypes.number,
}),
// Opaque type returned by require('./image.jpg')
PropTypes.number,
@@ -202,7 +211,7 @@ var Image = React.createClass({
},
render: function() {
- var source = resolveAssetSource(this.props.source) || {};
+ var source = resolveAssetSource(this.props.source) || { uri: null, width: undefined, height: undefined };
var {width, height, uri} = source;
var style = flattenStyle([{width, height}, styles.base, this.props.style]) || {};
@@ -216,7 +225,11 @@ var Image = React.createClass({
if (isNetwork && (tintColor || this.props.blurRadius)) {
RawImage = RCTImageView;
}
-
+
+ if (uri === '') {
+ console.warn('source.uri should not be an empty string');
+ }
+
if (this.props.src) {
console.warn('The component requires a `source` property rather than `src`.');
}
diff --git a/Libraries/Image/ImageSourcePropType.js b/Libraries/Image/ImageSourcePropType.js
new file mode 100644
index 000000000..6f6c7bf8f
--- /dev/null
+++ b/Libraries/Image/ImageSourcePropType.js
@@ -0,0 +1,56 @@
+/**
+ * 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.
+ *
+ * @providesModule ImageSourcePropType
+ * @no-flow
+ */
+'use strict';
+
+const PropTypes = require('ReactPropTypes');
+
+const ImageSourcePropType = PropTypes.oneOfType([
+ PropTypes.shape({
+ /**
+ * `uri` is a string representing the resource identifier for the image, which
+ * could be an http address, a local file path, or the name of a static image
+ * resource (which should be wrapped in the `require('./path/to/image.png')`
+ * function).
+ */
+ uri: PropTypes.string,
+ /**
+ * `method` is the HTTP Method to use. Defaults to GET if not specified.
+ */
+ method: PropTypes.string,
+ /**
+ * `headers` is an object representing the HTTP headers to send along with the
+ * request for a remote image.
+ */
+ headers: PropTypes.objectOf(PropTypes.string),
+ /**
+ * `body` is the HTTP body to send with the request. This must be a valid
+ * UTF-8 string, and will be sent exactly as specified, with no
+ * additional encoding (e.g. URL-escaping or base64) applied.
+ */
+ body: PropTypes.string,
+ /**
+ * `width` and `height` can be specified if known at build time, in which case
+ * these will be used to set the default `` component dimensions.
+ */
+ width: PropTypes.number,
+ height: PropTypes.number,
+ /**
+ * `scale` is used to indicate the scale factor of the image. Defaults to 1.0 if
+ * unspecified, meaning that one image pixel equates to one display point / DIP.
+ */
+ scale: PropTypes.number,
+ }),
+ // Opaque type returned by require('./image.jpg')
+ PropTypes.number,
+]);
+
+module.exports = ImageSourcePropType;
diff --git a/Libraries/Image/RCTImageEditingManager.m b/Libraries/Image/RCTImageEditingManager.m
index 9ff231d64..cc8dfd0fa 100644
--- a/Libraries/Image/RCTImageEditingManager.m
+++ b/Libraries/Image/RCTImageEditingManager.m
@@ -28,14 +28,14 @@ RCT_EXPORT_MODULE()
/**
* Crops an image and adds the result to the image store.
*
- * @param imageTag A URL, a string identifying an asset etc.
+ * @param imageRequest An image URL
* @param cropData Dictionary with `offset`, `size` and `displaySize`.
* `offset` and `size` are relative to the full-resolution image size.
* `displaySize` is an optimization - if specified, the image will
* be scaled down to `displaySize` rather than `size`.
* All units are in px (not points).
*/
-RCT_EXPORT_METHOD(cropImage:(NSString *)imageTag
+RCT_EXPORT_METHOD(cropImage:(NSURLRequest *)imageRequest
cropData:(NSDictionary *)cropData
successCallback:(RCTResponseSenderBlock)successCallback
errorCallback:(RCTResponseErrorBlock)errorCallback)
@@ -45,7 +45,7 @@ RCT_EXPORT_METHOD(cropImage:(NSString *)imageTag
[RCTConvert CGSize:cropData[@"size"]]
};
- [_bridge.imageLoader loadImageWithTag:imageTag callback:^(NSError *error, UIImage *image) {
+ [_bridge.imageLoader loadImageWithURLRequest:imageRequest callback:^(NSError *error, UIImage *image) {
if (error) {
errorCallback(error);
return;
diff --git a/Libraries/Image/RCTImageLoader.h b/Libraries/Image/RCTImageLoader.h
index d11c42410..02d358404 100644
--- a/Libraries/Image/RCTImageLoader.h
+++ b/Libraries/Image/RCTImageLoader.h
@@ -56,56 +56,85 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
* Loads the specified image at the highest available resolution.
* Can be called from any thread, will call back on an unspecified thread.
*/
-- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
- callback:(RCTImageLoaderCompletionBlock)callback;
+- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
+ callback:(RCTImageLoaderCompletionBlock)callback;
/**
- * As above, but includes target size, scale and resizeMode, which are used to
- * select the optimal dimensions for the loaded image.
+ * As above, but includes target `size`, `scale` and `resizeMode`, which are used to
+ * select the optimal dimensions for the loaded image. The `clipped` option
+ * controls whether the image will be clipped to fit the specified size exactly,
+ * or if the original aspect ratio should be retained.
*/
-- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
- size:(CGSize)size
- scale:(CGFloat)scale
- resizeMode:(RCTResizeMode)resizeMode
- progressBlock:(RCTImageLoaderProgressBlock)progressBlock
- completionBlock:(RCTImageLoaderCompletionBlock)completionBlock;
+- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ clipped:(BOOL)clipped
+ resizeMode:(RCTResizeMode)resizeMode
+ progressBlock:(RCTImageLoaderProgressBlock)progressBlock
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock;
/**
- * Loads an image without clipping the result to fit - used by RCTImageView.
- */
-- (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag
- size:(CGSize)size
- scale:(CGFloat)scale
- resizeMode:(RCTResizeMode)resizeMode
- progressBlock:(RCTImageLoaderProgressBlock)progressBlock
- completionBlock:(RCTImageLoaderCompletionBlock)completionBlock;
-
-/**
- * Finds an appropriate image decoder and passes the target size, scale and
- * resizeMode for optimal image decoding. Can be called from any thread,
- * will call callback on an unspecified thread.
+ * Finds an appropriate image decoder and passes the target `size`, `scale` and
+ * `resizeMode` for optimal image decoding. The `clipped` option controls
+ * whether the image will be clipped to fit the specified size exactly, or
+ * if the original aspect ratio should be retained. Can be called from any
+ * thread, will call callback on an unspecified thread.
*/
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
size:(CGSize)size
scale:(CGFloat)scale
+ clipped:(BOOL)clipped
resizeMode:(RCTResizeMode)resizeMode
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock;
-/**
- * Decodes an image without clipping the result to fit.
- */
-- (RCTImageLoaderCancellationBlock)decodeImageDataWithoutClipping:(NSData *)data
- size:(CGSize)size
- scale:(CGFloat)scale
- resizeMode:(RCTResizeMode)resizeMode
- completionBlock:(RCTImageLoaderCompletionBlock)completionBlock;
-
/**
* Get image size, in pixels. This method will do the least work possible to get
* the information, and won't decode the image if it doesn't have to.
*/
+- (RCTImageLoaderCancellationBlock)getImageSizeForURLRequest:(NSURLRequest *)imageURLRequest
+ block:(void(^)(NSError *error, CGSize size))completionBlock;
+
+@end
+
+@interface RCTImageLoader (Deprecated)
+
+- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
+ callback:(RCTImageLoaderCompletionBlock)callback
+__deprecated_msg("Use loadImageWithURLRequest:callback: instead");
+
+- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ progressBlock:(RCTImageLoaderProgressBlock)progressBlock
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
+__deprecated_msg("Use loadImageWithURLRequest:size:scale:clipped:resizeMode:progressBlock:completionBlock: instead");
+
+- (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ progressBlock:(RCTImageLoaderProgressBlock)progressBlock
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
+__deprecated_msg("Use loadImageWithURLRequest:size:scale:clipped:resizeMode:progressBlock:completionBlock: instead");
+
+- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
+__deprecated_msg("Use decodeImageData:size:scale:clipped:resizeMode:completionBlock: instead");
+
+- (RCTImageLoaderCancellationBlock)decodeImageDataWithoutClipping:(NSData *)data
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
+__deprecated_msg("Use decodeImageData:size:scale:clipped:resizeMode:completionBlock: instead");
+
- (RCTImageLoaderCancellationBlock)getImageSize:(NSString *)imageTag
- block:(void(^)(NSError *error, CGSize size))completionBlock;
+ block:(void(^)(NSError *error, CGSize size))completionBlock
+__deprecated_msg("Use getImageSizeWithURLRequest:callback: instead");
@end
diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m
index c6f4e2ec8..5059b2c1c 100644
--- a/Libraries/Image/RCTImageLoader.m
+++ b/Libraries/Image/RCTImageLoader.m
@@ -220,15 +220,16 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
return image;
}
-- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
+- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
callback:(RCTImageLoaderCompletionBlock)callback
{
- return [self loadImageWithTag:imageTag
- size:CGSizeZero
- scale:1
- resizeMode:RCTResizeModeStretch
- progressBlock:nil
- completionBlock:callback];
+ return [self loadImageWithURLRequest:imageURLRequest
+ size:CGSizeZero
+ scale:1
+ clipped:YES
+ resizeMode:RCTResizeModeStretch
+ progressBlock:nil
+ completionBlock:callback];
}
- (void)dequeueTasks
@@ -280,12 +281,12 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
* path taken. This is useful if you want to skip decoding, e.g. when preloading
* the image, or retrieving metadata.
*/
-- (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag
- size:(CGSize)size
- scale:(CGFloat)scale
- resizeMode:(RCTResizeMode)resizeMode
- progressBlock:(RCTImageLoaderProgressBlock)progressHandler
- completionBlock:(void (^)(NSError *error, id imageOrData))completionBlock
+- (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest *)imageURLRequest
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ progressBlock:(RCTImageLoaderProgressBlock)progressHandler
+ completionBlock:(void (^)(NSError *error, id imageOrData))completionBlock
{
__block volatile uint32_t cancelled = 0;
__block void(^cancelLoad)(void) = nil;
@@ -306,11 +307,6 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
}
};
- if (imageTag.length == 0) {
- completionHandler(RCTErrorWithMessage(@"source.uri should not be an empty string"), nil);
- return ^{};
- }
-
// All access to URL cache must be serialized
if (!_URLCacheQueue) {
[self setUp];
@@ -329,7 +325,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
}
// Find suitable image URL loader
- NSURLRequest *request = [RCTConvert NSURLRequest:imageTag];
+ NSURLRequest *request = imageURLRequest; // Use a local variable so we can reassign it in this block
id loadHandler = [strongSelf imageURLLoaderForURL:request.URL];
if (loadHandler) {
cancelLoad = [loadHandler loadImageForURL:request.URL
@@ -345,13 +341,13 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
if (RCT_DEBUG && ![_bridge respondsToSelector:@selector(networking)]) {
RCTLogError(@"No suitable image URL loader found for %@. You may need to "
" import the RCTNetwork library in order to load images.",
- imageTag);
+ request.URL.absoluteString);
return;
}
// Check if networking module can load image
if (RCT_DEBUG && ![_bridge.networking canHandleRequest:request]) {
- RCTLogError(@"No suitable image URL loader found for %@", imageTag);
+ RCTLogError(@"No suitable image URL loader found for %@", request.URL.absoluteString);
return;
}
@@ -405,7 +401,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
}
NSURL *redirectURL = [NSURL URLWithString: location];
- request = [NSURLRequest requestWithURL: redirectURL];
+ request = [NSURLRequest requestWithURL:redirectURL];
cachedResponse = [_URLCache cachedResponseForRequest:request];
continue;
}
@@ -470,36 +466,20 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
};
}
-- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
- size:(CGSize)size
- scale:(CGFloat)scale
- resizeMode:(RCTResizeMode)resizeMode
- progressBlock:(RCTImageLoaderProgressBlock)progressHandler
- completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
-{
- return [self loadImageWithoutClipping:imageTag
- size:size
- scale:scale
- resizeMode:resizeMode
- progressBlock:progressHandler
- completionBlock:^(NSError *error, UIImage *image) {
- completionBlock(error, RCTResizeImageIfNeeded(image, size, scale, resizeMode));
- }];
-}
-
-- (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag
- size:(CGSize)size
- scale:(CGFloat)scale
- resizeMode:(RCTResizeMode)resizeMode
- progressBlock:(RCTImageLoaderProgressBlock)progressHandler
- completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
+- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ clipped:(BOOL)clipped
+ resizeMode:(RCTResizeMode)resizeMode
+ progressBlock:(RCTImageLoaderProgressBlock)progressHandler
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
{
__block volatile uint32_t cancelled = 0;
__block void(^cancelLoad)(void) = nil;
__weak RCTImageLoader *weakSelf = self;
// Check decoded image cache
- NSString *cacheKey = RCTCacheKeyForImage(imageTag, size, scale, resizeMode);
+ NSString *cacheKey = RCTCacheKeyForImage(imageURLRequest.URL.absoluteString, size, scale, resizeMode);
{
UIImage *image = [_decodedImageCache objectForKey:cacheKey];
if (image) {
@@ -527,16 +507,17 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
if (!imageOrData || [imageOrData isKindOfClass:[UIImage class]]) {
cacheResultHandler(error, imageOrData);
} else {
- cancelLoad = [weakSelf decodeImageDataWithoutClipping:imageOrData
- size:size
- scale:scale
- resizeMode:resizeMode
- completionBlock:cacheResultHandler];
+ cancelLoad = [weakSelf decodeImageData:imageOrData
+ size:size
+ scale:scale
+ clipped:clipped
+ resizeMode:resizeMode
+ completionBlock:cacheResultHandler];
}
}
};
- cancelLoad = [self loadImageOrDataWithTag:imageTag
+ cancelLoad = [self loadImageOrDataWithURLRequest:imageURLRequest
size:size
scale:scale
resizeMode:resizeMode
@@ -553,23 +534,9 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data
size:(CGSize)size
scale:(CGFloat)scale
+ clipped:(BOOL)clipped
resizeMode:(RCTResizeMode)resizeMode
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
-{
- return [self decodeImageDataWithoutClipping:data
- size:size
- scale:scale
- resizeMode:resizeMode
- completionBlock:^(NSError *error, UIImage *image) {
- completionBlock(error, RCTResizeImageIfNeeded(image, size, scale, resizeMode));
- }];
-}
-
-- (RCTImageLoaderCancellationBlock)decodeImageDataWithoutClipping:(NSData *)data
- size:(CGSize)size
- scale:(CGFloat)scale
- resizeMode:(RCTResizeMode)resizeMode
- completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
{
if (data.length == 0) {
completionBlock(RCTErrorWithMessage(@"No image data"), nil);
@@ -583,11 +550,11 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
// expecting it, and may do expensive post-processing in the callback
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (!cancelled) {
- completionBlock(error, image);
+ completionBlock(error, clipped ? RCTResizeImageIfNeeded(image, size, scale, resizeMode) : image);
}
});
} else if (!cancelled) {
- completionBlock(error, image);
+ completionBlock(error, clipped ? RCTResizeImageIfNeeded(image, size, scale, resizeMode) : image);
}
};
@@ -675,10 +642,10 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
}
}
-- (RCTImageLoaderCancellationBlock)getImageSize:(NSString *)imageTag
+- (RCTImageLoaderCancellationBlock)getImageSizeForURLRequest:(NSURLRequest *)imageURLRequest
block:(void(^)(NSError *error, CGSize size))completionBlock
{
- return [self loadImageOrDataWithTag:imageTag
+ return [self loadImageOrDataWithURLRequest:imageURLRequest
size:CGSizeZero
scale:1
resizeMode:RCTResizeModeStretch
@@ -713,7 +680,7 @@ RCT_EXPORT_METHOD(prefetchImage:(NSString *)uri
return;
}
- [_bridge.imageLoader loadImageWithTag:uri callback:^(NSError *error, UIImage *image) {
+ [_bridge.imageLoader loadImageWithURLRequest:[RCTConvert NSURLRequest:uri] callback:^(NSError *error, UIImage *image) {
if (error) {
reject(RCTErrorPrefetchFailure, nil, error);
return;
@@ -743,7 +710,7 @@ RCT_EXPORT_METHOD(prefetchImage:(NSString *)uri
- (id)sendRequest:(NSURLRequest *)request withDelegate:(id)delegate
{
__block RCTImageLoaderCancellationBlock requestToken;
- requestToken = [self loadImageWithTag:request.URL.absoluteString callback:^(NSError *error, UIImage *image) {
+ requestToken = [self loadImageWithURLRequest:request callback:^(NSError *error, UIImage *image) {
if (error) {
[delegate URLRequest:requestToken didCompleteWithError:error];
return;
@@ -781,6 +748,90 @@ RCT_EXPORT_METHOD(prefetchImage:(NSString *)uri
@end
+@implementation RCTImageLoader (Deprecated)
+
+- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
+ callback:(RCTImageLoaderCompletionBlock)callback
+{
+ RCTLogWarn(@"[RCTImageLoader loadImageWithTag:callback:] is deprecated. Instead use [RCTImageLoader loadImageWithURLRequest:callback:]");
+ return [self loadImageWithURLRequest:[RCTConvert NSURLRequest:imageTag]
+ callback:callback];
+}
+
+- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ progressBlock:(RCTImageLoaderProgressBlock)progressBlock
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
+{
+ RCTLogWarn(@"[RCTImageLoader loadImageWithTag:size:scale:resizeMode:progressBlock:completionBlock:] is deprecated. Instead use [RCTImageLoader loadImageWithURLRequest:size:scale:clipped:resizeMode:progressBlock:completionBlock:]");
+ return [self loadImageWithURLRequest:[RCTConvert NSURLRequest:imageTag]
+ size:size
+ scale:scale
+ clipped:YES
+ resizeMode:resizeMode
+ progressBlock:progressBlock
+ completionBlock:completionBlock];
+}
+
+- (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ progressBlock:(RCTImageLoaderProgressBlock)progressBlock
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
+{
+ RCTLogWarn(@"[RCTImageLoader loadImageWithoutClipping:size:scale:resizeMode:progressBlock:completionBlock:] is deprecated. Instead use [RCTImageLoader loadImageWithURLRequest:size:scale:clipped:resizeMode:progressBlock:completionBlock:]");
+ return [self loadImageWithURLRequest:[RCTConvert NSURLRequest:imageTag]
+ size:size
+ scale:scale
+ clipped:NO
+ resizeMode:resizeMode
+ progressBlock:progressBlock
+ completionBlock:completionBlock];
+}
+
+- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
+{
+ RCTLogWarn(@"[RCTImageLoader decodeImageData:size:scale:resizeMode:completionBlock:] is deprecated. Instead use [RCTImageLoader decodeImageData:size:scale:clipped:resizeMode:completionBlock:]");
+ return [self decodeImageData:imageData
+ size:size
+ scale:scale
+ clipped:NO
+ resizeMode:resizeMode
+ completionBlock:completionBlock];
+}
+
+- (RCTImageLoaderCancellationBlock)decodeImageDataWithoutClipping:(NSData *)imageData
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(RCTResizeMode)resizeMode
+ completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
+{
+ RCTLogWarn(@"[RCTImageLoader decodeImageDataWithoutClipping:size:scale:resizeMode:completionBlock:] is deprecated. Instead use [RCTImageLoader decodeImageData:size:scale:clipped:resizeMode:completionBlock:]");
+ return [self decodeImageData:imageData
+ size:size
+ scale:scale
+ clipped:NO
+ resizeMode:resizeMode
+ completionBlock:completionBlock];
+}
+
+- (RCTImageLoaderCancellationBlock)getImageSize:(NSString *)imageTag
+ block:(void(^)(NSError *error, CGSize size))completionBlock
+{
+ RCTLogWarn(@"[RCTImageLoader getImageSize:block:] is deprecated. Instead use [RCTImageLoader getImageSizeForURLRequest:block:]");
+ return [self getImageSizeForURLRequest:[RCTConvert NSURLRequest:imageTag]
+ block:completionBlock];
+}
+
+@end
+
@implementation RCTBridge (RCTImageLoader)
- (RCTImageLoader *)imageLoader
diff --git a/Libraries/Image/RCTImageView.m b/Libraries/Image/RCTImageView.m
index 1376fb796..a0d63d35f 100644
--- a/Libraries/Image/RCTImageView.m
+++ b/Libraries/Image/RCTImageView.m
@@ -224,12 +224,15 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
RCTImageSource *source = _source;
CGFloat blurRadius = _blurRadius;
__weak RCTImageView *weakSelf = self;
- _reloadImageCancellationBlock = [_bridge.imageLoader loadImageWithoutClipping:_source.imageURL.absoluteString
- size:imageSize
- scale:imageScale
- resizeMode:(RCTResizeMode)self.contentMode
- progressBlock:progressHandler
- completionBlock:^(NSError *error, UIImage *loadedImage) {
+ _reloadImageCancellationBlock =
+ [_bridge.imageLoader loadImageWithURLRequest:_source.request
+ size:imageSize
+ scale:imageScale
+ clipped:NO
+ resizeMode:(RCTResizeMode)self.contentMode
+ progressBlock:progressHandler
+ completionBlock:^(NSError *error, UIImage *loadedImage) {
+
RCTImageView *strongSelf = weakSelf;
void (^setImageBlock)(UIImage *) = ^(UIImage *image) {
if (![source isEqual:strongSelf.source]) {
@@ -297,7 +300,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
if (RCTShouldReloadImageForSizeChange(imageSize, idealSize)) {
if (RCTShouldReloadImageForSizeChange(_targetSize, idealSize)) {
- RCTLogInfo(@"[PERF IMAGEVIEW] Reloading image %@ as size %@", _source.imageURL, NSStringFromCGSize(idealSize));
+ RCTLogInfo(@"[PERF IMAGEVIEW] Reloading image %@ as size %@", _source.request.URL.absoluteString, NSStringFromCGSize(idealSize));
// If the existing image or an image being loaded are not the right
// size, reload the asset in case there is a better size available.
diff --git a/Libraries/Image/RCTImageViewManager.m b/Libraries/Image/RCTImageViewManager.m
index 2bf7a0fe3..5da98b5cd 100644
--- a/Libraries/Image/RCTImageViewManager.m
+++ b/Libraries/Image/RCTImageViewManager.m
@@ -48,7 +48,7 @@ RCT_EXPORT_METHOD(getSize:(NSURL *)imageURL
successBlock:(RCTResponseSenderBlock)successBlock
errorBlock:(RCTResponseErrorBlock)errorBlock)
{
- [self.bridge.imageLoader getImageSize:imageURL.absoluteString
+ [self.bridge.imageLoader getImageSizeForURLRequest:[NSURLRequest requestWithURL:imageURL]
block:^(NSError *error, CGSize size) {
if (error) {
errorBlock(error);
diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m
index f5c6924d6..8c6816e9b 100644
--- a/React/Base/RCTConvert.m
+++ b/React/Base/RCTConvert.m
@@ -133,6 +133,23 @@ RCT_CUSTOM_CONVERTER(NSData *, NSData, [json dataUsingEncoding:NSUTF8StringEncod
if ([method isEqualToString:@"GET"] && headers == nil && body == nil) {
return [NSURLRequest requestWithURL:URL];
}
+
+ if (headers) {
+ __block BOOL allHeadersAreStrings = YES;
+ [headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, id header, BOOL *stop) {
+ if (![header isKindOfClass:[NSString class]]) {
+ RCTLogError(@"Values of HTTP headers passed must be of type string. "
+ "Value of header '%@' is not a string.", key);
+ allHeadersAreStrings = NO;
+ *stop = YES;
+ }
+ }];
+ if (!allHeadersAreStrings) {
+ // Set headers to nil here to avoid crashing later.
+ headers = nil;
+ }
+ }
+
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
request.HTTPBody = body;
request.HTTPMethod = method;
@@ -878,7 +895,7 @@ RCT_ENUM_CONVERTER(RCTAnimationType, (@{
return image;
}
- NSURL *URL = imageSource.imageURL;
+ NSURL *URL = imageSource.request.URL;
NSString *scheme = URL.scheme.lowercaseString;
if ([scheme isEqualToString:@"file"]) {
NSString *assetName = RCTBundlePathForURL(URL);
@@ -914,7 +931,7 @@ RCT_ENUM_CONVERTER(RCTAnimationType, (@{
if (!CGSizeEqualToSize(imageSource.size, CGSizeZero) &&
!CGSizeEqualToSize(imageSource.size, image.size)) {
RCTLogError(@"Image source %@ size %@ does not match loaded image size %@.",
- imageSource.imageURL.path.lastPathComponent,
+ URL.path.lastPathComponent,
NSStringFromCGSize(imageSource.size),
NSStringFromCGSize(image.size));
}
diff --git a/React/Base/RCTImageSource.h b/React/Base/RCTImageSource.h
index efcefaa2b..e5b52c502 100644
--- a/React/Base/RCTImageSource.h
+++ b/React/Base/RCTImageSource.h
@@ -16,7 +16,7 @@
*/
@interface RCTImageSource : NSObject
-@property (nonatomic, strong, readonly) NSURL *imageURL;
+@property (nonatomic, copy, readonly) NSURLRequest *request;
@property (nonatomic, assign, readonly) CGSize size;
@property (nonatomic, assign, readonly) CGFloat scale;
@@ -25,9 +25,9 @@
* Pass a size of CGSizeZero if you do not know or wish to specify the image
* size. Pass a scale of zero if you do not know or wish to specify the scale.
*/
-- (instancetype)initWithURL:(NSURL *)url
- size:(CGSize)size
- scale:(CGFloat)scale;
+- (instancetype)initWithURLRequest:(NSURLRequest *)request
+ size:(CGSize)size
+ scale:(CGFloat)scale;
/**
* Create a copy of the image source with the specified size and scale.
@@ -36,6 +36,13 @@
@end
+@interface RCTImageSource (Deprecated)
+
+@property (nonatomic, strong, readonly) NSURL *imageURL
+__deprecated_msg("Use request.URL instead.");
+
+@end
+
@interface RCTConvert (ImageSource)
+ (RCTImageSource *)RCTImageSource:(id)json;
diff --git a/React/Base/RCTImageSource.m b/React/Base/RCTImageSource.m
index 07518edf9..173bad517 100644
--- a/React/Base/RCTImageSource.m
+++ b/React/Base/RCTImageSource.m
@@ -18,10 +18,10 @@
@implementation RCTImageSource
-- (instancetype)initWithURL:(NSURL *)url size:(CGSize)size scale:(CGFloat)scale
+- (instancetype)initWithURLRequest:(NSURLRequest *)request size:(CGSize)size scale:(CGFloat)scale
{
if ((self = [super init])) {
- _imageURL = url;
+ _request = [request copy];
_size = size;
_scale = scale;
}
@@ -30,9 +30,9 @@
- (instancetype)imageSourceWithSize:(CGSize)size scale:(CGFloat)scale
{
- RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURL:_imageURL
- size:size
- scale:scale];
+ RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURLRequest:_request
+ size:size
+ scale:scale];
imageSource.packagerAsset = _packagerAsset;
return imageSource;
}
@@ -42,12 +42,22 @@
if (![object isKindOfClass:[RCTImageSource class]]) {
return NO;
}
- return [_imageURL isEqual:object.imageURL] && _scale == object.scale &&
+ return [_request isEqual:object.request] && _scale == object.scale &&
(CGSizeEqualToSize(_size, object.size) || CGSizeEqualToSize(object.size, CGSizeZero));
}
@end
+
+@implementation RCTImageSource (Deprecated)
+
+- (NSURL *)imageURL
+{
+ return self.request.URL;
+}
+
+@end
+
@implementation RCTConvert (ImageSource)
+ (RCTImageSource *)RCTImageSource:(id)json
@@ -56,25 +66,25 @@
return nil;
}
- NSURL *imageURL;
+ NSURLRequest *request;
CGSize size = CGSizeZero;
CGFloat scale = 1.0;
BOOL packagerAsset = NO;
if ([json isKindOfClass:[NSDictionary class]]) {
- if (!(imageURL = [self NSURL:RCTNilIfNull(json[@"uri"])])) {
+ if (!(request = [self NSURLRequest:json])) {
return nil;
}
size = [self CGSize:json];
scale = [self CGFloat:json[@"scale"]] ?: [self BOOL:json[@"deprecated"]] ? 0.0 : 1.0;
packagerAsset = [self BOOL:json[@"__packager_asset"]];
} else if ([json isKindOfClass:[NSString class]]) {
- imageURL = [self NSURL:json];
+ request = [self NSURLRequest:json];
} else {
RCTLogConvertError(json, @"an image. Did you forget to call resolveAssetSource() on the JS side?");
return nil;
}
- RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURL:imageURL
+ RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURLRequest:request
size:size
scale:scale];
imageSource.packagerAsset = packagerAsset;