Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS: Support HTTP headers for source prop on <Image> components #7338

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ - (void)testImageLoading

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 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) {
XCTAssertEqual(progress, 1);
XCTAssertEqual(total, 1);
} completionBlock:^(NSError *loadError, id loadedImage) {
Expand Down Expand Up @@ -78,8 +79,9 @@ - (void)testImageLoaderUsesImageURLLoaderWithHighestPriority
}];

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 resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) {
XCTAssertEqual(progress, 1);
XCTAssertEqual(total, 1);
} completionBlock:^(NSError *loadError, id loadedImage) {
Expand Down
3 changes: 2 additions & 1 deletion Libraries/CameraRoll/RCTCameraRollManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ @implementation RCTCameraRollManager
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
{
[_bridge.imageLoader loadImageWithTag:imageTag callback:^(NSError *loadError, UIImage *loadedImage) {
[_bridge.imageLoader loadImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:imageTag]]
callback:^(NSError *loadError, UIImage *loadedImage) {
if (loadError) {
reject(RCTErrorUnableToLoad, nil, loadError);
return;
Expand Down
18 changes: 17 additions & 1 deletion Libraries/Image/Image.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,22 @@ var Image = React.createClass({
* `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).
*
* `method` is the HTTP Method to use. Defaults to GET if not specified.
*

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

* `headers` is an object representing the HTTP headers to send along with the request
* for a remote image.
*
* `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.
*/
source: PropTypes.oneOfType([
PropTypes.shape({
uri: PropTypes.string,
method: PropTypes.string,
headers: PropTypes.objectOf(PropTypes.string),
body: PropTypes.string,
}),
// Opaque type returned by require('./image.jpg')
PropTypes.number,
Expand Down Expand Up @@ -216,7 +228,11 @@ var Image = React.createClass({
if (isNetwork && (tintColor || this.props.blurRadius)) {
RawImage = RCTImageView;
}


if (source && source.uri === '') {
console.warn('source.uri should not be an empty string');
}

if (this.props.src) {
console.warn('The <Image> component requires a `source` property rather than `src`.');
}
Expand Down
2 changes: 1 addition & 1 deletion Libraries/Image/RCTImageEditingManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ @implementation RCTImageEditingManager
[RCTConvert CGSize:cropData[@"size"]]
};

[_bridge.imageLoader loadImageWithTag:imageTag callback:^(NSError *error, UIImage *image) {
[_bridge.imageLoader loadImageWithURLRequest:[RCTConvert NSURLRequest:imageTag] callback:^(NSError *error, UIImage *image) {
if (error) {
errorCallback(error);
return;
Expand Down
32 changes: 28 additions & 4 deletions Libraries/Image/RCTImageLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ 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
- (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.
*/
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
Expand All @@ -73,7 +73,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
/**
* Loads an image without clipping the result to fit - used by RCTImageView.
*/
- (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag
- (RCTImageLoaderCancellationBlock)loadImageWithURLRequestWithoutClipping:(NSURLRequest *)imageURLRequest
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
Expand Down Expand Up @@ -104,11 +104,35 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
* 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)getImageSize:(NSString *)imageTag
- (RCTImageLoaderCancellationBlock)getImageSizeForURLRequest:(NSURLRequest *)imageURLRequest
block:(void(^)(NSError *error, CGSize size))completionBlock;

@end

@interface RCTImageLoader (Deprecated)

- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
callback:(RCTImageLoaderCompletionBlock)callback __deprecated;

- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock __deprecated;

- (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock __deprecated;

- (RCTImageLoaderCancellationBlock)getImageSize:(NSString *)imageTag
block:(void(^)(NSError *error, CGSize size))completionBlock __deprecated;

@end

@interface RCTBridge (RCTImageLoader)

/**
Expand Down
89 changes: 68 additions & 21 deletions Libraries/Image/RCTImageLoader.m
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,10 @@ - (void)dealloc
return image;
}

- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
callback:(RCTImageLoaderCompletionBlock)callback
{
return [self loadImageWithTag:imageTag
return [self loadImageWithURLRequest:imageURLRequest
size:CGSizeZero
scale:1
resizeMode:RCTResizeModeStretch
Expand Down Expand Up @@ -280,7 +280,7 @@ - (void)dequeueTasks
* path taken. This is useful if you want to skip decoding, e.g. when preloading
* the image, or retrieving metadata.
*/
- (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag
- (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest *)imageURLRequest
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
Expand All @@ -306,11 +306,6 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag
}
};

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];
Expand All @@ -329,7 +324,7 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag
}

// 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<RCTImageURLLoader> loadHandler = [strongSelf imageURLLoaderForURL:request.URL];
if (loadHandler) {
cancelLoad = [loadHandler loadImageForURL:request.URL
Expand All @@ -345,13 +340,13 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag
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;
}

Expand Down Expand Up @@ -405,7 +400,7 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag
}

NSURL *redirectURL = [NSURL URLWithString: location];
request = [NSURLRequest requestWithURL: redirectURL];
request = [NSURLRequest requestWithURL:redirectURL];
cachedResponse = [_URLCache cachedResponseForRequest:request];
continue;
}
Expand Down Expand Up @@ -470,14 +465,14 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithTag:(NSString *)imageTag
};
}

- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
{
return [self loadImageWithoutClipping:imageTag
return [self loadImageWithURLRequestWithoutClipping:imageURLRequest
size:size
scale:scale
resizeMode:resizeMode
Expand All @@ -487,7 +482,7 @@ - (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
}];
}

- (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag
- (RCTImageLoaderCancellationBlock)loadImageWithURLRequestWithoutClipping:(NSURLRequest *)imageURLRequest
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
Expand All @@ -499,7 +494,7 @@ - (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag
__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) {
Expand Down Expand Up @@ -536,7 +531,7 @@ - (RCTImageLoaderCancellationBlock)loadImageWithoutClipping:(NSString *)imageTag
}
};

cancelLoad = [self loadImageOrDataWithTag:imageTag
cancelLoad = [self loadImageOrDataWithURLRequest:imageURLRequest
size:size
scale:scale
resizeMode:resizeMode
Expand Down Expand Up @@ -675,10 +670,10 @@ - (RCTImageLoaderCancellationBlock)decodeImageDataWithoutClipping:(NSData *)data
}
}

- (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
Expand Down Expand Up @@ -713,7 +708,7 @@ - (RCTImageLoaderCancellationBlock)getImageSize:(NSString *)imageTag
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;
Expand Down Expand Up @@ -743,7 +738,7 @@ - (BOOL)canHandleRequest:(NSURLRequest *)request
- (id)sendRequest:(NSURLRequest *)request withDelegate:(id<RCTURLRequestDelegate>)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;
Expand Down Expand Up @@ -781,6 +776,58 @@ - (void)cancelRequest:(id)requestToken

@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:resizeMode:progressBlock:completionBlock:]");
return [self loadImageWithURLRequest:[RCTConvert NSURLRequest:imageTag]
size:size
scale:scale
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 loadImageWithURLRequestWithoutClipping:size:scale:resizeMode:progressBlock:completionBlock:]");
return [self loadImageWithURLRequestWithoutClipping:[RCTConvert NSURLRequest:imageTag]
size:size
scale:scale
resizeMode:resizeMode
progressBlock:progressBlock
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
Expand Down
14 changes: 7 additions & 7 deletions Libraries/Image/RCTImageView.m
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,12 @@ - (void)reloadImage
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 loadImageWithURLRequestWithoutClipping:_source.imageURLRequest
size:imageSize
scale:imageScale
resizeMode:(RCTResizeMode)self.contentMode
progressBlock:progressHandler
completionBlock:^(NSError *error, UIImage *loadedImage) {
RCTImageView *strongSelf = weakSelf;
void (^setImageBlock)(UIImage *) = ^(UIImage *image) {
if (![source isEqual:strongSelf.source]) {
Expand Down Expand Up @@ -297,7 +297,7 @@ - (void)reactSetFrame:(CGRect)frame

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.imageURLRequest.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.
Expand Down
Loading