From d37baa78f11f36aa5fb84307cc29ebe2bf444a33 Mon Sep 17 00:00:00 2001 From: Emily Janzer Date: Mon, 11 Nov 2019 17:18:40 -0800 Subject: [PATCH] Split JS spec for image loader module Summary: It turns out the ImageLoader native module has different method signatures on iOS than on Android, so the JS spec we currently have won't work for ANdroid. In this diff I'm splitting up the spec for NativeImageLoader into an Android & iOS versions (similar to PlatformConstants), and updating the Android spec to match the native implementation. I'm also changing `RCTImageLoader` to use the new generated spec, and updating the JS callers (`Image.android.js` and `Image.ios.js`) to use the right one for the platform (instead of importing the untyped `ImageLoader` native module from `react-native`, like we were on Android :-/). This will be a breaking change for anyone who's directly using `NativeImageLoader.js`, but I think most callsites should be using the `Image` component instead. Changelog: [General] [Changed] Split NativeImageLoader into NativeImageLoaderAndroid and NativeImageLoaderIOS Reviewed By: RSNara Differential Revision: D18439538 fbshipit-source-id: 94c796d3fd27800ea17053e963bee51aca921718 --- .../FBReactNativeSpec-generated.mm | 70 ++++++++++++++++--- .../FBReactNativeSpec/FBReactNativeSpec.h | 40 +++++++++-- Libraries/Image/Image.android.js | 13 ++-- Libraries/Image/Image.ios.js | 10 +-- Libraries/Image/NativeImageLoaderAndroid.js | 30 ++++++++ ...ImageLoader.js => NativeImageLoaderIOS.js} | 0 Libraries/Image/RCTImageLoader.mm | 4 +- .../specs/NativeImageLoaderAndroidSpec.java | 43 ++++++++++++ .../specs/NativeImageLoaderIOSSpec.java | 40 +++++++++++ 9 files changed, 221 insertions(+), 29 deletions(-) create mode 100644 Libraries/Image/NativeImageLoaderAndroid.js rename Libraries/Image/{NativeImageLoader.js => NativeImageLoaderIOS.js} (100%) create mode 100644 ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageLoaderAndroidSpec.java create mode 100644 ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageLoaderIOSSpec.java diff --git a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm index 68d242029ad..a0c033417ac 100644 --- a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm +++ b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm @@ -1325,36 +1325,84 @@ namespace facebook { namespace react { - static facebook::jsi::Value __hostFunction_NativeImageLoaderSpecJSI_getSize(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + static facebook::jsi::Value __hostFunction_NativeImageLoaderAndroidSpecJSI_abortRequest(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "abortRequest", @selector(abortRequest:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageLoaderAndroidSpecJSI_getSize(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "getSize", @selector(getSize:resolve:reject:), args, count); } - static facebook::jsi::Value __hostFunction_NativeImageLoaderSpecJSI_getSizeWithHeaders(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + static facebook::jsi::Value __hostFunction_NativeImageLoaderAndroidSpecJSI_getSizeWithHeaders(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "getSizeWithHeaders", @selector(getSizeWithHeaders:headers:resolve:reject:), args, count); } - static facebook::jsi::Value __hostFunction_NativeImageLoaderSpecJSI_prefetchImage(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "prefetchImage", @selector(prefetchImage:resolve:reject:), args, count); + static facebook::jsi::Value __hostFunction_NativeImageLoaderAndroidSpecJSI_prefetchImage(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "prefetchImage", @selector(prefetchImage:requestId:resolve:reject:), args, count); } - static facebook::jsi::Value __hostFunction_NativeImageLoaderSpecJSI_queryCache(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + static facebook::jsi::Value __hostFunction_NativeImageLoaderAndroidSpecJSI_queryCache(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "queryCache", @selector(queryCache:resolve:reject:), args, count); } - NativeImageLoaderSpecJSI::NativeImageLoaderSpecJSI(id instance, std::shared_ptr jsInvoker) - : ObjCTurboModule("ImageLoader", instance, jsInvoker) { + NativeImageLoaderAndroidSpecJSI::NativeImageLoaderAndroidSpecJSI(id instance, std::shared_ptr jsInvoker) + : ObjCTurboModule("ImageLoaderAndroid", instance, jsInvoker) { - methodMap_["getSize"] = MethodMetadata {1, __hostFunction_NativeImageLoaderSpecJSI_getSize}; + methodMap_["abortRequest"] = MethodMetadata {1, __hostFunction_NativeImageLoaderAndroidSpecJSI_abortRequest}; - methodMap_["getSizeWithHeaders"] = MethodMetadata {2, __hostFunction_NativeImageLoaderSpecJSI_getSizeWithHeaders}; + methodMap_["getSize"] = MethodMetadata {1, __hostFunction_NativeImageLoaderAndroidSpecJSI_getSize}; - methodMap_["prefetchImage"] = MethodMetadata {1, __hostFunction_NativeImageLoaderSpecJSI_prefetchImage}; + methodMap_["getSizeWithHeaders"] = MethodMetadata {2, __hostFunction_NativeImageLoaderAndroidSpecJSI_getSizeWithHeaders}; - methodMap_["queryCache"] = MethodMetadata {1, __hostFunction_NativeImageLoaderSpecJSI_queryCache}; + methodMap_["prefetchImage"] = MethodMetadata {2, __hostFunction_NativeImageLoaderAndroidSpecJSI_prefetchImage}; + + + methodMap_["queryCache"] = MethodMetadata {1, __hostFunction_NativeImageLoaderAndroidSpecJSI_queryCache}; + + + + } + + } // namespace react +} // namespace facebook +namespace facebook { + namespace react { + + + static facebook::jsi::Value __hostFunction_NativeImageLoaderIOSSpecJSI_getSize(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "getSize", @selector(getSize:resolve:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageLoaderIOSSpecJSI_getSizeWithHeaders(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "getSizeWithHeaders", @selector(getSizeWithHeaders:headers:resolve:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageLoaderIOSSpecJSI_prefetchImage(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "prefetchImage", @selector(prefetchImage:resolve:reject:), args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageLoaderIOSSpecJSI_queryCache(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, "queryCache", @selector(queryCache:resolve:reject:), args, count); + } + + + NativeImageLoaderIOSSpecJSI::NativeImageLoaderIOSSpecJSI(id instance, std::shared_ptr jsInvoker) + : ObjCTurboModule("ImageLoaderIOS", instance, jsInvoker) { + + methodMap_["getSize"] = MethodMetadata {1, __hostFunction_NativeImageLoaderIOSSpecJSI_getSize}; + + + methodMap_["getSizeWithHeaders"] = MethodMetadata {2, __hostFunction_NativeImageLoaderIOSSpecJSI_getSizeWithHeaders}; + + + methodMap_["prefetchImage"] = MethodMetadata {1, __hostFunction_NativeImageLoaderIOSSpecJSI_prefetchImage}; + + + methodMap_["queryCache"] = MethodMetadata {1, __hostFunction_NativeImageLoaderIOSSpecJSI_queryCache}; diff --git a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h index cf754c04493..637161e82a7 100644 --- a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h +++ b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h @@ -1466,7 +1466,39 @@ namespace facebook { }; } // namespace react } // namespace facebook -@protocol NativeImageLoaderSpec +@protocol NativeImageLoaderAndroidSpec + +- (void)abortRequest:(double)requestId; +- (void)getSize:(NSString *)uri + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)getSizeWithHeaders:(NSString *)uri + headers:(NSDictionary *)headers + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)prefetchImage:(NSString *)uri + requestId:(double)requestId + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)queryCache:(NSArray *)uris + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'ImageLoaderAndroid' + */ + + class JSI_EXPORT NativeImageLoaderAndroidSpecJSI : public ObjCTurboModule { + public: + NativeImageLoaderAndroidSpecJSI(id instance, std::shared_ptr jsInvoker); + + }; + } // namespace react +} // namespace facebook +@protocol NativeImageLoaderIOSSpec - (void)getSize:(NSString *)uri resolve:(RCTPromiseResolveBlock)resolve @@ -1486,12 +1518,12 @@ namespace facebook { namespace facebook { namespace react { /** - * ObjC++ class for module 'ImageLoader' + * ObjC++ class for module 'ImageLoaderIOS' */ - class JSI_EXPORT NativeImageLoaderSpecJSI : public ObjCTurboModule { + class JSI_EXPORT NativeImageLoaderIOSSpecJSI : public ObjCTurboModule { public: - NativeImageLoaderSpecJSI(id instance, std::shared_ptr jsInvoker); + NativeImageLoaderIOSSpecJSI(id instance, std::shared_ptr jsInvoker); }; } // namespace react diff --git a/Libraries/Image/Image.android.js b/Libraries/Image/Image.android.js index bd1dbdb304f..3f4e023185a 100644 --- a/Libraries/Image/Image.android.js +++ b/Libraries/Image/Image.android.js @@ -14,7 +14,6 @@ const DeprecatedImageStylePropTypes = require('../DeprecatedPropTypes/Deprecated const DeprecatedStyleSheetPropType = require('../DeprecatedPropTypes/DeprecatedStyleSheetPropType'); const DeprecatedViewPropTypes = require('../DeprecatedPropTypes/DeprecatedViewPropTypes'); const ImageViewNativeComponent = require('./ImageViewNativeComponent'); -const NativeModules = require('../BatchedBridge/NativeModules'); const PropTypes = require('prop-types'); const React = require('react'); const ReactNative = require('../Renderer/shims/ReactNative'); // eslint-disable-line no-unused-vars @@ -24,7 +23,7 @@ const TextAncestor = require('../Text/TextAncestor'); const flattenStyle = require('../StyleSheet/flattenStyle'); const resolveAssetSource = require('./resolveAssetSource'); -const {ImageLoader} = NativeModules; +import NativeImageLoaderAndroid from './NativeImageLoaderAndroid'; const TextInlineImageNativeComponent = require('./TextInlineImageNativeComponent'); @@ -149,7 +148,7 @@ function getSize( success: (width: number, height: number) => void, failure?: (error: any) => void, ): any { - return ImageLoader.getSize(url) + return NativeImageLoaderAndroid.getSize(url) .then(function(sizes) { success(sizes.width, sizes.height); }) @@ -173,7 +172,7 @@ function getSizeWithHeaders( success: (width: number, height: number) => void, failure?: (error: any) => void, ): any { - return ImageLoader.getSizeWithHeaders(url, headers) + return NativeImageLoaderAndroid.getSizeWithHeaders(url, headers) .then(function(sizes) { success(sizes.width, sizes.height); }) @@ -188,11 +187,11 @@ function getSizeWithHeaders( function prefetch(url: string, callback: ?Function): any { const requestId = generateRequestId(); callback && callback(requestId); - return ImageLoader.prefetchImage(url, requestId); + return NativeImageLoaderAndroid.prefetchImage(url, requestId); } function abortPrefetch(requestId: number) { - ImageLoader.abortRequest(requestId); + NativeImageLoaderAndroid.abortRequest(requestId); } /** @@ -203,7 +202,7 @@ function abortPrefetch(requestId: number) { async function queryCache( urls: Array, ): Promise<{[string]: 'memory' | 'disk' | 'disk/memory'}> { - return await ImageLoader.queryCache(urls); + return await NativeImageLoaderAndroid.queryCache(urls); } type ImageComponentStatics = $ReadOnly<{| diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index a09d0f2e261..1c149808e64 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -24,7 +24,7 @@ import type {ImageProps as ImagePropsType} from './ImageProps'; import type {HostComponent} from '../Renderer/shims/ReactNativeTypes'; import type {ImageStyleProp} from '../StyleSheet/StyleSheet'; -import NativeImageLoader from './NativeImageLoader'; +import NativeImageLoaderIOS from './NativeImageLoaderIOS'; const ImageViewManager = NativeModules.ImageViewManager; const RCTImageView: HostComponent = requireNativeComponent( @@ -36,7 +36,7 @@ function getSize( success: (width: number, height: number) => void, failure?: (error: any) => void, ) { - NativeImageLoader.getSize(uri) + NativeImageLoaderIOS.getSize(uri) .then(([width, height]) => success(width, height)) .catch( failure || @@ -52,7 +52,7 @@ function getSizeWithHeaders( success: (width: number, height: number) => void, failure?: (error: any) => void, ): any { - return NativeImageLoader.getSizeWithHeaders(uri, headers) + return NativeImageLoaderIOS.getSizeWithHeaders(uri, headers) .then(function(sizes) { success(sizes.width, sizes.height); }) @@ -65,13 +65,13 @@ function getSizeWithHeaders( } function prefetch(url: string): any { - return NativeImageLoader.prefetchImage(url); + return NativeImageLoaderIOS.prefetchImage(url); } async function queryCache( urls: Array, ): Promise<{[string]: 'memory' | 'disk' | 'disk/memory'}> { - return await NativeImageLoader.queryCache(urls); + return await NativeImageLoaderIOS.queryCache(urls); } type ImageComponentStatics = $ReadOnly<{| diff --git a/Libraries/Image/NativeImageLoaderAndroid.js b/Libraries/Image/NativeImageLoaderAndroid.js new file mode 100644 index 00000000000..cd14b8cfa29 --- /dev/null +++ b/Libraries/Image/NativeImageLoaderAndroid.js @@ -0,0 +1,30 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type {TurboModule} from '../TurboModule/RCTExport'; +import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; + +export interface Spec extends TurboModule { + +abortRequest: (requestId: number) => void; + +getConstants: () => {||}; + +getSize: ( + uri: string, + ) => Promise<$ReadOnly<{width: number, height: number}>>; + +getSizeWithHeaders: ( + uri: string, + headers: Object, + ) => Promise<{width: number, height: number}>; + +prefetchImage: (uri: string, requestId: number) => Promise; + +queryCache: (uris: Array) => Promise; +} + +export default (TurboModuleRegistry.getEnforcing('ImageLoader'): Spec); diff --git a/Libraries/Image/NativeImageLoader.js b/Libraries/Image/NativeImageLoaderIOS.js similarity index 100% rename from Libraries/Image/NativeImageLoader.js rename to Libraries/Image/NativeImageLoaderIOS.js diff --git a/Libraries/Image/RCTImageLoader.mm b/Libraries/Image/RCTImageLoader.mm index 9dc1b8aa555..6052cebcbe0 100644 --- a/Libraries/Image/RCTImageLoader.mm +++ b/Libraries/Image/RCTImageLoader.mm @@ -28,7 +28,7 @@ static NSInteger RCTImageBytesForImage(UIImage *image) return image.images ? image.images.count * singleImageBytes : singleImageBytes; } -@interface RCTImageLoader() +@interface RCTImageLoader() @end @@ -946,7 +946,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, - (std::shared_ptr)getTurboModuleWithJsInvoker: (std::shared_ptr)jsInvoker { - return std::make_shared(self, jsInvoker); + return std::make_shared(self, jsInvoker); } RCT_EXPORT_METHOD(getSize:(NSString *)uri resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageLoaderAndroidSpec.java b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageLoaderAndroidSpec.java new file mode 100644 index 00000000000..dd045e96e64 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageLoaderAndroidSpec.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + * + *

Generated by an internal genrule from Flow types. + * + * @generated + * @nolint + */ + +package com.facebook.fbreact.specs; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReactModuleWithSpec; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; + +public abstract class NativeImageLoaderAndroidSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { + public NativeImageLoaderAndroidSpec(ReactApplicationContext reactContext) { + super(reactContext); + } + + @ReactMethod + public abstract void getSize(String uri, Promise promise); + + @ReactMethod + public abstract void abortRequest(double requestId); + + @ReactMethod + public abstract void prefetchImage(String uri, double requestId, Promise promise); + + @ReactMethod + public abstract void queryCache(ReadableArray uris, Promise promise); + + @ReactMethod + public abstract void getSizeWithHeaders(String uri, ReadableMap headers, Promise promise); +} diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageLoaderIOSSpec.java b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageLoaderIOSSpec.java new file mode 100644 index 00000000000..6af51a1f5b9 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageLoaderIOSSpec.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + * + *

Generated by an internal genrule from Flow types. + * + * @generated + * @nolint + */ + +package com.facebook.fbreact.specs; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReactModuleWithSpec; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; + +public abstract class NativeImageLoaderIOSSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { + public NativeImageLoaderIOSSpec(ReactApplicationContext reactContext) { + super(reactContext); + } + + @ReactMethod + public abstract void getSize(String uri, Promise promise); + + @ReactMethod + public abstract void prefetchImage(String uri, Promise promise); + + @ReactMethod + public abstract void queryCache(ReadableArray uris, Promise promise); + + @ReactMethod + public abstract void getSizeWithHeaders(String uri, ReadableMap headers, Promise promise); +}