mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Properly handle null values coming from JS (#49250)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/49250 The TurboModule System decided to ignore the Null values when they are coming to JS. However, in iOS, null value can be mapped to `[NSNull null];` and this value is a valid value that can be used on the native side. In the old architecture, when the user were sending a null value from JS to a native module, the Native side was receiving the value. In the New Architecture, the value was stripped away. This change allow us to handle the `null` value properly in the interop layer, to restore the usage of legacy modules in the New Arch. I also tried with a more radical approach, but several tests were crashing because some modules do not know how to handle `NSNull`. See discussion happening here: https://github.com/invertase/react-native-firebase/issues/8144#issuecomment-2548067344 ## Changelog: [iOS][Changed] - Properly handle `null` values coming from NativeModules. Reviewed By: sammy-SC Differential Revision: D69301396 fbshipit-source-id: be275185e2643092f6c3dc2481fe9381bbcf69e9
This commit is contained in:
committed by
Facebook GitHub Bot
parent
e74246bd66
commit
d4236791e2
+4
-4
@@ -346,7 +346,7 @@ void ObjCInteropTurboModule::setInvocationArg(
|
|||||||
SEL selector = selectorForType(argumentType);
|
SEL selector = selectorForType(argumentType);
|
||||||
|
|
||||||
if ([RCTConvert respondsToSelector:selector]) {
|
if ([RCTConvert respondsToSelector:selector]) {
|
||||||
id objCArg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
|
id objCArg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
|
||||||
|
|
||||||
if (objCArgType == @encode(char)) {
|
if (objCArgType == @encode(char)) {
|
||||||
char arg = RCTConvertTo<char>(selector, objCArg);
|
char arg = RCTConvertTo<char>(selector, objCArg);
|
||||||
@@ -500,7 +500,7 @@ void ObjCInteropTurboModule::setInvocationArg(
|
|||||||
}
|
}
|
||||||
|
|
||||||
RCTResponseSenderBlock arg =
|
RCTResponseSenderBlock arg =
|
||||||
(RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
|
(RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
|
||||||
if (arg) {
|
if (arg) {
|
||||||
[retainedObjectsForInvocation addObject:arg];
|
[retainedObjectsForInvocation addObject:arg];
|
||||||
}
|
}
|
||||||
@@ -515,7 +515,7 @@ void ObjCInteropTurboModule::setInvocationArg(
|
|||||||
}
|
}
|
||||||
|
|
||||||
RCTResponseSenderBlock senderBlock =
|
RCTResponseSenderBlock senderBlock =
|
||||||
(RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
|
(RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
|
||||||
RCTResponseErrorBlock arg = ^(NSError *error) {
|
RCTResponseErrorBlock arg = ^(NSError *error) {
|
||||||
senderBlock(@[ RCTJSErrorFromNSError(error) ]);
|
senderBlock(@[ RCTJSErrorFromNSError(error) ]);
|
||||||
};
|
};
|
||||||
@@ -545,7 +545,7 @@ void ObjCInteropTurboModule::setInvocationArg(
|
|||||||
runtime, errorPrefix + "JavaScript argument must be a plain object. Got " + getType(runtime, jsiArg));
|
runtime, errorPrefix + "JavaScript argument must be a plain object. Got " + getType(runtime, jsiArg));
|
||||||
}
|
}
|
||||||
|
|
||||||
id arg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
|
id arg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
|
||||||
|
|
||||||
RCTManagedPointer *(*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend;
|
RCTManagedPointer *(*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend;
|
||||||
RCTManagedPointer *box = convert([RCTCxxConvert class], selector, arg);
|
RCTManagedPointer *box = convert([RCTCxxConvert class], selector, arg);
|
||||||
|
|||||||
+5
@@ -32,6 +32,11 @@ using EventEmitterCallback = std::function<void(const std::string &, id)>;
|
|||||||
namespace TurboModuleConvertUtils {
|
namespace TurboModuleConvertUtils {
|
||||||
jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value);
|
jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value);
|
||||||
id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker);
|
id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker);
|
||||||
|
id convertJSIValueToObjCObject(
|
||||||
|
jsi::Runtime &runtime,
|
||||||
|
const jsi::Value &value,
|
||||||
|
std::shared_ptr<CallInvoker> jsInvoker,
|
||||||
|
BOOL useNSNull);
|
||||||
} // namespace TurboModuleConvertUtils
|
} // namespace TurboModuleConvertUtils
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|||||||
+27
-9
@@ -111,21 +111,27 @@ static NSString *convertJSIStringToNSString(jsi::Runtime &runtime, const jsi::St
|
|||||||
return [NSString stringWithUTF8String:value.utf8(runtime).c_str()];
|
return [NSString stringWithUTF8String:value.utf8(runtime).c_str()];
|
||||||
}
|
}
|
||||||
|
|
||||||
static NSArray *
|
static NSArray *convertJSIArrayToNSArray(
|
||||||
convertJSIArrayToNSArray(jsi::Runtime &runtime, const jsi::Array &value, std::shared_ptr<CallInvoker> jsInvoker)
|
jsi::Runtime &runtime,
|
||||||
|
const jsi::Array &value,
|
||||||
|
std::shared_ptr<CallInvoker> jsInvoker,
|
||||||
|
BOOL useNSNull)
|
||||||
{
|
{
|
||||||
size_t size = value.size(runtime);
|
size_t size = value.size(runtime);
|
||||||
NSMutableArray *result = [NSMutableArray new];
|
NSMutableArray *result = [NSMutableArray new];
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
// Insert kCFNull when it's `undefined` value to preserve the indices.
|
// Insert kCFNull when it's `undefined` value to preserve the indices.
|
||||||
id convertedObject = convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker);
|
id convertedObject = convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker, useNSNull);
|
||||||
[result addObject:convertedObject ? convertedObject : (id)kCFNull];
|
[result addObject:convertedObject ? convertedObject : (id)kCFNull];
|
||||||
}
|
}
|
||||||
return [result copy];
|
return [result copy];
|
||||||
}
|
}
|
||||||
|
|
||||||
static NSDictionary *
|
static NSDictionary *convertJSIObjectToNSDictionary(
|
||||||
convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value, std::shared_ptr<CallInvoker> jsInvoker)
|
jsi::Runtime &runtime,
|
||||||
|
const jsi::Object &value,
|
||||||
|
std::shared_ptr<CallInvoker> jsInvoker,
|
||||||
|
BOOL useNSNull)
|
||||||
{
|
{
|
||||||
jsi::Array propertyNames = value.getPropertyNames(runtime);
|
jsi::Array propertyNames = value.getPropertyNames(runtime);
|
||||||
size_t size = propertyNames.size(runtime);
|
size_t size = propertyNames.size(runtime);
|
||||||
@@ -133,7 +139,7 @@ convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value,
|
|||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
jsi::String name = propertyNames.getValueAtIndex(runtime, i).getString(runtime);
|
jsi::String name = propertyNames.getValueAtIndex(runtime, i).getString(runtime);
|
||||||
NSString *k = convertJSIStringToNSString(runtime, name);
|
NSString *k = convertJSIStringToNSString(runtime, name);
|
||||||
id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker);
|
id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker, useNSNull);
|
||||||
if (v) {
|
if (v) {
|
||||||
result[k] = v;
|
result[k] = v;
|
||||||
}
|
}
|
||||||
@@ -161,9 +167,21 @@ convertJSIFunctionToCallback(jsi::Runtime &rt, jsi::Function &&function, std::sh
|
|||||||
|
|
||||||
id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker)
|
id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker)
|
||||||
{
|
{
|
||||||
if (value.isUndefined() || value.isNull()) {
|
return convertJSIValueToObjCObject(runtime, value, jsInvoker, NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
id convertJSIValueToObjCObject(
|
||||||
|
jsi::Runtime &runtime,
|
||||||
|
const jsi::Value &value,
|
||||||
|
std::shared_ptr<CallInvoker> jsInvoker,
|
||||||
|
BOOL useNSNull)
|
||||||
|
{
|
||||||
|
if (value.isUndefined() || (value.isNull() && !useNSNull)) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
if (value.isNull() && useNSNull) {
|
||||||
|
return [NSNull null];
|
||||||
|
}
|
||||||
if (value.isBool()) {
|
if (value.isBool()) {
|
||||||
return @(value.getBool());
|
return @(value.getBool());
|
||||||
}
|
}
|
||||||
@@ -176,12 +194,12 @@ id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, s
|
|||||||
if (value.isObject()) {
|
if (value.isObject()) {
|
||||||
jsi::Object o = value.getObject(runtime);
|
jsi::Object o = value.getObject(runtime);
|
||||||
if (o.isArray(runtime)) {
|
if (o.isArray(runtime)) {
|
||||||
return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker);
|
return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker, useNSNull);
|
||||||
}
|
}
|
||||||
if (o.isFunction(runtime)) {
|
if (o.isFunction(runtime)) {
|
||||||
return convertJSIFunctionToCallback(runtime, o.getFunction(runtime), jsInvoker);
|
return convertJSIFunctionToCallback(runtime, o.getFunction(runtime), jsInvoker);
|
||||||
}
|
}
|
||||||
return convertJSIObjectToNSDictionary(runtime, o, jsInvoker);
|
return convertJSIObjectToNSDictionary(runtime, o, jsInvoker, useNSNull);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Unsupported jsi::Value kind");
|
throw std::runtime_error("Unsupported jsi::Value kind");
|
||||||
|
|||||||
Reference in New Issue
Block a user