Skip to content
Open
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
2 changes: 2 additions & 0 deletions packages/react-native/Libraries/Image/ImageSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export interface ImageURISource {
*
* - `reload`: The data for the URL will be loaded from the originating source.
* No existing cache data should be used to satisfy a URL load request.
* On iOS, images bundled with the app are then also not kept in the system
* image cache once they are no longer shown.
*
* - `force-cache`: The existing cached data will be used to satisfy the request,
* regardless of its age or expiration date. If there is no existing data in the cache
Expand Down
48 changes: 37 additions & 11 deletions packages/react-native/Libraries/Image/RCTBundleAssetImageLoader.mm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@
@interface RCTBundleAssetImageLoader () <RCTTurboModule>
@end

static void RCTCompleteLocalAssetLoad(
UIImage *image,
NSURL *imageURL,
RCTImageLoaderProgressBlock progressHandler,
RCTImageLoaderCompletionBlock completionHandler)
{
if (image != nullptr) {
if (progressHandler != nullptr) {
progressHandler(1, 1);
}
completionHandler(nil, image);
} else {
NSString *message = [NSString stringWithFormat:@"Could not find image %@", imageURL];
RCTLogWarn(@"%@", message);
completionHandler(RCTErrorWithMessage(message), nil);
}
}

@implementation RCTBundleAssetImageLoader

RCT_EXPORT_MODULE()
Expand Down Expand Up @@ -49,18 +67,26 @@ - (nullable RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
UIImage *image = RCTImageFromLocalAssetURL(imageURL);
if (image != nullptr) {
if (progressHandler != nullptr) {
progressHandler(1, 1);
}
completionHandler(nil, image);
} else {
NSString *message = [NSString stringWithFormat:@"Could not find image %@", imageURL];
RCTLogWarn(@"%@", message);
completionHandler(RCTErrorWithMessage(message), nil);
}
RCTCompleteLocalAssetLoad(RCTImageFromLocalAssetURL(imageURL), imageURL, progressHandler, completionHandler);
return nil;
}

- (nullable RCTImageLoaderCancellationBlock)
loadImageIgnoringCacheForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandlerWithMetadata:(RCTImageLoaderCompletionBlockWithMetadata)completionHandler
{
RCTCompleteLocalAssetLoad(
RCTImageFromLocalAssetURLIgnoringSystemCache(imageURL),
imageURL,
progressHandler,
^(NSError *error, UIImage *image) {
completionHandler(error, image, nil);
});
return nil;
}

Expand Down
16 changes: 16 additions & 0 deletions packages/react-native/Libraries/Image/RCTImageLoader.mm
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,27 @@ - (void)setImageCache:(id<RCTImageCache>)cache
static RCTImageLoaderCancellationBlock RCTLoadImageURLFromLoader(
id<RCTImageURLLoader> loadHandler,
NSURL *imageURL,
BOOL ignoreCache,
CGSize size,
CGFloat scale,
RCTResizeMode resizeMode,
RCTImageLoaderProgressBlock progressHandler,
RCTImageLoaderPartialLoadBlock partialLoadHandler,
RCTImageLoaderCompletionBlockWithMetadata completionHandler)
{
if (ignoreCache &&
[loadHandler respondsToSelector:@selector
(loadImageIgnoringCacheForURL:
size:scale:resizeMode:progressHandler:partialLoadHandler
:completionHandlerWithMetadata:)]) {
return [loadHandler loadImageIgnoringCacheForURL:imageURL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
partialLoadHandler:partialLoadHandler
completionHandlerWithMetadata:completionHandler];
}
if ([loadHandler
respondsToSelector:@selector
(loadImageForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandlerWithMetadata:)]) {
Expand Down Expand Up @@ -619,6 +633,7 @@ - (RCTImageURLLoaderRequest *)_loadImageOrDataWithURLRequest:(NSURLRequest *)req
RCTImageLoaderCancellationBlock cb = RCTLoadImageURLFromLoader(
loadHandler,
request.URL,
request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData,
size,
scale,
resizeMode,
Expand Down Expand Up @@ -663,6 +678,7 @@ - (RCTImageURLLoaderRequest *)_loadImageOrDataWithURLRequest:(NSURLRequest *)req
cancelLoadLocal = RCTLoadImageURLFromLoader(
loadHandler,
request.URL,
request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData,
size,
scale,
resizeMode,
Expand Down
15 changes: 15 additions & 0 deletions packages/react-native/Libraries/Image/RCTImageURLLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ typedef dispatch_block_t RCTImageLoaderCancellationBlock;
completionHandlerWithMetadata:
(RCTImageLoaderCompletionBlockWithMetadata)completionHandlerWithMetadata;

/*
* Load the image without using any cache the loader keeps of its own (for
* example the system cache behind +[UIImage imageNamed:]). Called instead of
* the methods above when the image source asks to ignore local caches
* (`cache: 'reload'`). Loaders that don't implement it are called as usual.
*/
- (nullable RCTImageLoaderCancellationBlock)
loadImageIgnoringCacheForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandlerWithMetadata:(RCTImageLoaderCompletionBlockWithMetadata)completionHandlerWithMetadata;

/**
* If more than one RCTImageURLLoader responds YES to `-canLoadImageURL:`
* then `loaderPriority` is used to determine which one to use. The loader
Expand Down
48 changes: 37 additions & 11 deletions packages/react-native/Libraries/Image/RCTLocalAssetImageLoader.mm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@
@interface RCTLocalAssetImageLoader () <RCTTurboModule>
@end

static void RCTCompleteLocalAssetLoad(
UIImage *image,
NSURL *imageURL,
RCTImageLoaderProgressBlock progressHandler,
RCTImageLoaderCompletionBlock completionHandler)
{
if (image != nullptr) {
if (progressHandler != nullptr) {
progressHandler(1, 1);
}
completionHandler(nil, image);
} else {
NSString *message = [NSString stringWithFormat:@"Could not find image %@", imageURL];
RCTLogWarn(@"%@", message);
completionHandler(RCTErrorWithMessage(message), nil);
}
}

@implementation RCTLocalAssetImageLoader

RCT_EXPORT_MODULE()
Expand Down Expand Up @@ -49,18 +67,26 @@ - (nullable RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
UIImage *image = RCTImageFromLocalAssetURL(imageURL);
if (image != nullptr) {
if (progressHandler != nullptr) {
progressHandler(1, 1);
}
completionHandler(nil, image);
} else {
NSString *message = [NSString stringWithFormat:@"Could not find image %@", imageURL];
RCTLogWarn(@"%@", message);
completionHandler(RCTErrorWithMessage(message), nil);
}
RCTCompleteLocalAssetLoad(RCTImageFromLocalAssetURL(imageURL), imageURL, progressHandler, completionHandler);
return nil;
}

- (nullable RCTImageLoaderCancellationBlock)
loadImageIgnoringCacheForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandlerWithMetadata:(RCTImageLoaderCompletionBlockWithMetadata)completionHandler
{
RCTCompleteLocalAssetLoad(
RCTImageFromLocalAssetURLIgnoringSystemCache(imageURL),
imageURL,
progressHandler,
^(NSError *error, UIImage *image) {
completionHandler(error, image, nil);
});
return nil;
}

Expand Down
6 changes: 6 additions & 0 deletions packages/react-native/React/Base/RCTUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@ RCT_EXTERN BOOL RCTIsLocalAssetURL(NSURL *__nullable imageURL);
// does not correspond to a local asset.
RCT_EXTERN UIImage *__nullable RCTImageFromLocalAssetURL(NSURL *imageURL);

// Same as RCTImageFromLocalAssetURL, but reads the image file directly instead of
// using +[UIImage imageNamed:], so the image is not kept in the system image cache
// after it is no longer used. Images compiled into an asset catalog can only be
// loaded by name and still go through the system cache.
RCT_EXTERN UIImage *__nullable RCTImageFromLocalAssetURLIgnoringSystemCache(NSURL *imageURL);

// Only used in case when RCTImageFromLocalAssetURL fails to get an image
// This method basically checks for the image in the bundle location, instead
// of the CodePush location
Expand Down
20 changes: 17 additions & 3 deletions packages/react-native/React/Base/RCTUtils.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,7 @@ static BOOL RCTUseAssetCatalog(void)
return RCTImageFromLocalAssetURL(bundleImageUrl);
}

UIImage *__nullable RCTImageFromLocalAssetURL(NSURL *imageURL)
static UIImage *__nullable RCTImageFromLocalAssetURLWithSystemCache(NSURL *imageURL, BOOL useSystemCache)
{
if (RCTUseAssetCatalog()) {
NSString *catalogName = RCTAssetCatalogNameForURL(imageURL);
Expand Down Expand Up @@ -1158,7 +1158,7 @@ static BOOL RCTUseAssetCatalog(void)
}

UIImage *image = nil;
if (imageName) {
if (imageName && useSystemCache) {
if (bundle) {
image = [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil];
} else {
Expand Down Expand Up @@ -1187,7 +1187,11 @@ static BOOL RCTUseAssetCatalog(void)
error:nil];
for (NSURL *frameworkURL in possibleFrameworks) {
bundle = [NSBundle bundleWithURL:frameworkURL];
image = [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil];
if (useSystemCache) {
image = [UIImage imageNamed:imageName inBundle:bundle compatibleWithTraitCollection:nil];
} else {
image = [UIImage imageWithContentsOfFile:[bundle.bundlePath stringByAppendingPathComponent:imageName]];
}
if (image) {
RCTLogWarn(@"Image %@ not found in mainBundle, but found in %@", imageName, bundle);
break;
Expand All @@ -1197,6 +1201,16 @@ static BOOL RCTUseAssetCatalog(void)
return image;
}

UIImage *__nullable RCTImageFromLocalAssetURL(NSURL *imageURL)
{
return RCTImageFromLocalAssetURLWithSystemCache(imageURL, YES);
}

UIImage *__nullable RCTImageFromLocalAssetURLIgnoringSystemCache(NSURL *imageURL)
{
return RCTImageFromLocalAssetURLWithSystemCache(imageURL, NO);
}

RCT_EXTERN NSString *__nullable RCTTempFilePath(NSString *extension, NSError **error)
{
static NSError *setupError = nil;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export interface ImageURISource {
*
* - `reload`: The data for the URL will be loaded from the originating source.
* No existing cache data should be used to satisfy a URL load request.
* On iOS, images bundled with the app are then also not kept in the system
* image cache once they are no longer shown.
*
* - `force-cache`: The existing cached data will be used to satisfy the request,
* regardless of its age or expiration date. If there is no existing data in the cache
Expand Down
1 change: 1 addition & 0 deletions scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -2757,6 +2757,7 @@ protocol RCTImageURLLoader : public RCTBridgeModule {
public virtual BOOL shouldCacheLoadedImages();
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandler:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlock completionHandler);
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandlerWithMetadata:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlockWithMetadata completionHandlerWithMetadata);
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageIgnoringCacheForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandlerWithMetadata:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlockWithMetadata completionHandlerWithMetadata);
public virtual float loaderPriority();
}

Expand Down
1 change: 1 addition & 0 deletions scripts/cxx-api/api-snapshots/ReactAppleFrameworksCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -2831,6 +2831,7 @@ protocol RCTImageURLLoader : public RCTBridgeModule {
public virtual BOOL shouldCacheLoadedImages();
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandler:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlock completionHandler);
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandlerWithMetadata:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlockWithMetadata completionHandlerWithMetadata);
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageIgnoringCacheForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandlerWithMetadata:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlockWithMetadata completionHandlerWithMetadata);
public virtual float loaderPriority();
}

Expand Down
1 change: 1 addition & 0 deletions scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -2749,6 +2749,7 @@ protocol RCTImageURLLoader : public RCTBridgeModule {
public virtual BOOL shouldCacheLoadedImages();
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandler:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlock completionHandler);
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandlerWithMetadata:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlockWithMetadata completionHandlerWithMetadata);
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageIgnoringCacheForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandlerWithMetadata:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlockWithMetadata completionHandlerWithMetadata);
public virtual float loaderPriority();
}

Expand Down
1 change: 1 addition & 0 deletions scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api
Original file line number Diff line number Diff line change
Expand Up @@ -2757,6 +2757,7 @@ protocol RCTImageURLLoader : public RCTBridgeModule {
public virtual BOOL shouldCacheLoadedImages();
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandler:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlock completionHandler);
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandlerWithMetadata:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlockWithMetadata completionHandlerWithMetadata);
public virtual _Nullable RCTImageLoaderCancellationBlock loadImageIgnoringCacheForURL:size:scale:resizeMode:progressHandler:partialLoadHandler:completionHandlerWithMetadata:(NSURL* imageURL, CGSize size, CGFloat scale, RCTResizeMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderPartialLoadBlock partialLoadHandler, RCTImageLoaderCompletionBlockWithMetadata completionHandlerWithMetadata);
public virtual float loaderPriority();
}

Expand Down
Loading