diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index fc082b098f84af..c308e70080af7b 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -45,11 +45,16 @@ function prefetch(url: string) { return ImageViewManager.prefetchImage(url); } +async function queryCache(urls: Array): Promise> { + return await ImageViewManager.queryCache(urls); +} + declare class ImageComponentType extends ReactNative.NativeComponent< ImagePropsType, > { static getSize: typeof getSize; static prefetch: typeof prefetch; + static queryCache: typeof queryCache; static resolveAssetSource: typeof resolveAssetSource; static propTypes: typeof ImageProps; } @@ -133,6 +138,13 @@ Image.getSize = getSize; */ Image.prefetch = prefetch; +/** + * Performs cache interrogation. + * + * See https://facebook.github.io/react-native/docs/image.html#querycache + */ +Image.queryCache = queryCache; + /** * Resolves an asset reference into an object. * diff --git a/Libraries/Image/RCTImageLoader.h b/Libraries/Image/RCTImageLoader.h index a0c6e798a8a960..17552632aabae0 100644 --- a/Libraries/Image/RCTImageLoader.h +++ b/Libraries/Image/RCTImageLoader.h @@ -129,6 +129,14 @@ typedef dispatch_block_t RCTImageLoaderCancellationBlock; */ - (RCTImageLoaderCancellationBlock)getImageSizeForURLRequest:(NSURLRequest *)imageURLRequest block:(void(^)(NSError *error, CGSize size))completionBlock; +/** + * Determines whether given image URLs are cached locally. The `requests` array is expected + * to contain objects convertible to NSURLRequest. The return value maps URLs to strings: + * "disk" for images known to be cached in non-volatile storage, "memory" for images known + * to be cached in memory. Dictionary items corresponding to images that are not known to be + * cached are simply missing. + */ +- (NSDictionary *)getImageCacheStatus:(NSArray *)requests; /** * Allows developers to set their own caching implementation for diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m index 775e53e95f7a08..baaf9f0d65e257 100644 --- a/Libraries/Image/RCTImageLoader.m +++ b/Libraries/Image/RCTImageLoader.m @@ -779,6 +779,25 @@ - (RCTImageLoaderCancellationBlock)getImageSizeForURLRequest:(NSURLRequest *)ima completionBlock:completion]; } +- (NSDictionary *)getImageCacheStatus:(NSArray *)requests +{ + NSMutableDictionary *results = [NSMutableDictionary dictionary]; + for (id request in requests) { + NSURLRequest *urlRequest = [RCTConvert NSURLRequest:request]; + if (urlRequest) { + NSCachedURLResponse *cachedResponse = [NSURLCache.sharedURLCache cachedResponseForRequest:urlRequest]; + if (cachedResponse) { + if (cachedResponse.storagePolicy == NSURLCacheStorageAllowedInMemoryOnly) { + [results setObject:@"memory" forKey:urlRequest.URL.absoluteString]; + } else { + [results setObject:@"disk" forKey:urlRequest.URL.absoluteString]; + } + } + } + } + return results; +} + #pragma mark - RCTURLRequestHandler - (BOOL)canHandleRequest:(NSURLRequest *)request diff --git a/Libraries/Image/RCTImageViewManager.m b/Libraries/Image/RCTImageViewManager.m index bd382e4bf191d0..b038c18d39079b 100644 --- a/Libraries/Image/RCTImageViewManager.m +++ b/Libraries/Image/RCTImageViewManager.m @@ -82,4 +82,11 @@ - (UIView *)view }]; } +RCT_EXPORT_METHOD(queryCache:(NSArray *)requests + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) +{ + resolve([self.bridge.imageLoader getImageCacheStatus:requests]); +} + @end diff --git a/RNTester/js/ImageExample.js b/RNTester/js/ImageExample.js index 6672195e92b8ec..b8fc6bfe3c2614 100644 --- a/RNTester/js/ImageExample.js +++ b/RNTester/js/ImageExample.js @@ -77,6 +77,18 @@ var NetworkImageCallbackExample = createReactClass({ this._loadEventFired( `✔ Prefetch OK (+${new Date() - mountTime}ms)`, ); + Image.queryCache([IMAGE_PREFETCH_URL]).then((map) => { + var result = map.get(IMAGE_PREFETCH_URL); + if (result) { + this._loadEventFired( + `✔ queryCache "${result}" (+${new Date() - mountTime}ms)`, + ); + } else { + this._loadEventFired( + `✘ queryCache (+${new Date() - mountTime}ms)`, + ); + } + }); }, error => { this._loadEventFired(